git.delta.rocks / unique-network / refs/commits / 203ce836134a

difftreelog

test restructure

Yaroslav Bolyukin2023-11-14parent: #dba4716.patch.diff
in: master

570 files changed

modifiedjs-packages/.gitignorediffbeforeafterboth
--- a/js-packages/.gitignore
+++ b/js-packages/.gitignore
@@ -1,8 +1,11 @@
 /node_modules/
 /*/dist/
-/*/tsconfig.tsbuildinfo
+/**/tsconfig.tsbuildinfo
 properties.csv
 erc721.csv
 erc20.csv
 .yarn/cache
-.yarn/install-state.gz
\ No newline at end of file
+.yarn/install-state.gz
+/scripts/metadata.json
+/**/*.js
+/**/*.d.ts
modifiedjs-packages/package.jsondiffbeforeafterboth
--- a/js-packages/package.json
+++ b/js-packages/package.json
@@ -2,6 +2,9 @@
   "name": "unique-tests",
   "version": "1.0.0",
   "description": "Unique Chain Tests",
+  "author": "",
+  "license": "SEE LICENSE IN ../LICENSE",
+  "homepage": "",
   "main": "",
   "private": true,
   "dependencies": {
@@ -29,6 +32,7 @@
     "chai-subset": "^1.6.0",
     "eslint": "^8.53.0",
     "eslint-plugin-mocha": "^10.2.0",
+    "ts-node": "^10.9.1",
     "typescript": "^5.2.2"
   },
   "mocha": {
@@ -38,16 +42,13 @@
     ]
   },
   "scripts": {
-    "lint": "eslint --ext .ts,.js src/",
+    "lint": "eslint --ext .ts .",
     "fix": "yarn lint --fix",
-    "polkadot-types-fetch-metadata": "yarn ts-node --esm scripts/src/fetchMetadata.ts",
-    "polkadot-types-from-defs": "ts-node --esm ./node_modules/.bin/polkadot-types-from-defs --endpoint types/src/metadata.json --input types/src/ --package .",
-    "polkadot-types-from-chain": "ts-node --esm ./node_modules/.bin/polkadot-types-from-chain --endpoint types/src/metadata.json --output types/src/ --package .",
-    "polkadot-types": "echo \"export default {}\" > types/src/lookup.ts && yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain"
+    "polkadot-types-fetch-metadata": "yarn ts-node --esm scripts/fetchMetadata.ts",
+    "polkadot-types-from-defs": "ts-node --esm ./node_modules/.bin/polkadot-types-from-defs --endpoint scripts/metadata.json --input types/ --package .",
+    "polkadot-types-from-chain": "ts-node --esm ./node_modules/.bin/polkadot-types-from-chain --endpoint scripts/metadata.json --output types/ --package .",
+    "polkadot-types": "echo \"export default {}\" > types/lookup.ts && yarn polkadot-types-fetch-metadata && yarn polkadot-types-from-defs && yarn polkadot-types-from-defs && yarn polkadot-types-from-chain && rm types/registry.ts"
   },
-  "author": "",
-  "license": "SEE LICENSE IN ../LICENSE",
-  "homepage": "",
   "resolutions": {
     "decode-uri-component": "^0.2.1"
   },
deletedjs-packages/playgrounds/src/index.tsdiffbeforeafterboth

no changes

deletedjs-packages/playgrounds/src/types.tsdiffbeforeafterboth
--- a/js-packages/playgrounds/src/types.ts
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import type {IKeyringPair} from '@polkadot/types/types';
-
-export const NON_EXISTENT_COLLECTION_ID = 4_294_967_295;
-
-export const MILLISECS_PER_BLOCK = 12000;
-export const MINUTES = 60_000 / MILLISECS_PER_BLOCK;
-export const HOURS = MINUTES * 60;
-export const DAYS = HOURS * 24;
-
-export interface IEvent {
-  section: string;
-  method: string;
-  index: [number, number] | string;
-  data: any[];
-  phase: {applyExtrinsic: number} | 'Initialization',
-}
-
-export interface IPhasicEvent {
-  phase: any, // {ApplyExtrinsic: number} | 'Initialization',
-  event: IEvent;
-}
-
-export interface ITransactionResult {
-  status: 'Fail' | 'Success';
-  result: {
-      dispatchError: any,
-      events: IPhasicEvent[];
-  },
-  blockHash: string,
-  moduleError?: string | object;
-}
-
-export interface ISubscribeBlockEventsData {
-  number: number;
-  hash: string;
-  timestamp: number;
-  events: IEvent[];
-}
-
-export interface ILogger {
-  log: (msg: any, level?: string) => void;
-  level: {
-    ERROR: 'ERROR';
-    WARNING: 'WARNING';
-    INFO: 'INFO';
-    [key: string]: string;
-  }
-}
-
-export interface IUniqueHelperLog {
-  executedAt: number;
-  executionTime: number;
-  type: 'extrinsic' | 'rpc';
-  status: 'Fail' | 'Success';
-  call: string;
-  params: any[];
-  moduleError?: string;
-  dispatchError?: any;
-  events?: any;
-}
-
-export interface IApiListeners {
-  connected?: (...args: any[]) => any;
-  disconnected?: (...args: any[]) => any;
-  error?: (...args: any[]) => any;
-  ready?: (...args: any[]) => any;
-  decorated?: (...args: any[]) => any;
-}
-
-export type ICrossAccountId = {
-  Substrate: TSubstrateAccount;
-} | {
-  Ethereum: TEthereumAccount;
-}
-
-export type ICrossAccountIdLower = {
-  substrate: TSubstrateAccount;
-} | {
-  ethereum: TEthereumAccount;
-};
-
-export interface IEthCrossAccountId {
-  0: TEthereumAccount;
-  1: TSubstrateAccount;
-  eth: TEthereumAccount;
-  sub: TSubstrateAccount;
-}
-
-export interface ICollectionLimits {
-  accountTokenOwnershipLimit?: number | null;
-  sponsoredDataSize?: number | null;
-  sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;
-  tokenLimit?: number | null;
-  sponsorTransferTimeout?: number | null;
-  sponsorApproveTimeout?: number | null;
-  ownerCanTransfer?: boolean | null;
-  ownerCanDestroy?: boolean | null;
-  transfersEnabled?: boolean | null;
-}
-
-export interface INestingPermissions {
-  tokenOwner?: boolean;
-  collectionAdmin?: boolean;
-  restricted?: number[] | null;
-}
-
-export interface ICollectionPermissions {
-  access?: 'Normal' | 'AllowList';
-  mintMode?: boolean;
-  nesting?: INestingPermissions;
-}
-
-export interface IProperty {
-  key: string;
-  value?: string;
-}
-
-export interface ITokenPropertyPermission {
-  key: string;
-  permission: {
-    mutable?: boolean;
-    tokenOwner?: boolean;
-    collectionAdmin?: boolean;
-  }
-}
-
-export interface IToken {
-  collectionId: number;
-  tokenId: number;
-}
-
-export interface IBlock {
-  extrinsics: IExtrinsic[]
-  header: {
-    parentHash: string,
-    number: number,
-  };
-}
-
-export interface IExtrinsic {
-  isSigned: boolean,
-  method: {
-    method: string,
-    section: string,
-    args: any[]
-  }
-}
-
-export interface ICollectionFlags {
-  foreign: boolean,
-  erc721metadata: boolean,
-}
-
-export enum CollectionFlag {
-  None = 0,
-  /// External collections can't be managed using `unique` api
-  External = 1,
-  /// Supports ERC721Metadata
-  Erc721metadata = 64,
-  /// Tokens in foreign collections can be transferred, but not burnt
-  Foreign = 128,
-}
-
-export interface ICollectionCreationOptions {
-  name?: string | number[];
-  description?: string | number[];
-  tokenPrefix?: string | number[];
-  mode?: {
-    nft?: null;
-    refungible?: null;
-    fungible?: number;
-  }
-  permissions?: ICollectionPermissions;
-  properties?: IProperty[];
-  tokenPropertyPermissions?: ITokenPropertyPermission[];
-  limits?: ICollectionLimits;
-  pendingSponsor?: ICrossAccountId;
-  adminList?: ICrossAccountId[];
-  flags?: number[] | CollectionFlag[] ,
-}
-
-export interface IChainProperties {
-  ss58Format: number;
-  tokenDecimals: number[];
-  tokenSymbol: string[]
-}
-
-export interface ISubstrateBalance {
-  free: bigint,
-  reserved: bigint,
-  frozen: bigint,
-}
-
-export interface IStakingInfo {
-  block: bigint,
-  amount: bigint,
-}
-
-export interface IPovInfo {
-  proofSize: number,
-  compactProofSize: number,
-  compressedProofSize: number,
-  results: any[],
-  kv: any,
-}
-
-export interface ISchedulerOptions {
-  scheduledId?: string,
-  priority?: number,
-  periodic?: {
-    period: number,
-    repetitions: number,
-  },
-}
-
-export interface DemocracySplitAccount {
-  aye: bigint,
-  nay: bigint,
-}
-
-export type TSubstrateAccount = string;
-export type TEthereumAccount = string;
-export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';
-export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';
-export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';
-export type TRelayNetworks = 'rococo' | 'westend';
-export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;
-export type TSigner = IKeyringPair; // | 'string'
-export type TCollectionMode = 'nft' | 'rft' | 'ft';
deletedjs-packages/playgrounds/src/types.xcm.tsdiffbeforeafterboth
--- a/js-packages/playgrounds/src/types.xcm.ts
+++ /dev/null
@@ -1,36 +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,
-  },
-}
-
-export interface IForeignAssetMetadata {
-  name?: number | Uint8Array,
-  symbol?: string,
-  decimals?: number,
-  minimalBalance?: bigint,
-}
\ No newline at end of file
deletedjs-packages/playgrounds/src/unique.dev.tsdiffbeforeafterboth
--- a/js-packages/playgrounds/src/unique.dev.ts
+++ /dev/null
@@ -1,1547 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import '@unique/opal-types/src/augment-api.js';
-import '@unique/opal-types/src/augment-types.js';
-import '@unique/opal-types/src/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/src/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} 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 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.transfer', [{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.transfer', [{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 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,
-      );
-    }
-  };
-}
deletedjs-packages/playgrounds/src/unique.governance.tsdiffbeforeafterboth
--- a/js-packages/playgrounds/src/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);
-  }*/
-}
deletedjs-packages/playgrounds/src/unique.tsdiffbeforeafterboth
--- a/js-packages/playgrounds/src/unique.ts
+++ /dev/null
@@ -1,3498 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-/* eslint-disable @typescript-eslint/no-var-requires */
-/* eslint-disable function-call-argument-newline */
-/* eslint-disable no-prototype-builtins */
-
-import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';
-import type {SignerOptions} from '@polkadot/api/types';
-import type {AugmentedSubmittables} from '@polkadot/api-base/types/submittable';
-import type {ApiInterfaceEvents} from '@polkadot/api/types';
-import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {hexToU8a} from '@polkadot/util/hex';
-import {u8aConcat} from '@polkadot/util/u8a';
-import type {
-  IApiListeners,
-  IBlock,
-  IEvent,
-  IChainProperties,
-  ICollectionCreationOptions,
-  ICollectionLimits,
-  ICollectionPermissions,
-  ICrossAccountId,
-  ICrossAccountIdLower,
-  ILogger,
-  INestingPermissions,
-  IProperty,
-  IStakingInfo,
-  ISubstrateBalance,
-  IToken,
-  ITokenPropertyPermission,
-  ITransactionResult,
-  IUniqueHelperLog,
-  TApiAllowedListeners,
-  TEthereumAccount,
-  TSigner,
-  TSubstrateAccount,
-  TNetworks,
-  IEthCrossAccountId,
-} from './types.js';
-import type {RuntimeDispatchInfo} from '@polkadot/types/interfaces';
-
-export class CrossAccountId {
-  account: ICrossAccountId;
-
-  constructor(account: ICrossAccountId) {
-    this.account = account;
-  }
-
-  static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {
-    switch (domain) {
-      case 'Substrate': return new CrossAccountId({Substrate: account.address});
-      case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();
-    }
-  }
-
-  static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {
-    if('substrate' in address) return new CrossAccountId({Substrate: address.substrate});
-    else return new CrossAccountId({Ethereum: address.ethereum});
-  }
-
-  static normalizeSubstrateAddress(address: {Substrate: TSubstrateAccount}, ss58Format = 42): TSubstrateAccount {
-    return encodeAddress(decodeAddress(address.Substrate), ss58Format);
-  }
-
-  static withNormalizedSubstrate(address: ICrossAccountId, ss58Format = 42): ICrossAccountId {
-    if('Substrate' in address) return {Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)};
-    return address;
-  }
-
-  withNormalizedSubstrate(ss58Format = 42): CrossAccountId {
-    if('Substrate' in this.account) this.account = CrossAccountId.withNormalizedSubstrate(this.account, ss58Format);
-    return this;
-  }
-
-  static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {
-    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));
-  }
-
-  toEthereum(): CrossAccountId {
-    this.account = CrossAccountId.toEthereum(this.account);
-    return this;
-  }
-
-  static toEthereum(account: ICrossAccountId): ICrossAccountId {
-    if('Substrate' in account) return {Ethereum: CrossAccountId.translateSubToEth(account.Substrate)};
-    return account;
-  }
-
-  static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {
-    return evmToAddress(address, ss58Format);
-  }
-
-  toSubstrate(ss58Format?: number): CrossAccountId {
-    this.account = CrossAccountId.toSubstrate(this.account, ss58Format);
-    return this;
-  }
-
-  static toSubstrate(account: ICrossAccountId, ss58Format?: number): ICrossAccountId {
-    if('Ethereum' in account) return {Substrate: CrossAccountId.translateEthToSub(account.Ethereum, ss58Format)};
-    return account;
-  }
-
-  toLowerCase(): CrossAccountId {
-    this.account = CrossAccountId.toLowerCase(this.account);
-    return this;
-  }
-
-  static toLowerCase(account: ICrossAccountId) {
-    if('Substrate' in account) return {Substrate: account.Substrate.toLowerCase()};
-    if('Ethereum' in account) return {Ethereum: account.Ethereum.toLowerCase()};
-    return account;
-  }
-
-  toICrossAccountId(): ICrossAccountId {
-    return this.account;
-  }
-}
-
-const nesting = {
-  toChecksumAddress(address: string): string {
-    if(typeof address === 'undefined') return '';
-
-    if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);
-
-    address = address.toLowerCase().replace(/^0x/i, '');
-    const addressHash = keccakAsHex(address).replace(/^0x/i, '');
-    const checksumAddress = ['0x'];
-
-    for(let i = 0; i < address.length; i++) {
-      // If ith character is 8 to f then make it uppercase
-      if(parseInt(addressHash[i], 16) > 7) {
-        checksumAddress.push(address[i].toUpperCase());
-      } else {
-        checksumAddress.push(address[i]);
-      }
-    }
-    return checksumAddress.join('');
-  },
-  tokenIdToAddress(collectionId: number, tokenId: number) {
-    return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);
-  },
-};
-
-class UniqueUtil {
-  static transactionStatus = {
-    NOT_READY: 'NotReady',
-    FAIL: 'Fail',
-    SUCCESS: 'Success',
-  };
-
-  static chainLogType = {
-    EXTRINSIC: 'extrinsic',
-    RPC: 'rpc',
-  };
-
-  static getTokenAccount(token: IToken): ICrossAccountId {
-    return {Ethereum: this.getTokenAddress(token)};
-  }
-
-  static getTokenAddress(token: IToken): string {
-    return nesting.tokenIdToAddress(token.collectionId, token.tokenId);
-  }
-
-  static getDefaultLogger(): ILogger {
-    return {
-      log(msg: any, level = 'INFO') {
-        console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));
-      },
-      level: {
-        ERROR: 'ERROR',
-        WARNING: 'WARNING',
-        INFO: 'INFO',
-      },
-    };
-  }
-
-  static vec2str(arr: string[] | number[]) {
-    return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');
-  }
-
-  static str2vec(string: string) {
-    if(typeof string !== 'string') return string;
-    return Array.from(string).map(x => x.charCodeAt(0));
-  }
-
-  static fromSeed(seed: string, ss58Format = 42) {
-    const keyring = new Keyring({type: 'sr25519', ss58Format});
-    return keyring.addFromUri(seed);
-  }
-
-  static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {
-    if(creationResult.status !== this.transactionStatus.SUCCESS) {
-      throw Error('Unable to create collection!');
-    }
-
-    let collectionId = null;
-    creationResult.result.events.forEach(({event: {data, method, section}}) => {
-      if((section === 'common') && (method === 'CollectionCreated')) {
-        collectionId = parseInt(data[0].toString(), 10);
-      }
-    });
-
-    if(collectionId === null) {
-      throw Error('No CollectionCreated event was found!');
-    }
-
-    return collectionId;
-  }
-
-  static extractTokensFromCreationResult(creationResult: ITransactionResult): {
-    success: boolean,
-    tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],
-  } {
-    if(creationResult.status !== this.transactionStatus.SUCCESS) {
-      throw Error('Unable to create tokens!');
-    }
-    let success = false;
-    const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];
-    creationResult.result.events.forEach(({event: {data, method, section}}) => {
-      if(method === 'ExtrinsicSuccess') {
-        success = true;
-      } else if((section === 'common') && (method === 'ItemCreated')) {
-        tokens.push({
-          collectionId: parseInt(data[0].toString(), 10),
-          tokenId: parseInt(data[1].toString(), 10),
-          owner: data[2].toHuman(),
-          amount: data[3].toBigInt(),
-        });
-      }
-    });
-    return {success, tokens};
-  }
-
-  static extractTokensFromBurnResult(burnResult: ITransactionResult): {
-    success: boolean,
-    tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],
-  } {
-    if(burnResult.status !== this.transactionStatus.SUCCESS) {
-      throw Error('Unable to burn tokens!');
-    }
-    let success = false;
-    const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];
-    burnResult.result.events.forEach(({event: {data, method, section}}: any) => {
-      if(method === 'ExtrinsicSuccess') {
-        success = true;
-      } else if((section === 'common') && (method === 'ItemDestroyed')) {
-        tokens.push({
-          collectionId: parseInt(data[0].toString(), 10),
-          tokenId: parseInt(data[1].toString(), 10),
-          owner: data[2].toHuman(),
-          amount: data[3].toBigInt(),
-        });
-      }
-    });
-    return {success, tokens};
-  }
-
-  static findCollectionInEvents(events: { event: IEvent }[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {
-    let eventId = null;
-    events.forEach(({event: {data, method, section}}) => {
-      if((section === expectedSection) && (method === expectedMethod)) {
-        eventId = parseInt(data[0].toString(), 10);
-      }
-    });
-
-    if(eventId === null) {
-      throw Error(`No ${expectedMethod} event was found!`);
-    }
-    return eventId === collectionId;
-  }
-
-  static isTokenTransferSuccess(events: { event: IEvent }[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
-    const normalizeAddress = (address: string | ICrossAccountId) => {
-      if(typeof address === 'string') return address;
-      if('Substrate' in address) return CrossAccountId.withNormalizedSubstrate(address);
-      if('Ethereum' in address) return CrossAccountId.toLowerCase(address);
-      return address;
-    };
-    let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;
-    events.forEach(({event: {data, method, section}}) => {
-      if((section === 'common') && (method === 'Transfer')) {
-        transfer = {
-          collectionId: data[0].toJSON(),
-          tokenId: data[1].toJSON(),
-          from: normalizeAddress(data[2].toHuman()),
-          to: normalizeAddress(data[3].toHuman()),
-          amount: BigInt(data[4].toJSON()),
-        };
-      }
-    });
-    let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;
-    isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);
-    isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);
-    isSuccess = isSuccess && amount === transfer.amount;
-    return isSuccess;
-  }
-
-  static bigIntToDecimals(number: bigint, decimals = 18) {
-    const numberStr = number.toString();
-    const dotPos = numberStr.length - decimals;
-
-    if(dotPos <= 0) {
-      return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;
-    } else {
-      const intPart = numberStr.substring(0, dotPos);
-      const fractPart = numberStr.substring(dotPos);
-      return intPart + '.' + fractPart;
-    }
-  }
-}
-
-class UniqueEventHelper {
-  static extractIndex(index: any): [number, number] | string {
-    if(index.toRawType() === '[u8;2]') return [index[0], index[1]];
-    return index.toJSON();
-  }
-
-  static extractSub(data: any, subTypes: any): { [key: string]: any } {
-    let obj: any = {};
-    let index = 0;
-
-    if(data.entries) {
-      for(const [key, value] of data.entries()) {
-        obj[key] = this.extractData(value, subTypes[index]);
-        index++;
-      }
-    } else obj = data.toJSON();
-
-    return obj;
-  }
-
-  static toHuman(data: any) {
-    return data && data.toHuman ? data.toHuman() : `${data}`;
-  }
-
-  static extractData(data: any, type: any): any {
-    if(!type) return this.toHuman(data);
-    if(['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();
-    if(['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();
-    if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);
-    return this.toHuman(data);
-  }
-
-  public static extractEvents(events: { event: any, phase: any }[]): IEvent[] {
-    const parsedEvents: IEvent[] = [];
-
-    events.forEach((record) => {
-      const {event, phase} = record;
-      const types = event.typeDef;
-
-      const eventData: IEvent = {
-        section: event.section.toString(),
-        method: event.method.toString(),
-        index: this.extractIndex(event.index),
-        data: [],
-        phase: phase.toJSON(),
-      };
-
-      event.data.forEach((val: any, index: number) => {
-        eventData.data.push(this.extractData(val, types[index]));
-      });
-
-      parsedEvents.push(eventData);
-    });
-
-    return parsedEvents;
-  }
-}
-const InvalidTypeSymbol = Symbol('Invalid type');
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-export type Invalid =
-  | ((
-    invalidType: typeof InvalidTypeSymbol,
-    ..._: typeof InvalidTypeSymbol[]
-  ) => typeof InvalidTypeSymbol)
-  | null
-  | undefined;
-// Has slightly better error messages than Get
-type Get2<T, P extends string, E> =
-  P extends `${infer Key}.${infer Key2}` ? Key extends keyof T ? Key2 extends keyof T[Key] ? T[Key][Key2] : E : E : E;
-type ForceFunction<T> = T extends (...args: any) => any ? T : (...args: any) => Invalid;
-
-export class ChainHelperBase {
-  helperBase: any;
-
-  transactionStatus = UniqueUtil.transactionStatus;
-  chainLogType = UniqueUtil.chainLogType;
-  util: typeof UniqueUtil;
-  eventHelper: typeof UniqueEventHelper;
-  logger: ILogger;
-  api: ApiPromise | null;
-  forcedNetwork: TNetworks | null;
-  network: TNetworks | null;
-  wsEndpoint: string | null;
-  chainLog: IUniqueHelperLog[];
-  children: ChainHelperBase[];
-  address: AddressGroup;
-  chain: ChainGroup;
-
-  constructor(logger?: ILogger, helperBase?: any) {
-    this.helperBase = helperBase;
-
-    this.util = UniqueUtil;
-    this.eventHelper = UniqueEventHelper;
-    if(typeof logger == 'undefined') logger = this.util.getDefaultLogger();
-    this.logger = logger;
-    this.api = null;
-    this.forcedNetwork = null;
-    this.network = null;
-    this.wsEndpoint = null;
-    this.chainLog = [];
-    this.children = [];
-    this.address = new AddressGroup(this);
-    this.chain = new ChainGroup(this);
-  }
-
-  clone(helperCls: ChainHelperBaseConstructor, options: { [key: string]: any } = {}) {
-    Object.setPrototypeOf(helperCls.prototype, this);
-    const newHelper = new helperCls(this.logger, options);
-
-    newHelper.api = this.api;
-    newHelper.network = this.network;
-    newHelper.forceNetwork = this.forceNetwork;
-
-    this.children.push(newHelper);
-
-    return newHelper;
-  }
-
-  getEndpoint(): string {
-    if(this.wsEndpoint === null) throw Error('No connection was established');
-    return this.wsEndpoint;
-  }
-
-  getApi(): ApiPromise {
-    if(this.api === null) throw Error('API not initialized');
-    return this.api;
-  }
-
-  async subscribeEvents(expectedEvents: { section: string, names: string[] }[]) {
-    const collectedEvents: IEvent[] = [];
-    const unsubscribe = await this.getApi().query.system.events((events: any) => {
-      const ievents = this.eventHelper.extractEvents(events);
-      ievents.forEach((event) => {
-        expectedEvents.forEach((e => {
-          if(event.section === e.section && e.names.includes(event.method)) {
-            collectedEvents.push(event);
-          }
-        }));
-      });
-    });
-    return {unsubscribe: unsubscribe as any, collectedEvents};
-  }
-
-  clearChainLog(): void {
-    this.chainLog = [];
-  }
-
-  forceNetwork(value: TNetworks): void {
-    this.forcedNetwork = value;
-  }
-
-  async connect(wsEndpoint: string, listeners?: IApiListeners) {
-    if(this.api !== null) throw Error('Already connected');
-    const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);
-    this.wsEndpoint = wsEndpoint;
-    this.api = api;
-    this.network = network;
-  }
-
-  async disconnect() {
-    for(const child of this.children) {
-      child.clearApi();
-    }
-
-    if(this.api === null) return;
-    await this.api.disconnect();
-    this.clearApi();
-  }
-
-  clearApi() {
-    this.api = null;
-    this.network = null;
-  }
-
-  static async detectNetwork(api: ApiPromise): Promise<TNetworks> {
-    const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;
-    const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];
-
-    if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;
-
-    if(['quartz', 'unique', 'sapphire'].indexOf(spec.specName) > -1) return spec.specName;
-    return 'opal';
-  }
-
-  static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {
-    if(!wsEndpoint) throw new Error('wsEndpoint was not set');
-    const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});
-    await api.isReady;
-
-    const network = await this.detectNetwork(api);
-
-    await api.disconnect();
-
-    return network;
-  }
-
-  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{
-    api: ApiPromise;
-    network: TNetworks;
-  }> {
-    if(typeof network === 'undefined' || network === null) network = 'opal';
-    if(!wsEndpoint) throw new Error('wsEndpoint was not set');
-    const supportedRPC = {
-      opal: {
-        unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,
-      },
-      quartz: {
-        unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,
-      },
-      unique: {
-        unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,
-      },
-      rococo: {},
-      westend: {},
-      moonbeam: {},
-      moonriver: {},
-      acala: {},
-      karura: {},
-      westmint: {},
-    };
-    if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);
-    const rpc = supportedRPC[network] as any;
-
-    // TODO: investigate how to replace rpc in runtime
-    // api._rpcCore.addUserInterfaces(rpc);
-
-    const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});
-
-    await api.isReadyOrError;
-
-    if(typeof listeners === 'undefined') listeners = {};
-    for(const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {
-      if(!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;
-      api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);
-    }
-
-    return {api, network};
-  }
-
-  getTransactionStatus(data: { events: { event: IEvent }[], status: any }) {
-    const {events, status} = data;
-    if(status.isReady) {
-      return this.transactionStatus.NOT_READY;
-    }
-    if(status.isBroadcast) {
-      return this.transactionStatus.NOT_READY;
-    }
-    if(status.isInBlock || status.isFinalized) {
-      const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');
-      if(errors.length > 0) {
-        return this.transactionStatus.FAIL;
-      }
-      if(events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {
-        return this.transactionStatus.SUCCESS;
-      }
-    }
-
-    return this.transactionStatus.FAIL;
-  }
-
-  signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {
-    const sign = (callback: any) => {
-      if(options !== null) return transaction.signAndSend(sender, options, callback);
-      return transaction.signAndSend(sender, callback);
-    };
-    // eslint-disable-next-line no-async-promise-executor
-    return new Promise(async (resolve, reject) => {
-      try {
-        const unsub = await sign((result: any) => {
-          const status = this.getTransactionStatus(result);
-
-          if(status === this.transactionStatus.SUCCESS) {
-            this.logger.log(`${label} successful`);
-            unsub();
-            resolve({result, status, blockHash: result.status.asInBlock.toHuman()});
-          } else if(status === this.transactionStatus.FAIL) {
-            let moduleError = null;
-
-            if(result.hasOwnProperty('dispatchError')) {
-              const dispatchError = result['dispatchError'];
-
-              if(dispatchError) {
-                if(dispatchError.isModule) {
-                  const modErr = dispatchError.asModule;
-                  const errorMeta = dispatchError.registry.findMetaError(modErr);
-
-                  moduleError = `${errorMeta.section}.${errorMeta.name}`;
-                } else if(dispatchError.isToken) {
-                  moduleError = `Token: ${dispatchError.asToken}`;
-                } else {
-                  // May be [object Object] in case of unhandled non-unit enum
-                  moduleError = `Misc: ${dispatchError.toHuman()}`;
-                }
-              } else {
-                this.logger.log(result, this.logger.level.ERROR);
-              }
-            }
-
-            this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);
-            unsub();
-            reject({status, moduleError, result});
-          }
-        });
-      } catch (e) {
-        this.logger.log(e, this.logger.level.ERROR);
-        reject(e);
-      }
-    });
-  }
-
-  async signTransactionWithoutSending(signer: TSigner, tx: any) {
-    const api = this.getApi();
-    const signingInfo = await api.derive.tx.signingInfo(signer.address);
-
-    tx.sign(signer, {
-      blockHash: api.genesisHash,
-      genesisHash: api.genesisHash,
-      runtimeVersion: api.runtimeVersion,
-      nonce: signingInfo.nonce,
-    });
-
-    return tx.toHex();
-  }
-
-  async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {
-    const api = this.getApi();
-    const signingInfo = await api.derive.tx.signingInfo(signer.address);
-
-    // We need to sign the tx because
-    // unsigned transactions does not have an inclusion fee
-    tx.sign(signer, {
-      blockHash: api.genesisHash,
-      genesisHash: api.genesisHash,
-      runtimeVersion: api.runtimeVersion,
-      nonce: signingInfo.nonce,
-    });
-
-    if(len === null) {
-      return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;
-    } else {
-      return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;
-    }
-  }
-
-  constructApiCall(apiCall: string, params: any[]) {
-    if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);
-    let call = this.getApi() as any;
-    for(const part of apiCall.slice(4).split('.')) {
-      call = call[part];
-      if(!call) {
-        const advice = part.includes('_') ? ' Looks like it needs to be converted to camel case.' : '';
-        throw Error(`Function ${part} of api call ${apiCall} not found.${advice}`);
-      }
-    }
-    return call(...params);
-  }
-
-  encodeApiCall(apiCall: string, params: any[]) {
-    return this.constructApiCall(apiCall, params).method.toHex();
-  }
-
-  async executeExtrinsic<
-    E extends string,
-    V extends (
-      ...args: any) => any = ForceFunction<
-        Get2<
-          AugmentedSubmittables<'promise'>,
-          E, (...args: any) => Invalid
-        >
-      >
-  >(
-    sender: TSigner,
-    extrinsic: `api.tx.${E}`,
-    params: Parameters<V>,
-    expectSuccess = true,
-    options: Partial<SignerOptions> | null = null,/*, failureMessage='expected success'*/
-  ): Promise<ITransactionResult> {
-    if(this.api === null) throw Error('API not initialized');
-
-    const startTime = (new Date()).getTime();
-    let result: ITransactionResult;
-    let events: IEvent[] = [];
-    try {
-      result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;
-      events = this.eventHelper.extractEvents(result.result.events);
-      const errorEvent = events.find((event) => event.method == 'ExecutedFailed' || event.method == 'CreatedFailed');
-      if(errorEvent)
-        throw Error(errorEvent.method + ': ' + extrinsic);
-    }
-    catch (e) {
-      if(!(e as object).hasOwnProperty('status')) throw e;
-      result = e as ITransactionResult;
-    }
-
-    const endTime = (new Date()).getTime();
-
-    const log = {
-      executedAt: endTime,
-      executionTime: endTime - startTime,
-      type: this.chainLogType.EXTRINSIC,
-      status: result.status,
-      call: extrinsic,
-      signer: this.getSignerAddress(sender),
-      params,
-    } as IUniqueHelperLog;
-
-    let errorMessage = '';
-
-    if(result.status !== this.transactionStatus.SUCCESS) {
-      if(result.moduleError) {
-        errorMessage = typeof result.moduleError === 'string'
-          ? result.moduleError
-          : `${Object.keys(result.moduleError)[0]}: ${Object.values(result.moduleError)[0]}`;
-        log.moduleError = errorMessage;
-      }
-      else if(result.result.dispatchError) log.dispatchError = result.result.dispatchError;
-    }
-    if(events.length > 0) log.events = events;
-
-    this.chainLog.push(log);
-
-    if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {
-      if(result.moduleError) throw Error(`${errorMessage}`);
-      else if(result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));
-    }
-    return result as any;
-  }
-  executeExtrinsicUncheckedWeight<
-      E extends string,
-      V extends (
-         ...args: any) => any = ForceFunction<
-            Get2<
-               AugmentedSubmittables<'promise'>,
-               E, (...args: any) => Invalid
-            >
-         >
-   >(
-    _sender: TSigner,
-    _extrinsic: `api.tx.${E}`,
-    _params: Parameters<V>,
-    _expectSuccess = true,
-    _options: Partial<SignerOptions> | null = null,/*, failureMessage='expected success'*/
-  ): Promise<ITransactionResult> {
-    throw new Error('executeExtrinsicUncheckedWeight only supported in sudo');
-  }
-
-  async callRpc
-  // TODO: make it strongly typed, or use api.query/api.rpc directly
-  // <
-  // K extends 'rpc' | 'query',
-  // E extends string,
-  // V extends (...args: any) => any = ForceFunction<
-  //   Get2<
-  //     K extends 'rpc' ? DecoratedRpc<'promise', RpcInterface> : QueryableStorage<'promise'>,
-  //     E, (...args: any) => Invalid<'not found'>
-  //   >
-  // >,
-  // P = Parameters<V>,
-  // >
-  (rpc: string, params?: any[]): Promise<any> {
-
-    if(typeof params === 'undefined') params = [] as any;
-    if(this.api === null) throw Error('API not initialized');
-    if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);
-
-    const startTime = (new Date()).getTime();
-    let result;
-    let error = null;
-    const log = {
-      type: this.chainLogType.RPC,
-      call: rpc,
-      params,
-    } as any as IUniqueHelperLog;
-
-    try {
-      result = await this.constructApiCall(rpc, params as any);
-    }
-    catch (e) {
-      error = e;
-    }
-
-    const endTime = (new Date()).getTime();
-
-    log.executedAt = endTime;
-    log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';
-    log.executionTime = endTime - startTime;
-
-    this.chainLog.push(log);
-
-    if(error !== null) throw error;
-
-    return result;
-  }
-
-  getSignerAddress(signer: IKeyringPair | string): string {
-    if(typeof signer === 'string') return signer;
-    return signer.address;
-  }
-
-  fetchAllPalletNames(): string[] {
-    if(this.api === null) throw Error('API not initialized');
-    return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()).sort();
-  }
-
-  fetchMissingPalletNames(requiredPallets: readonly string[]): string[] {
-    const palletNames = this.fetchAllPalletNames();
-    return requiredPallets.filter(p => !palletNames.includes(p));
-  }
-}
-
-
-export class HelperGroup<T extends ChainHelperBase> {
-  helper: T;
-
-  constructor(uniqueHelper: T) {
-    this.helper = uniqueHelper;
-  }
-}
-
-
-class CollectionGroup extends HelperGroup<UniqueHelper> {
-  /**
- * Get number of blocks when sponsored transaction is available.
- *
- * @param collectionId ID of collection
- * @param tokenId ID of token
- * @param addressObj address for which the sponsorship is checked
- * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});
- * @returns number of blocks or null if sponsorship hasn't been set
- */
-  async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {
-    return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();
-  }
-
-  /**
-   * Get the number of created collections.
-   *
-   * @returns number of created collections
-   */
-  async getTotalCount(): Promise<number> {
-    return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();
-  }
-
-  /**
-   * Get information about the collection with additional data,
-   * including the number of tokens it contains, its administrators,
-   * the normalized address of the collection's owner, and decoded name and description.
-   *
-   * @param collectionId ID of collection
-   * @example await getData(2)
-   * @returns collection information object
-   */
-  async getData(collectionId: number): Promise<{
-    id: number;
-    name: string;
-    description: string;
-    tokensCount: number;
-    admins: CrossAccountId[];
-    normalizedOwner: TSubstrateAccount;
-    raw: any
-  } | null> {
-    const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);
-    const humanCollection = collection.toHuman(), collectionData = {
-      id: collectionId, name: null, description: null, tokensCount: 0, admins: [],
-      raw: humanCollection,
-    } as any, jsonCollection = collection.toJSON();
-    if(humanCollection === null) return null;
-    collectionData.raw.limits = jsonCollection.limits;
-    collectionData.raw.permissions = jsonCollection.permissions;
-    collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);
-    for(const key of ['name', 'description']) {
-      collectionData[key] = this.helper.util.vec2str(humanCollection[key]);
-    }
-
-    collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))
-      ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)
-      : 0;
-    collectionData.admins = await this.getAdmins(collectionId);
-
-    return collectionData;
-  }
-
-  /**
-   * Get the addresses of the collection's administrators, optionally normalized.
-   *
-   * @param collectionId ID of collection
-   * @param normalize whether to normalize the addresses to the default ss58 format
-   * @example await getAdmins(1)
-   * @returns array of administrators
-   */
-  async getAdmins(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {
-    const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman() as ICrossAccountId[];
-
-    return normalize
-      ? admins.map(address => CrossAccountId.withNormalizedSubstrate(address))
-      : admins;
-  }
-
-  /**
-   * Get the addresses added to the collection allow-list, optionally normalized.
-   * @param collectionId ID of collection
-   * @param normalize whether to normalize the addresses to the default ss58 format
-   * @example await getAllowList(1)
-   * @returns array of allow-listed addresses
-   */
-  async getAllowList(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {
-    const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman() as ICrossAccountId[];
-    return normalize
-      ? allowListed.map(address => CrossAccountId.withNormalizedSubstrate(address))
-      : allowListed;
-  }
-
-  /**
-   * Get the effective limits of the collection instead of null for default values
-   *
-   * @param collectionId ID of collection
-   * @example await getEffectiveLimits(2)
-   * @returns object of collection limits
-   */
-  async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {
-    return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();
-  }
-
-  /**
-   * Burns the collection if the signer has sufficient permissions and collection is empty.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @example await helper.collection.burn(aliceKeyring, 3);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async burn(signer: TSigner, collectionId: number): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.destroyCollection', [collectionId],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');
-  }
-
-  /**
-   * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param sponsorAddress Sponsor substrate address
-   * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');
-  }
-
-  /**
-   * Confirms consent to sponsor the collection on behalf of the signer.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @example confirmSponsorship(aliceKeyring, 10)
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.confirmSponsorship', [collectionId],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');
-  }
-
-  /**
-   * Removes the sponsor of a collection, regardless if it consented or not.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @example removeSponsor(aliceKeyring, 10)
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.removeCollectionSponsor', [collectionId],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');
-  }
-
-  /**
-   * Sets the limits of the collection. At least one limit must be specified for a correct call.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param limits collection limits object
-   * @example
-   * await setLimits(
-   *   aliceKeyring,
-   *   10,
-   *   {
-   *     sponsorTransferTimeout: 0,
-   *     ownerCanDestroy: false
-   *   }
-   * )
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.setCollectionLimits', [collectionId, limits],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');
-  }
-
-  /**
-   * Changes the owner of the collection to the new Substrate address.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param ownerAddress substrate address of new owner
-   * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');
-  }
-
-  /**
-   * Adds a collection administrator.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param adminAddressObj Administrator address (substrate or ethereum)
-   * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');
-  }
-
-  /**
-   * Removes a collection administrator.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param adminAddressObj Administrator address (substrate or ethereum)
-   * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');
-  }
-
-  /**
-   * Check if user is in allow list.
-   *
-   * @param collectionId ID of collection
-   * @param user Account to check
-   * @example await getAdmins(1)
-   * @returns is user in allow list
-   */
-  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {
-    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();
-  }
-
-  /**
-   * Adds an address to allow list
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param addressObj address to add to the allow list
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.addToAllowList', [collectionId, addressObj],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');
-  }
-
-  /**
-   * Removes an address from allow list
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param addressObj address to remove from the allow list
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');
-  }
-
-  /**
-   * Sets onchain permissions for selected collection.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param permissions collection permissions object
-   * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.setCollectionPermissions', [collectionId, permissions],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');
-  }
-
-  /**
-   * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param permissions nesting permissions object
-   * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {
-    return await this.setPermissions(signer, collectionId, {nesting: permissions});
-  }
-
-  /**
-   * Disables nesting for selected collection.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @example disableNesting(aliceKeyring, 10);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {
-    return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});
-  }
-
-  /**
-   * Sets onchain properties to the collection.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param properties array of property objects
-   * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.setCollectionProperties', [collectionId, properties],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');
-  }
-
-  /**
-   * Get collection properties.
-   *
-   * @param collectionId ID of collection
-   * @param propertyKeys optionally filter the returned properties to only these keys
-   * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);
-   * @returns array of key-value pairs
-   */
-  async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {
-    return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();
-  }
-
-  async getPropertiesConsumedSpace(collectionId: number): Promise<number> {
-    const api = this.helper.getApi();
-    const props = (await api.query.common.collectionProperties(collectionId)).toJSON();
-
-    return (props! as any).consumedSpace;
-  }
-
-  async getCollectionOptions(collectionId: number) {
-    return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();
-  }
-
-  /**
-   * Deletes onchain properties from the collection.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param propertyKeys array of property keys to delete
-   * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');
-  }
-
-  /**
-   * Changes the owner of the token.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param addressObj address of a new owner
-   * @param amount amount of tokens to be transfered. For NFT must be set to 1n
-   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})
-   * @returns true if the token success, otherwise false
-   */
-  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],
-      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,
-    );
-
-    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);
-  }
-
-  /**
-   *
-   * Change ownership of a token(s) on behalf of the owner.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param fromAddressObj address on behalf of which the token will be sent
-   * @param toAddressObj new token owner
-   * @param amount amount of tokens to be transfered. For NFT must be set to 1n
-   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})
-   * @returns true if the token success, otherwise false
-   */
-  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],
-      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,
-    );
-    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);
-  }
-
-  /**
-   *
-   * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param amount amount of tokens to be burned. For NFT must be set to 1n
-   * @example burnToken(aliceKeyring, 10, 5);
-   * @returns ```true``` if the extrinsic is successful, otherwise ```false```
-   */
-  async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {
-    const burnResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.burnItem', [collectionId, tokenId, amount],
-      true, // `Unable to burn token for ${label}`,
-    );
-    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);
-    if(burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');
-    return burnedTokens.success;
-  }
-
-  /**
-   * Destroys a concrete instance of NFT on behalf of the owner
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param fromAddressObj address on behalf of which the token will be burnt
-   * @param amount amount of tokens to be burned. For NFT must be set to 1n
-   * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
-    const burnResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],
-      true, // `Unable to burn token from for ${label}`,
-    );
-    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);
-    return burnedTokens.success && burnedTokens.tokens.length > 0;
-  }
-
-  /**
-   * Set, change, or remove approved address to transfer the ownership of the NFT.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens
-   * @param amount amount of token to be approved. For NFT must be set to 1n
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
-    const approveResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],
-      true, // `Unable to approve token for ${label}`,
-    );
-
-    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');
-  }
-
-  /**
-   * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param fromAddressObj Signer's Ethereum address containing her tokens
-   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens
-   * @param amount amount of token to be approved. For NFT must be set to 1n
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async approveTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
-    const approveResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.approveFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],
-      true, // `Unable to approve token for ${label}`,
-    );
-
-    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');
-  }
-
-  /**
-   * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens
-   * @param amount amount of token to be approved. For NFT must be set to 1n
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async approveTokenFromEth(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
-    const ethMirror = CrossAccountId.fromKeyring(signer).toEthereum().toICrossAccountId();
-    return await this.approveTokenFrom(signer, collectionId, tokenId, ethMirror, toAddressObj, amount);
-  }
-
-  /**
-   * Get the amount of token pieces approved to transfer or burn. Normally 0.
-   *
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param toAccountObj address which is approved to use token pieces
-   * @param fromAccountObj address which may have allowed the use of its owned tokens
-   * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})
-   * @returns number of approved to transfer pieces
-   */
-  async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();
-  }
-
-  /**
-   * Get the last created token ID in a collection
-   *
-   * @param collectionId ID of collection
-   * @example getLastTokenId(10);
-   * @returns id of the last created token
-   */
-  async getLastTokenId(collectionId: number): Promise<number> {
-    return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();
-  }
-
-  /**
-   * Check if token exists
-   *
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @example doesTokenExist(10, 20);
-   * @returns true if the token exists, otherwise false
-   */
-  async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {
-    return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();
-  }
-}
-
-class NFTnRFT extends CollectionGroup {
-  /**
-   * Get tokens owned by account
-   *
-   * @param collectionId ID of collection
-   * @param addressObj tokens owner
-   * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})
-   * @returns array of token ids owned by account
-   */
-  async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {
-    return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();
-  }
-
-  /**
-   * Get token data
-   *
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param propertyKeys optionally filter the token properties to only these keys
-   * @param blockHashAt optionally query the data at some block with this hash
-   * @example getToken(10, 5);
-   * @returns human readable token data
-   */
-  async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{
-    properties: IProperty[];
-    owner: ICrossAccountId;
-    normalizedOwner: ICrossAccountId;
-  } | null> {
-    let args;
-    if(typeof blockHashAt === 'undefined') {
-      args = [collectionId, tokenId];
-    }
-    else {
-      if(propertyKeys.length == 0) {
-        const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();
-        if(!collection) return null;
-        propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);
-      }
-      args = [collectionId, tokenId, propertyKeys, blockHashAt];
-    }
-    const tokenData = (await this.helper.callRpc('api.rpc.unique.tokenData', args)).toHuman();
-    if(tokenData === null || tokenData.owner === null) return null;
-    tokenData.normalizedOwner = CrossAccountId.withNormalizedSubstrate(tokenData.owner);
-    return tokenData;
-  }
-
-  /**
-   * Get token's owner
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param blockHashAt optionally query the data at the block with this hash
-   * @example getTokenOwner(10, 5);
-   * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}
-   */
-  async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId> {
-    let owner;
-    if(typeof blockHashAt === 'undefined') {
-      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);
-    } else {
-      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);
-    }
-    return CrossAccountId.fromLowerCaseKeys(owner.toJSON()).toICrossAccountId();
-  }
-
-  /**
-   * Recursively find the address that owns the token
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param blockHashAt
-   * @example getTokenTopmostOwner(10, 5);
-   * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}
-   */
-  async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId | null> {
-    let owner;
-    if(typeof blockHashAt === 'undefined') {
-      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);
-    } else {
-      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);
-    }
-
-    if(owner === null) return null;
-
-    return owner.toHuman();
-  }
-
-  /**
-   * Nest one token into another
-   * @param signer keyring of signer
-   * @param tokenObj token to be nested
-   * @param rootTokenObj token to be parent
-   * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {
-    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);
-    const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);
-    if(!result) {
-      throw Error('Unable to nest token!');
-    }
-    return result;
-  }
-
-  /**
-     * Remove token from nested state
-     * @param signer keyring of signer
-     * @param tokenObj token to unnest
-     * @param rootTokenObj parent of a token
-     * @param toAddressObj address of a new token owner
-     * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});
-     * @returns ```true``` if extrinsic success, otherwise ```false```
-     */
-  async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {
-    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);
-    const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);
-    if(!result) {
-      throw Error('Unable to unnest token!');
-    }
-    return result;
-  }
-
-  /**
-   * Set permissions to change token properties
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param permissions permissions to change a property by the collection admin or token owner
-   * @example setTokenPropertyPermissions(
-   *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]
-   * )
-   * @returns true if extrinsic success otherwise false
-   */
-  async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');
-  }
-
-  /**
-   * Get token property permissions.
-   *
-   * @param collectionId ID of collection
-   * @param propertyKeys optionally filter the returned property permissions to only these keys
-   * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);
-   * @returns array of key-permission pairs
-   */
-  async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {
-    return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
-  }
-
-  /**
-   * Set token properties
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection
-   * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');
-  }
-
-  /**
-   * Get properties, metadata assigned to a token.
-   *
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param propertyKeys optionally filter the returned properties to only these keys
-   * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);
-   * @returns array of key-value pairs
-   */
-  async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {
-    return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();
-  }
-
-  /**
-   * Delete the provided properties of a token
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param propertyKeys property keys to be deleted
-   * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],
-      true,
-    );
-
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');
-  }
-
-  /**
-   * Mint new collection
-   *
-   * @param signer keyring of signer
-   * @param collectionOptions basic collection options and properties
-   * @param mode NFT or RFT type of a collection
-   * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")
-   * @returns object of the created collection
-   */
-  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {
-    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
-    collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};
-    for(const key of ['name', 'description', 'tokenPrefix']) {
-      if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);
-    }
-
-    let flags = 0;
-    // convert CollectionFlags to number and join them in one number
-    if(collectionOptions.flags) {
-      for(let i = 0; i < collectionOptions.flags.length; i++){
-        const flag = collectionOptions.flags[i];
-        flags = flags | flag;
-      }
-    }
-    collectionOptions.flags = [flags];
-
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createCollectionEx', [collectionOptions],
-      true, // errorLabel,
-    );
-    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));
-  }
-
-  getCollectionObject(_collectionId: number): any {
-    return null;
-  }
-
-  getTokenObject(_collectionId: number, _tokenId: number): any {
-    return null;
-  }
-
-  /**
-   * Tells whether the given `owner` approves the `operator`.
-   * @param collectionId ID of collection
-   * @param owner owner address
-   * @param operator operator addrees
-   * @returns true if operator is enabled
-   */
-  async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {
-    return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();
-  }
-
-  /** Sets or unsets the approval of a given operator.
-   *  The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
-   *  @param operator Operator
-   *  @param approved Should operator status be granted or revoked?
-   *  @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],
-      true,
-    );
-    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');
-  }
-}
-
-
-class NFTGroup extends NFTnRFT {
-  /**
-   * Get collection object
-   * @param collectionId ID of collection
-   * @example getCollectionObject(2);
-   * @returns instance of UniqueNFTCollection
-   */
-  override getCollectionObject(collectionId: number): UniqueNFTCollection {
-    return new UniqueNFTCollection(collectionId, this.helper);
-  }
-
-  /**
-   * Get token object
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @example getTokenObject(10, 5);
-   * @returns instance of UniqueNFTToken
-   */
-  override getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {
-    return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));
-  }
-
-  /**
-   * Is token approved to transfer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param toAccountObj address to be approved
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {
-    return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, (await this.getTokenOwner(collectionId, tokenId)))) === 1n;
-  }
-
-  /**
-   * Changes the owner of the token.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param addressObj address of a new owner
-   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {
-    return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);
-  }
-
-  /**
-   *
-   * Change ownership of a NFT on behalf of the owner.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param fromAddressObj address on behalf of which the token will be sent
-   * @param toAddressObj new token owner
-   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {
-    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);
-  }
-
-  /**
-   * Get tokens nested in the provided token
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param blockHashAt optionally query the data at the block with this hash
-   * @example getTokenChildren(10, 5);
-   * @returns tokens whose depth of nesting is <= 5
-   */
-  async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {
-    let children;
-    if(typeof blockHashAt === 'undefined') {
-      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);
-    } else {
-      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);
-    }
-
-    return children.toJSON().map((x: any) => ({collectionId: x.collection, tokenId: x.token}));
-  }
-
-  /**
-   * Mint new collection
-   * @param signer keyring of signer
-   * @param collectionOptions Collection options
-   * @example
-   * mintCollection(aliceKeyring, {
-   *   name: 'New',
-   *   description: 'New collection',
-   *   tokenPrefix: 'NEW',
-   * })
-   * @returns object of the created collection
-   */
-  override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {
-    return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;
-  }
-
-  /**
-   * Mint new token
-   * @param signer keyring of signer
-   * @param data token data
-   * @returns created token object
-   */
-  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {
-        NFT: {
-          properties: data.properties,
-        },
-      }],
-      true,
-    );
-    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);
-    if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');
-    if(createdTokens.tokens.length < 1) throw Error('No tokens minted');
-    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);
-  }
-
-  /**
-   * Mint multiple NFT tokens
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokens array of tokens with owner and properties
-   * @example
-   * mintMultipleTokens(aliceKeyring, 10, [{
-   *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},
-   *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],
-   *   },{
-   *     owner: {Ethereum: "0x9F0583DbB855d..."},
-   *     properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],
-   * }]);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],
-      true,
-    );
-    const collection = this.getCollectionObject(collectionId);
-    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
-  }
-
-  /**
-   * Mint multiple NFT tokens with one owner
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param owner tokens owner
-   * @param tokens array of tokens with owner and properties
-   * @example
-   * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{
-   *   properties: [{
-   *   key: "gender",
-   *   value: "female",
-   *  },{
-   *   key: "age",
-   *   value: "33",
-   *  }],
-   * }]);
-   * @returns array of newly created tokens
-   */
-  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {
-    const rawTokens = [];
-    for(const token of tokens) {
-      const raw = {NFT: {properties: token.properties}};
-      rawTokens.push(raw);
-    }
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],
-      true,
-    );
-    const collection = this.getCollectionObject(collectionId);
-    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
-  }
-
-  /**
-   * Set, change, or remove approved address to transfer the ownership of the NFT.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param toAddressObj address to approve
-   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
-    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);
-  }
-}
-
-
-class RFTGroup extends NFTnRFT {
-  /**
-   * Get collection object
-   * @param collectionId ID of collection
-   * @example getCollectionObject(2);
-   * @returns instance of UniqueRFTCollection
-   */
-  override getCollectionObject(collectionId: number): UniqueRFTCollection {
-    return new UniqueRFTCollection(collectionId, this.helper);
-  }
-
-  /**
-   * Get token object
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @example getTokenObject(10, 5);
-   * @returns instance of UniqueNFTToken
-   */
-  override getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {
-    return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));
-  }
-
-  /**
-   * Get top 10 token owners with the largest number of pieces
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @example getTokenTop10Owners(10, 5);
-   * @returns array of top 10 owners
-   */
-  async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<ICrossAccountId[]> {
-    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map(a => a.toICrossAccountId());
-  }
-
-  /**
-   * Get number of pieces owned by address
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param addressObj address token owner
-   * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});
-   * @returns number of pieces ownerd by address
-   */
-  async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();
-  }
-
-  /**
-   * Transfer pieces of token to another address
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param addressObj address of a new owner
-   * @param amount number of pieces to be transfered
-   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
-    return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);
-  }
-
-  /**
-   * Change ownership of some pieces of RFT on behalf of the owner.
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param fromAddressObj address on behalf of which the token will be sent
-   * @param toAddressObj new token owner
-   * @param amount number of pieces to be transfered
-   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
-    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);
-  }
-
-  /**
-   * Mint new collection
-   * @param signer keyring of signer
-   * @param collectionOptions Collection options
-   * @example
-   * mintCollection(aliceKeyring, {
-   *   name: 'New',
-   *   description: 'New collection',
-   *   tokenPrefix: 'NEW',
-   * })
-   * @returns object of the created collection
-   */
-  override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {
-    return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;
-  }
-
-  /**
-   * Mint new token
-   * @param signer keyring of signer
-   * @param data token data
-   * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});
-   * @returns created token object
-   */
-  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {
-        ReFungible: {
-          pieces: data.pieces,
-          properties: data.properties,
-        },
-      }],
-      true,
-    );
-    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);
-    if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');
-    if(createdTokens.tokens.length < 1) throw Error('No tokens minted');
-    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);
-  }
-
-  mintMultipleTokens(_signer: TSigner, _collectionId: number, _tokens: { owner: ICrossAccountId, pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {
-    throw Error('Not implemented');
-    // const creationResult = await this.helper.executeExtrinsic(
-    //   signer,
-    //   'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],
-    //   true, // `Unable to mint RFT tokens for ${label}`,
-    // );
-    // const collection = this.getCollectionObject(collectionId);
-    // return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
-  }
-
-  /**
-   * Mint multiple RFT tokens with one owner
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param owner tokens owner
-   * @param tokens array of tokens with properties and pieces
-   * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);
-   * @returns array of newly created RFT tokens
-   */
-  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {
-    const rawTokens = [];
-    for(const token of tokens) {
-      const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};
-      rawTokens.push(raw);
-    }
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],
-      true,
-    );
-    const collection = this.getCollectionObject(collectionId);
-    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
-  }
-
-  /**
-   * Destroys a concrete instance of RFT.
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param amount number of pieces to be burnt
-   * @example burnToken(aliceKeyring, 10, 5);
-   * @returns ```true``` if the extrinsic is successful, otherwise ```false```
-   */
-  override async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {
-    return await super.burnToken(signer, collectionId, tokenId, amount);
-  }
-
-  /**
-   * Destroys a concrete instance of RFT on behalf of the owner.
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param fromAddressObj address on behalf of which the token will be burnt
-   * @param amount number of pieces to be burnt
-   * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  override async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
-    return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);
-  }
-
-  /**
-   * Set, change, or remove approved address to transfer the ownership of the RFT.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param toAddressObj address to approve
-   * @param amount number of pieces to be approved
-   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);
-   * @returns true if the token success, otherwise false
-   */
-  override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
-    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);
-  }
-
-  /**
-   * Get total number of pieces
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @example getTokenTotalPieces(10, 5);
-   * @returns number of pieces
-   */
-  async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();
-  }
-
-  /**
-   * Change number of token pieces. Signer must be the owner of all token pieces.
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param tokenId ID of token
-   * @param amount new number of pieces
-   * @example repartitionToken(aliceKeyring, 10, 5, 12345n);
-   * @returns true if the repartion was success, otherwise false
-   */
-  async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {
-    const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);
-    const repartitionResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.repartition', [collectionId, tokenId, amount],
-      true,
-    );
-    if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');
-    return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');
-  }
-}
-
-
-class FTGroup extends CollectionGroup {
-  /**
-   * Get collection object
-   * @param collectionId ID of collection
-   * @example getCollectionObject(2);
-   * @returns instance of UniqueFTCollection
-   */
-  getCollectionObject(collectionId: number): UniqueFTCollection {
-    return new UniqueFTCollection(collectionId, this.helper);
-  }
-
-  /**
-   * Mint new fungible collection
-   * @param signer keyring of signer
-   * @param collectionOptions Collection options
-   * @param decimalPoints number of token decimals
-   * @example
-   * mintCollection(aliceKeyring, {
-   *   name: 'New',
-   *   description: 'New collection',
-   *   tokenPrefix: 'NEW',
-   * }, 18)
-   * @returns newly created fungible collection
-   */
-  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {
-    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
-    if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');
-    collectionOptions.mode = {fungible: decimalPoints};
-    for(const key of ['name', 'description', 'tokenPrefix']) {
-      if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);
-    }
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createCollectionEx', [collectionOptions],
-      true,
-    );
-    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));
-  }
-
-  /**
-   * Mint tokens
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param owner address owner of new tokens
-   * @param amount amount of tokens to be meanted
-   * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {
-        Fungible: {
-          value: amount,
-        },
-      }],
-      true, // `Unable to mint fungible tokens for ${label}`,
-    );
-    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');
-  }
-
-  /**
-   * Mint multiple Fungible tokens with one owner
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param owner tokens owner
-   * @param tokens array of tokens with properties and pieces
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: { value: bigint }[], owner: ICrossAccountId): Promise<boolean> {
-    const rawTokens = [];
-    for(const token of tokens) {
-      const raw = {Fungible: {Value: token.value}};
-      rawTokens.push(raw);
-    }
-    const creationResult = await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],
-      true,
-    );
-    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');
-  }
-
-  /**
-   * Get the top 10 owners with the largest balance for the Fungible collection
-   * @param collectionId ID of collection
-   * @example getTop10Owners(10);
-   * @returns array of ```ICrossAccountId```
-   */
-  async getTop10Owners(collectionId: number): Promise<ICrossAccountId[]> {
-    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map(a => a.toICrossAccountId());
-  }
-
-  /**
-   * Get account balance
-   * @param collectionId ID of collection
-   * @param addressObj address of owner
-   * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})
-   * @returns amount of fungible tokens owned by address
-   */
-  async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();
-  }
-
-  /**
-   * Transfer tokens to address
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param toAddressObj address recipient
-   * @param amount amount of tokens to be sent
-   * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);
-  }
-
-  /**
-   * Transfer some tokens on behalf of the owner.
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param fromAddressObj address on behalf of which tokens will be sent
-   * @param toAddressObj address where token to be sent
-   * @param amount number of tokens to be sent
-   * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);
-  }
-
-  /**
-   * Destroy some amount of tokens
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param amount amount of tokens to be destroyed
-   * @example burnTokens(aliceKeyring, 10, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async burnTokens(signer: IKeyringPair, collectionId: number, amount = 1n): Promise<boolean> {
-    return await super.burnToken(signer, collectionId, 0, amount);
-  }
-
-  /**
-   * Burn some tokens on behalf of the owner.
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param fromAddressObj address on behalf of which tokens will be burnt
-   * @param amount amount of tokens to be burnt
-   * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
-    return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);
-  }
-
-  /**
-   * Get total collection supply
-   * @param collectionId
-   * @returns
-   */
-  async getTotalPieces(collectionId: number): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();
-  }
-
-  /**
-   * Set, change, or remove approved address to transfer tokens.
-   *
-   * @param signer keyring of signer
-   * @param collectionId ID of collection
-   * @param toAddressObj address to be approved
-   * @param amount amount of tokens to be approved
-   * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {
-    return super.approveToken(signer, collectionId, 0, toAddressObj, amount);
-  }
-
-  /**
-   * Get amount of fungible tokens approved to transfer
-   * @param collectionId ID of collection
-   * @param fromAddressObj owner of tokens
-   * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner
-   * @returns number of tokens approved for the transfer
-   */
-  getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
-    return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);
-  }
-}
-
-
-class ChainGroup extends HelperGroup<ChainHelperBase> {
-  /**
-   * Get system properties of a chain
-   * @example getChainProperties();
-   * @returns ss58Format, token decimals, and token symbol
-   */
-  getChainProperties(): IChainProperties {
-    const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();
-    return {
-      ss58Format: properties.ss58Format.toJSON(),
-      tokenDecimals: properties.tokenDecimals.toJSON(),
-      tokenSymbol: properties.tokenSymbol.toJSON(),
-    };
-  }
-
-  /**
-   * Get chain header
-   * @example getLatestBlockNumber();
-   * @returns the number of the last block
-   */
-  async getLatestBlockNumber(): Promise<number> {
-    return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();
-  }
-
-  /**
-   * Get block hash by block number
-   * @param blockNumber number of block
-   * @example getBlockHashByNumber(12345);
-   * @returns hash of a block
-   */
-  async getBlockHashByNumber(blockNumber: number): Promise<string | null> {
-    const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();
-    if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;
-    return blockHash;
-  }
-
-  // TODO add docs
-  async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {
-    const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);
-    if(!blockHash) return null;
-    return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;
-  }
-
-  /**
-   * Get latest relay block
-   * @returns {number} relay block
-   */
-  async getRelayBlockNumber(): Promise<bigint> {
-    const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;
-    return BigInt(blockNumber);
-  }
-
-  /**
-   * Get account nonce
-   * @param address substrate address
-   * @example getNonce("5GrwvaEF5zXb26Fz...");
-   * @returns number, account's nonce
-   */
-  async getNonce(address: TSubstrateAccount): Promise<number> {
-    return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();
-  }
-}
-
-export class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
-  /**
- * Get substrate address balance
- * @param address substrate address
- * @example getSubstrate("5GrwvaEF5zXb26Fz...")
- * @returns amount of tokens on address
- */
-  async getSubstrate(address: TSubstrateAccount): Promise<bigint> {
-    return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();
-  }
-
-  /**
-   * Transfer tokens to substrate address
-   * @param signer keyring of signer
-   * @param address substrate address of a recipient
-   * @param amount amount of tokens to be transfered
-   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);
-
-    let transfer = {from: null, to: null, amount: 0n} as any;
-    result.result.events.forEach(({event: {data, method, section}}: any) => {
-      if((section === 'balances') && (method === 'Transfer')) {
-        transfer = {
-          from: this.helper.address.normalizeSubstrate(data[0]),
-          to: this.helper.address.normalizeSubstrate(data[1]),
-          amount: BigInt(data[2]),
-        };
-      }
-    });
-    const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from
-      && this.helper.address.normalizeSubstrate(address) === transfer.to
-      && BigInt(amount) === transfer.amount;
-    return isSuccess;
-  }
-
-  /**
-   * Get full substrate balance including free, frozen, and reserved
-   * @param address substrate address
-   * @returns
-   */
-  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {
-    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;
-    return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};
-  }
-
-  /**
-   * Get total issuance
-   * @returns
-   */
-  async getTotalIssuance(): Promise<bigint> {
-    const total = (await this.helper.callRpc('api.query.balances.totalIssuance', []));
-    return total.toBigInt();
-  }
-
-  async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> {
-    const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();
-    return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons}));
-  }
-  async getFrozen(address: TSubstrateAccount): Promise<{ id: string, amount: bigint }[]> {
-    const locks = (await this.helper.api!.query.balances.freezes(address)) as unknown as Array<any>;
-    return locks.map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()}));
-  }
-}
-
-export class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
-  /**
-   * Get ethereum address balance
-   * @param address ethereum address
-   * @example getEthereum("0x9F0583DbB855d...")
-   * @returns amount of tokens on address
-   */
-  async getEthereum(address: TEthereumAccount): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();
-  }
-
-  /**
-   * Transfer tokens to address
-   * @param signer keyring of signer
-   * @param address Ethereum address of a recipient
-   * @param amount amount of tokens to be transfered
-   * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);
-
-    let transfer = {from: null, to: null, amount: 0n} as any;
-    result.result.events.forEach(({event: {data, method, section}}: any) => {
-      if((section === 'balances') && (method === 'Transfer')) {
-        transfer = {
-          from: data[0].toString(),
-          to: data[1].toString(),
-          amount: BigInt(data[2]),
-        };
-      }
-    });
-    const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from
-      && address === transfer.to
-      && BigInt(amount) === transfer.amount;
-    return isSuccess;
-  }
-}
-
-class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
-  subBalanceGroup: SubstrateBalanceGroup<T>;
-  ethBalanceGroup: EthereumBalanceGroup<T>;
-
-  constructor(helper: T) {
-    super(helper);
-    this.subBalanceGroup = new SubstrateBalanceGroup(helper);
-    this.ethBalanceGroup = new EthereumBalanceGroup(helper);
-  }
-
-  getCollectionCreationPrice(): bigint {
-    return 2n * this.getOneTokenNominal();
-  }
-  /**
-   * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).
-   * @example getOneTokenNominal()
-   * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.
-   */
-  getOneTokenNominal(): bigint {
-    const chainProperties = this.helper.chain.getChainProperties();
-    return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);
-  }
-
-  /**
-   * Get substrate address balance
-   * @param address substrate address
-   * @example getSubstrate("5GrwvaEF5zXb26Fz...")
-   * @returns amount of tokens on address
-   */
-  getSubstrate(address: TSubstrateAccount): Promise<bigint> {
-    return this.subBalanceGroup.getSubstrate(address);
-  }
-
-  /**
-   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved
-   * @param address substrate address
-   * @returns
-   */
-  getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {
-    return this.subBalanceGroup.getSubstrateFull(address);
-  }
-
-  /**
-   * Get total issuance
-   * @returns
-   */
-  getTotalIssuance(): Promise<bigint> {
-    return this.subBalanceGroup.getTotalIssuance();
-  }
-
-  /**
-   * Get locked balances
-   * @param address substrate address
-   * @returns locked balances with reason via api.query.balances.locks
-   * @deprecated all the methods should switch to getFrozen
-   */
-  getLocked(address: TSubstrateAccount) {
-    return this.subBalanceGroup.getLocked(address);
-  }
-
-  /**
-   * Get frozen balances
-   * @param address substrate address
-   * @returns frozen balances with id via api.query.balances.freezes
-   */
-  getFrozen(address: TSubstrateAccount) {
-    return this.subBalanceGroup.getFrozen(address);
-  }
-
-  /**
-   * Get ethereum address balance
-   * @param address ethereum address
-   * @example getEthereum("0x9F0583DbB855d...")
-   * @returns amount of tokens on address
-   */
-  getEthereum(address: TEthereumAccount): Promise<bigint> {
-    return this.ethBalanceGroup.getEthereum(address);
-  }
-
-  async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint) {
-    await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceSetBalance', [address, amount], true);
-  }
-
-  /**
-   * Transfer tokens to substrate address
-   * @param signer keyring of signer
-   * @param address substrate address of a recipient
-   * @param amount amount of tokens to be transfered
-   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);
-   * @returns ```true``` if extrinsic success, otherwise ```false```
-   */
-  transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {
-    return this.subBalanceGroup.transferToSubstrate(signer, address, amount);
-  }
-
-  async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {
-    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);
-
-    let transfer = {from: null, to: null, amount: 0n} as any;
-    result.result.events.forEach(({event: {data, method, section}}: any) => {
-      if((section === 'balances') && (method === 'Transfer')) {
-        transfer = {
-          from: this.helper.address.normalizeSubstrate(data[0]),
-          to: this.helper.address.normalizeSubstrate(data[1]),
-          amount: BigInt(data[2]),
-        };
-      }
-    });
-    let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;
-    isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;
-    isSuccess = isSuccess && BigInt(amount) === transfer.amount;
-    return isSuccess;
-  }
-
-  /**
-   * Transfer tokens with the unlock period
-   * @param signer signers Keyring
-   * @param address Substrate address of recipient
-   * @param schedule Schedule params
-   * @example vestedTransfer(signer, recepient.address, 20000, 100, 10, 50 * nominal); // total amount of vested tokens will be 100 * 50 = 5000
-   */
-  async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: { start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }): Promise<void> {
-    const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]);
-    const event = result.result.events
-      .find((e: any) => e.event.section === 'vesting' &&
-        e.event.method === 'VestingScheduleAdded' &&
-        e.event.data[0].toHuman() === signer.address);
-    if(!event) throw Error('Cannot find transfer in events');
-  }
-
-  /**
-   * Get schedule for recepient of vested transfer
-   * @param address Substrate address of recipient
-   * @returns
-   */
-  async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> {
-    const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();
-    return schedule.map((schedule: any) => ({
-      start: BigInt(schedule.start),
-      period: BigInt(schedule.period),
-      periodCount: BigInt(schedule.periodCount),
-      perPeriod: BigInt(schedule.perPeriod),
-    }));
-  }
-
-  /**
-   * Claim vested tokens
-   * @param signer signers Keyring
-   */
-  async claim(signer: TSigner) {
-    const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);
-    const event = result.result.events
-      .find((e: any) => e.event.section === 'vesting' &&
-        e.event.method === 'Claimed' &&
-        e.event.data[0].toHuman() === signer.address);
-    if(!event) throw Error('Cannot find claim in events');
-  }
-}
-
-class AddressGroup extends HelperGroup<ChainHelperBase> {
-  /**
-   * Normalizes the address to the specified ss58 format, by default ```42```.
-   * @param address substrate address
-   * @param ss58Format format for address conversion, by default ```42```
-   * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
-   * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation
-   */
-  normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {
-    return CrossAccountId.normalizeSubstrateAddress({Substrate: address}, ss58Format);
-  }
-
-  /**
-   * Get address in the connected chain format
-   * @param address substrate address
-   * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network
-   * @returns address in chain format
-   */
-  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {
-    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);
-  }
-
-  /**
-   * Get substrate mirror of an ethereum address
-   * @param ethAddress ethereum address
-   * @param toChainFormat false for normalized account
-   * @example ethToSubstrate('0x9F0583DbB855d...')
-   * @returns substrate mirror of a provided ethereum address
-   */
-  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat = false): TSubstrateAccount {
-    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);
-  }
-
-  /**
-   * Get ethereum mirror of a substrate address
-   * @param subAddress substrate account
-   * @example substrateToEth("5DnSF6RRjwteE3BrC...")
-   * @returns ethereum mirror of a provided substrate address
-   */
-  substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {
-    return CrossAccountId.translateSubToEth(subAddress);
-  }
-
-  /**
-   * Encode key to substrate address
-   * @param key key for encoding address
-   * @param ss58Format prefix for encoding to the address of the corresponding network
-   * @returns encoded substrate address
-   */
-  encodeSubstrateAddress(key: Uint8Array | string | bigint, ss58Format = 42): string {
-    const u8a: Uint8Array = typeof key === 'string'
-      ? hexToU8a(key)
-      : typeof key === 'bigint'
-        ? hexToU8a(key.toString(16))
-        : key;
-
-    if(ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {
-      throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);
-    }
-
-    const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];
-    if(!allowedDecodedLengths.includes(u8a.length)) {
-      throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);
-    }
-
-    const u8aPrefix = ss58Format < 64
-      ? new Uint8Array([ss58Format])
-      : new Uint8Array([
-        ((ss58Format & 0xfc) >> 2) | 0x40,
-        (ss58Format >> 8) | ((ss58Format & 0x03) << 6),
-      ]);
-
-    const input = u8aConcat(u8aPrefix, u8a);
-
-    return base58Encode(u8aConcat(
-      input,
-      blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),
-    ));
-  }
-
-  /**
-   * Restore substrate address from bigint representation
-   * @param number decimal representation of substrate address
-   * @returns substrate address
-   */
-  restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {
-    if(this.helper.api === null) {
-      throw 'Not connected';
-    }
-    const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();
-    if(res === undefined || res === null) {
-      throw 'Restore address error';
-    }
-    return res.toString();
-  }
-
-  /**
-   * Convert etherium cross account id to substrate cross account id
-   * @param ethCrossAccount etherium cross account
-   * @returns substrate cross account id
-   */
-  convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {
-    if(ethCrossAccount.sub === '0') {
-      return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};
-    }
-
-    const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));
-    return {Substrate: ss58};
-  }
-
-  paraSiblingSovereignAccount(paraid: number) {
-    // We are getting a *sibling* parachain sovereign account,
-    // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c
-    const siblingPrefix = '0x7369626c';
-
-    const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);
-    const suffix = '000000000000000000000000000000000000000000000000';
-
-    return siblingPrefix + encodedParaId + suffix;
-  }
-}
-
-
-class StakingGroup extends HelperGroup<UniqueHelper> {
-  /**
-   * Stake tokens for App Promotion
-   * @param signer keyring of signer
-   * @param amountToStake amount of tokens to stake
-   * @param label extra label for log
-   * @returns
-   */
-  async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {
-    if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;
-    await this.helper.executeExtrinsic(
-      signer, 'api.tx.appPromotion.stake',
-      [amountToStake], true,
-    );
-    // TODO extract info from stakeResult
-    return true;
-  }
-
-  /**
-   * Unstake all staked tokens
-   * @param signer keyring of signer
-   * @param amountToUnstake amount of tokens to unstake
-   * @param label extra label for log
-   * @returns block hash where unstake happened
-   */
-  async unstakeAll(signer: TSigner, label?: string): Promise<string> {
-    if(typeof label === 'undefined') label = `${signer.address}`;
-    const unstakeResult = await this.helper.executeExtrinsic(
-      signer, 'api.tx.appPromotion.unstakeAll',
-      [], true,
-    );
-    return unstakeResult.blockHash;
-  }
-
-  /**
-   * Unstake the part of a staked tokens
-   * @param signer keyring of signer
-   * @param amount amount of tokens to unstake
-   * @param label extra label for log
-   * @returns block hash where unstake happened
-   */
-  async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise<string> {
-    if(typeof label === 'undefined') label = `${signer.address}`;
-    const unstakeResult = await this.helper.executeExtrinsic(
-      signer, 'api.tx.appPromotion.unstakePartial',
-      [amount], true,
-    );
-    return unstakeResult.blockHash;
-  }
-
-  /**
-   * Get total number of active stakes
-   * @param address substrate address
-   * @returns {number}
-   */
-  async getStakesNumber(address: ICrossAccountId): Promise<number> {
-    if('Ethereum' in address) throw Error('only substrate address');
-    return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber();
-  }
-
-  /**
-   * Get total staked amount for address
-   * @param address substrate or ethereum address
-   * @returns total staked amount
-   */
-  async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {
-    if(address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();
-    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();
-  }
-
-  /**
-   * Get total staked per block
-   * @param address substrate or ethereum address
-   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block
-   */
-  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
-    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);
-    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({
-      block: block.toBigInt(),
-      amount: amount.toBigInt(),
-    }));
-  }
-
-  /**
-   * Get total pending unstake amount for address
-   * @param address substrate or ethereum address
-   * @returns total pending unstake amount
-   */
-  async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {
-    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();
-  }
-
-  /**
-   * Get pending unstake amount per block for address
-   * @param address substrate or ethereum address
-   * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block
-   */
-  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
-    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);
-    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({
-      block: block.toBigInt(),
-      amount: amount.toBigInt(),
-    }));
-    return result;
-  }
-}
-
-
-class PreimageGroup extends HelperGroup<UniqueHelper> {
-  async getPreimageInfo(h256: string) {
-    return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON();
-  }
-
-  /**
-   * Create a preimage from an API call.
-   * @param signer keyring of the signer.
-   * @param call an extrinsic call
-   * @example await notePreimageFromCall(preimageMaker,
-   *   helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd])
-   * );
-   * @returns promise of extrinsic execution.
-   */
-  notePreimageFromCall(signer: TSigner, call: any, returnPreimageHash = false) {
-    return this.notePreimage(signer, call.method.toHex(), returnPreimageHash);
-  }
-
-  /**
-   * Create a preimage with a hex or a byte array.
-   * @param signer keyring of the signer.
-   * @param bytes preimage encoded in hex or a byte array, e.g. an extrinsic call.
-   * @example await notePreimage(preimageMaker,
-   *   helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex()
-   * );
-   * @returns promise of extrinsic execution.
-   */
-  async notePreimage(signer: TSigner, bytes: string | Uint8Array, returnPreimageHash = false) {
-    const promise = this.helper.executeExtrinsic(signer, 'api.tx.preimage.notePreimage', [bytes]);
-    if(returnPreimageHash) {
-      const result = await promise;
-      const events = result.result.events.filter((x: any) => x.event.method === 'Noted' && x.event.section === 'preimage');
-      const preimageHash = events[0].event.data[0].toHuman();
-      return preimageHash;
-    }
-    return promise;
-  }
-
-  /**
-   * Delete an existing preimage and return the deposit.
-   * @param signer keyring of the signer - either the owner or the preimage manager (sudo).
-   * @param h256 hash of the preimage.
-   * @returns promise of extrinsic execution.
-   */
-  unnotePreimage(signer: TSigner, h256: string) {
-    return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unnotePreimage', [h256]);
-  }
-
-  /**
-   * Request a preimage be uploaded to the chain without paying any fees or deposits.
-   * @param signer keyring of the signer - either the owner or the preimage manager (sudo).
-   * @param h256 hash of the preimage.
-   * @returns promise of extrinsic execution.
-   */
-  requestPreimage(signer: TSigner, h256: string) {
-    return this.helper.executeExtrinsic(signer, 'api.tx.preimage.requestPreimage', [h256]);
-  }
-
-  /**
-   * Clear a previously made request for a preimage.
-   * @param signer keyring of the signer - either the owner or the preimage manager (sudo).
-   * @param h256 hash of the preimage.
-   * @returns promise of extrinsic execution.
-   */
-  unrequestPreimage(signer: TSigner, h256: string) {
-    return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]);
-  }
-}
-
-class UtilityGroup<T extends ChainHelperBase> extends HelperGroup<T> {
-  async batch(signer: TSigner, txs: any[]) {
-    return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batch', [txs]);
-  }
-
-  async batchAll(signer: TSigner, txs: any[]) {
-    return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batchAll', [txs]);
-  }
-
-  batchAllCall(txs: any[]) {
-    return this.helper.constructApiCall('api.tx.utility.batchAll', [txs]);
-  }
-}
-
-export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase;
-export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper;
-
-export class UniqueHelper extends ChainHelperBase {
-  balance: BalanceGroup<UniqueHelper>;
-  collection: CollectionGroup;
-  nft: NFTGroup;
-  rft: RFTGroup;
-  ft: FTGroup;
-  staking: StakingGroup;
-  preimage: PreimageGroup;
-  utility: UtilityGroup<UniqueHelper>;
-
-  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
-    super(logger, options.helperBase ?? UniqueHelper);
-
-    this.balance = new BalanceGroup(this);
-    this.collection = new CollectionGroup(this);
-    this.nft = new NFTGroup(this);
-    this.rft = new RFTGroup(this);
-    this.ft = new FTGroup(this);
-    this.staking = new StakingGroup(this);
-    this.preimage = new PreimageGroup(this);
-    this.utility = new UtilityGroup(this);
-  }
-}
-
-export class UniqueBaseCollection {
-  helper: UniqueHelper;
-  collectionId: number;
-
-  constructor(collectionId: number, uniqueHelper: UniqueHelper) {
-    this.collectionId = collectionId;
-    this.helper = uniqueHelper;
-  }
-
-  async getData() {
-    return await this.helper.collection.getData(this.collectionId);
-  }
-
-  async getLastTokenId() {
-    return await this.helper.collection.getLastTokenId(this.collectionId);
-  }
-
-  async doesTokenExist(tokenId: number) {
-    return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);
-  }
-
-  async getAdmins() {
-    return await this.helper.collection.getAdmins(this.collectionId);
-  }
-
-  async getAllowList() {
-    return await this.helper.collection.getAllowList(this.collectionId);
-  }
-
-  async getEffectiveLimits() {
-    return await this.helper.collection.getEffectiveLimits(this.collectionId);
-  }
-
-  async getProperties(propertyKeys?: string[] | null) {
-    return await this.helper.collection.getProperties(this.collectionId, propertyKeys);
-  }
-
-  async getPropertiesConsumedSpace() {
-    return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);
-  }
-
-  async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {
-    return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);
-  }
-
-  async getOptions() {
-    return await this.helper.collection.getCollectionOptions(this.collectionId);
-  }
-
-  async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {
-    return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);
-  }
-
-  async confirmSponsorship(signer: TSigner) {
-    return await this.helper.collection.confirmSponsorship(signer, this.collectionId);
-  }
-
-  async removeSponsor(signer: TSigner) {
-    return await this.helper.collection.removeSponsor(signer, this.collectionId);
-  }
-
-  async setLimits(signer: TSigner, limits: ICollectionLimits) {
-    return await this.helper.collection.setLimits(signer, this.collectionId, limits);
-  }
-
-  async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {
-    return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);
-  }
-
-  async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {
-    return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);
-  }
-
-  async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {
-    return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);
-  }
-
-  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {
-    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);
-  }
-
-  async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {
-    return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);
-  }
-
-  async setProperties(signer: TSigner, properties: IProperty[]) {
-    return await this.helper.collection.setProperties(signer, this.collectionId, properties);
-  }
-
-  async deleteProperties(signer: TSigner, propertyKeys: string[]) {
-    return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);
-  }
-
-  async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {
-    return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);
-  }
-
-  async enableNesting(signer: TSigner, permissions: INestingPermissions) {
-    return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);
-  }
-
-  async disableNesting(signer: TSigner) {
-    return await this.helper.collection.disableNesting(signer, this.collectionId);
-  }
-
-  async burn(signer: TSigner) {
-    return await this.helper.collection.burn(signer, this.collectionId);
-  }
-}
-
-export class UniqueNFTCollection extends UniqueBaseCollection {
-  getTokenObject(tokenId: number) {
-    return new UniqueNFToken(tokenId, this);
-  }
-
-  async getTokensByAddress(addressObj: ICrossAccountId) {
-    return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);
-  }
-
-  async getToken(tokenId: number, blockHashAt?: string) {
-    return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);
-  }
-
-  async getTokenOwner(tokenId: number, blockHashAt?: string) {
-    return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);
-  }
-
-  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {
-    return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);
-  }
-
-  async getTokenChildren(tokenId: number, blockHashAt?: string) {
-    return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);
-  }
-
-  async getPropertyPermissions(propertyKeys: string[] | null = null) {
-    return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);
-  }
-
-  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {
-    return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
-  }
-
-  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {
-    const api = this.helper.getApi();
-    const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any;
-
-    return props?.consumedSpace ?? 0;
-  }
-
-  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {
-    return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);
-  }
-
-  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
-    return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);
-  }
-
-  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {
-    return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);
-  }
-
-  async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {
-    return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);
-  }
-
-  async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {
-    return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});
-  }
-
-  async mintMultipleTokens(signer: TSigner, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]) {
-    return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);
-  }
-
-  async burnToken(signer: TSigner, tokenId: number) {
-    return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);
-  }
-
-  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {
-    return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);
-  }
-
-  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
-    return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);
-  }
-
-  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {
-    return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);
-  }
-
-  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {
-    return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);
-  }
-
-  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {
-    return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);
-  }
-
-  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
-    return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
-  }
-}
-
-export class UniqueRFTCollection extends UniqueBaseCollection {
-  getTokenObject(tokenId: number) {
-    return new UniqueRFToken(tokenId, this);
-  }
-
-  async getToken(tokenId: number, blockHashAt?: string) {
-    return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);
-  }
-
-  async getTokenOwner(tokenId: number, blockHashAt?: string) {
-    return await this.helper.rft.getTokenOwner(this.collectionId, tokenId, blockHashAt);
-  }
-
-  async getTokensByAddress(addressObj: ICrossAccountId) {
-    return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);
-  }
-
-  async getTop10TokenOwners(tokenId: number) {
-    return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);
-  }
-
-  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {
-    return await this.helper.rft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);
-  }
-
-  async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {
-    return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);
-  }
-
-  async getTokenTotalPieces(tokenId: number) {
-    return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);
-  }
-
-  async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
-    return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);
-  }
-
-  async getPropertyPermissions(propertyKeys: string[] | null = null) {
-    return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);
-  }
-
-  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {
-    return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
-  }
-
-  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {
-    const api = this.helper.getApi();
-    const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any;
-
-    return props?.consumedSpace ?? 0;
-  }
-
-  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);
-  }
-
-  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);
-  }
-
-  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);
-  }
-
-  async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {
-    return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);
-  }
-
-  async mintToken(signer: TSigner, pieces = 1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {
-    return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});
-  }
-
-  async mintMultipleTokens(signer: TSigner, tokens: { pieces: bigint, owner: ICrossAccountId, properties?: IProperty[] }[]) {
-    return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);
-  }
-
-  async burnToken(signer: TSigner, tokenId: number, amount = 1n) {
-    return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);
-  }
-
-  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);
-  }
-
-  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
-    return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);
-  }
-
-  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {
-    return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);
-  }
-
-  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {
-    return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);
-  }
-
-  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {
-    return await this.helper.rft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);
-  }
-
-  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
-    return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
-  }
-}
-
-export class UniqueFTCollection extends UniqueBaseCollection {
-  async getBalance(addressObj: ICrossAccountId) {
-    return await this.helper.ft.getBalance(this.collectionId, addressObj);
-  }
-
-  async getTotalPieces() {
-    return await this.helper.ft.getTotalPieces(this.collectionId);
-  }
-
-  async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
-    return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);
-  }
-
-  async getTop10Owners() {
-    return await this.helper.ft.getTop10Owners(this.collectionId);
-  }
-
-  async mint(signer: TSigner, amount = 1n, owner: ICrossAccountId = {Substrate: signer.address}) {
-    return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);
-  }
-
-  async mintWithOneOwner(signer: TSigner, tokens: { value: bigint }[], owner: ICrossAccountId = {Substrate: signer.address}) {
-    return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);
-  }
-
-  async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);
-  }
-
-  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);
-  }
-
-  async burnTokens(signer: TSigner, amount = 1n) {
-    return await this.helper.ft.burnTokens(signer, this.collectionId, amount);
-  }
-
-  async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);
-  }
-
-  async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);
-  }
-}
-
-export class UniqueBaseToken {
-  collection: UniqueNFTCollection | UniqueRFTCollection;
-  collectionId: number;
-  tokenId: number;
-
-  constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {
-    this.collection = collection;
-    this.collectionId = collection.collectionId;
-    this.tokenId = tokenId;
-  }
-
-  async getNextSponsored(addressObj: ICrossAccountId) {
-    return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);
-  }
-
-  async getProperties(propertyKeys?: string[] | null) {
-    return await this.collection.getTokenProperties(this.tokenId, propertyKeys);
-  }
-
-  async getTokenPropertiesConsumedSpace() {
-    return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);
-  }
-
-  async setProperties(signer: TSigner, properties: IProperty[]) {
-    return await this.collection.setTokenProperties(signer, this.tokenId, properties);
-  }
-
-  async deleteProperties(signer: TSigner, propertyKeys: string[]) {
-    return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);
-  }
-
-  async doesExist() {
-    return await this.collection.doesTokenExist(this.tokenId);
-  }
-
-  nestingAccount(): ICrossAccountId {
-    return this.collection.helper.util.getTokenAccount(this);
-  }
-}
-
-export class UniqueNFToken extends UniqueBaseToken {
-  declare collection: UniqueNFTCollection;
-
-  constructor(tokenId: number, collection: UniqueNFTCollection) {
-    super(tokenId, collection);
-    this.collection = collection;
-  }
-
-  async getData(blockHashAt?: string) {
-    return await this.collection.getToken(this.tokenId, blockHashAt);
-  }
-
-  async getOwner(blockHashAt?: string) {
-    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);
-  }
-
-  async getTopmostOwner(blockHashAt?: string) {
-    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);
-  }
-
-  async getChildren(blockHashAt?: string) {
-    return await this.collection.getTokenChildren(this.tokenId, blockHashAt);
-  }
-
-  async nest(signer: TSigner, toTokenObj: IToken) {
-    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);
-  }
-
-  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
-    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);
-  }
-
-  async transfer(signer: TSigner, addressObj: ICrossAccountId) {
-    return await this.collection.transferToken(signer, this.tokenId, addressObj);
-  }
-
-  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
-    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);
-  }
-
-  async approve(signer: TSigner, toAddressObj: ICrossAccountId) {
-    return await this.collection.approveToken(signer, this.tokenId, toAddressObj);
-  }
-
-  async isApproved(toAddressObj: ICrossAccountId) {
-    return await this.collection.isTokenApproved(this.tokenId, toAddressObj);
-  }
-
-  async burn(signer: TSigner) {
-    return await this.collection.burnToken(signer, this.tokenId);
-  }
-
-  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {
-    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);
-  }
-}
-
-export class UniqueRFToken extends UniqueBaseToken {
-  declare collection: UniqueRFTCollection;
-
-  constructor(tokenId: number, collection: UniqueRFTCollection) {
-    super(tokenId, collection);
-    this.collection = collection;
-  }
-
-  async getData(blockHashAt?: string) {
-    return await this.collection.getToken(this.tokenId, blockHashAt);
-  }
-
-  async getOwner(blockHashAt?: string) {
-    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);
-  }
-
-  async getTop10Owners() {
-    return await this.collection.getTop10TokenOwners(this.tokenId);
-  }
-
-  async getTopmostOwner(blockHashAt?: string) {
-    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);
-  }
-
-  async nest(signer: TSigner, toTokenObj: IToken) {
-    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);
-  }
-
-  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
-    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);
-  }
-
-  async getBalance(addressObj: ICrossAccountId) {
-    return await this.collection.getTokenBalance(this.tokenId, addressObj);
-  }
-
-  async getTotalPieces() {
-    return await this.collection.getTokenTotalPieces(this.tokenId);
-  }
-
-  async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {
-    return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);
-  }
-
-  async transfer(signer: TSigner, addressObj: ICrossAccountId, amount = 1n) {
-    return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);
-  }
-
-  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);
-  }
-
-  async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);
-  }
-
-  async repartition(signer: TSigner, amount: bigint) {
-    return await this.collection.repartitionToken(signer, this.tokenId, amount);
-  }
-
-  async burn(signer: TSigner, amount = 1n) {
-    return await this.collection.burnToken(signer, this.tokenId, amount);
-  }
-
-  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {
-    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);
-  }
-}
deletedjs-packages/playgrounds/src/unique.xcm.tsdiffbeforeafterboth
--- a/js-packages/playgrounds/src/unique.xcm.ts
+++ /dev/null
@@ -1,375 +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, IForeignAssetMetadata, 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 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 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, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
-    await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.foreignAssets.registerForeignAsset',
-      [ownerAddress, location, metadata],
-      true,
-    );
-  }
-
-  async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {
-    await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.foreignAssets.updateForeignAsset',
-      [foreignAssetId, location, metadata],
-      true,
-    );
-  }
-}
-
-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 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);
-  }
-}
-
deletedjs-packages/playgrounds/tsconfig.jsondiffbeforeafterboth
--- a/js-packages/playgrounds/tsconfig.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "extends": "../tsconfig.packages.json",
-  "compilerOptions": {
-    "rootDir": "./src",
-    "outDir": "dist"
-  },
-  "references": [
-    { "path": "../types/tsconfig.json" },
-  ]
-}
\ No newline at end of file
addedjs-packages/playgrounds/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/playgrounds/types.ts
@@ -0,0 +1,232 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+import type {IKeyringPair} from '@polkadot/types/types';
+
+export const NON_EXISTENT_COLLECTION_ID = 4_294_967_295;
+
+export const MILLISECS_PER_BLOCK = 12000;
+export const MINUTES = 60_000 / MILLISECS_PER_BLOCK;
+export const HOURS = MINUTES * 60;
+export const DAYS = HOURS * 24;
+
+export interface IEvent {
+  section: string;
+  method: string;
+  index: [number, number] | string;
+  data: any[];
+  phase: {applyExtrinsic: number} | 'Initialization',
+}
+
+export interface IPhasicEvent {
+  phase: any, // {ApplyExtrinsic: number} | 'Initialization',
+  event: IEvent;
+}
+
+export interface ITransactionResult {
+  status: 'Fail' | 'Success';
+  result: {
+      dispatchError: any,
+      events: IPhasicEvent[];
+  },
+  blockHash: string,
+  moduleError?: string | object;
+}
+
+export interface ISubscribeBlockEventsData {
+  number: number;
+  hash: string;
+  timestamp: number;
+  events: IEvent[];
+}
+
+export interface ILogger {
+  log: (msg: any, level?: string) => void;
+  level: {
+    ERROR: 'ERROR';
+    WARNING: 'WARNING';
+    INFO: 'INFO';
+    [key: string]: string;
+  }
+}
+
+export interface IUniqueHelperLog {
+  executedAt: number;
+  executionTime: number;
+  type: 'extrinsic' | 'rpc';
+  status: 'Fail' | 'Success';
+  call: string;
+  params: any[];
+  moduleError?: string;
+  dispatchError?: any;
+  events?: any;
+}
+
+export interface IApiListeners {
+  connected?: (...args: any[]) => any;
+  disconnected?: (...args: any[]) => any;
+  error?: (...args: any[]) => any;
+  ready?: (...args: any[]) => any;
+  decorated?: (...args: any[]) => any;
+}
+
+export type ICrossAccountId = {
+  Substrate: TSubstrateAccount;
+} | {
+  Ethereum: TEthereumAccount;
+}
+
+export type ICrossAccountIdLower = {
+  substrate: TSubstrateAccount;
+} | {
+  ethereum: TEthereumAccount;
+};
+
+export interface IEthCrossAccountId {
+  0: TEthereumAccount;
+  1: TSubstrateAccount;
+  eth: TEthereumAccount;
+  sub: TSubstrateAccount;
+}
+
+export interface ICollectionLimits {
+  accountTokenOwnershipLimit?: number | null;
+  sponsoredDataSize?: number | null;
+  sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;
+  tokenLimit?: number | null;
+  sponsorTransferTimeout?: number | null;
+  sponsorApproveTimeout?: number | null;
+  ownerCanTransfer?: boolean | null;
+  ownerCanDestroy?: boolean | null;
+  transfersEnabled?: boolean | null;
+}
+
+export interface INestingPermissions {
+  tokenOwner?: boolean;
+  collectionAdmin?: boolean;
+  restricted?: number[] | null;
+}
+
+export interface ICollectionPermissions {
+  access?: 'Normal' | 'AllowList';
+  mintMode?: boolean;
+  nesting?: INestingPermissions;
+}
+
+export interface IProperty {
+  key: string;
+  value?: string;
+}
+
+export interface ITokenPropertyPermission {
+  key: string;
+  permission: {
+    mutable?: boolean;
+    tokenOwner?: boolean;
+    collectionAdmin?: boolean;
+  }
+}
+
+export interface IToken {
+  collectionId: number;
+  tokenId: number;
+}
+
+export interface IBlock {
+  extrinsics: IExtrinsic[]
+  header: {
+    parentHash: string,
+    number: number,
+  };
+}
+
+export interface IExtrinsic {
+  isSigned: boolean,
+  method: {
+    method: string,
+    section: string,
+    args: any[]
+  }
+}
+
+export interface ICollectionFlags {
+  foreign: boolean,
+  erc721metadata: boolean,
+}
+
+export enum CollectionFlag {
+  None = 0,
+  /// External collections can't be managed using `unique` api
+  External = 1,
+  /// Supports ERC721Metadata
+  Erc721metadata = 64,
+  /// Tokens in foreign collections can be transferred, but not burnt
+  Foreign = 128,
+}
+
+export interface ICollectionCreationOptions {
+  name?: string | number[];
+  description?: string | number[];
+  tokenPrefix?: string | number[];
+  mode?: {
+    nft?: null;
+    refungible?: null;
+    fungible?: number;
+  }
+  permissions?: ICollectionPermissions;
+  properties?: IProperty[];
+  tokenPropertyPermissions?: ITokenPropertyPermission[];
+  limits?: ICollectionLimits;
+  pendingSponsor?: ICrossAccountId;
+  adminList?: ICrossAccountId[];
+  flags?: number[] | CollectionFlag[] ,
+}
+
+export interface IChainProperties {
+  ss58Format: number;
+  tokenDecimals: number[];
+  tokenSymbol: string[]
+}
+
+export interface ISubstrateBalance {
+  free: bigint,
+  reserved: bigint,
+  frozen: bigint,
+}
+
+export interface IStakingInfo {
+  block: bigint,
+  amount: bigint,
+}
+
+export interface IPovInfo {
+  proofSize: number,
+  compactProofSize: number,
+  compressedProofSize: number,
+  results: any[],
+  kv: any,
+}
+
+export interface ISchedulerOptions {
+  scheduledId?: string,
+  priority?: number,
+  periodic?: {
+    period: number,
+    repetitions: number,
+  },
+}
+
+export interface DemocracySplitAccount {
+  aye: bigint,
+  nay: bigint,
+}
+
+export type TSubstrateAccount = string;
+export type TEthereumAccount = string;
+export type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';
+export type TUniqueNetworks = 'opal' | 'quartz' | 'unique';
+export type TSiblingNetworkds = 'moonbeam' | 'moonriver' | 'acala' | 'karura' | 'westmint';
+export type TRelayNetworks = 'rococo' | 'westend';
+export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;
+export type TSigner = IKeyringPair; // | 'string'
+export type TCollectionMode = 'nft' | 'rft' | 'ft';
addedjs-packages/playgrounds/types.xcm.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/playgrounds/types.xcm.ts
@@ -0,0 +1,36 @@
+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,
+  },
+}
+
+export interface IForeignAssetMetadata {
+  name?: number | Uint8Array,
+  symbol?: string,
+  decimals?: number,
+  minimalBalance?: bigint,
+}
\ No newline at end of file
addedjs-packages/playgrounds/unique.dev.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/playgrounds/unique.dev.ts
@@ -0,0 +1,1547 @@
+// 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} 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 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.transfer', [{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.transfer', [{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 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,
+      );
+    }
+  };
+}
addedjs-packages/playgrounds/unique.governance.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/playgrounds/unique.governance.ts
@@ -0,0 +1,531 @@
+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);
+  }*/
+}
addedjs-packages/playgrounds/unique.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/playgrounds/unique.ts
@@ -0,0 +1,3498 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+/* eslint-disable @typescript-eslint/no-var-requires */
+/* eslint-disable function-call-argument-newline */
+/* eslint-disable no-prototype-builtins */
+
+import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';
+import type {SignerOptions} from '@polkadot/api/types';
+import type {AugmentedSubmittables} from '@polkadot/api-base/types/submittable';
+import type {ApiInterfaceEvents} from '@polkadot/api/types';
+import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {hexToU8a} from '@polkadot/util/hex';
+import {u8aConcat} from '@polkadot/util/u8a';
+import type {
+  IApiListeners,
+  IBlock,
+  IEvent,
+  IChainProperties,
+  ICollectionCreationOptions,
+  ICollectionLimits,
+  ICollectionPermissions,
+  ICrossAccountId,
+  ICrossAccountIdLower,
+  ILogger,
+  INestingPermissions,
+  IProperty,
+  IStakingInfo,
+  ISubstrateBalance,
+  IToken,
+  ITokenPropertyPermission,
+  ITransactionResult,
+  IUniqueHelperLog,
+  TApiAllowedListeners,
+  TEthereumAccount,
+  TSigner,
+  TSubstrateAccount,
+  TNetworks,
+  IEthCrossAccountId,
+} from './types.js';
+import type {RuntimeDispatchInfo} from '@polkadot/types/interfaces';
+
+export class CrossAccountId {
+  account: ICrossAccountId;
+
+  constructor(account: ICrossAccountId) {
+    this.account = account;
+  }
+
+  static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {
+    switch (domain) {
+      case 'Substrate': return new CrossAccountId({Substrate: account.address});
+      case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();
+    }
+  }
+
+  static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {
+    if('substrate' in address) return new CrossAccountId({Substrate: address.substrate});
+    else return new CrossAccountId({Ethereum: address.ethereum});
+  }
+
+  static normalizeSubstrateAddress(address: {Substrate: TSubstrateAccount}, ss58Format = 42): TSubstrateAccount {
+    return encodeAddress(decodeAddress(address.Substrate), ss58Format);
+  }
+
+  static withNormalizedSubstrate(address: ICrossAccountId, ss58Format = 42): ICrossAccountId {
+    if('Substrate' in address) return {Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)};
+    return address;
+  }
+
+  withNormalizedSubstrate(ss58Format = 42): CrossAccountId {
+    if('Substrate' in this.account) this.account = CrossAccountId.withNormalizedSubstrate(this.account, ss58Format);
+    return this;
+  }
+
+  static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {
+    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));
+  }
+
+  toEthereum(): CrossAccountId {
+    this.account = CrossAccountId.toEthereum(this.account);
+    return this;
+  }
+
+  static toEthereum(account: ICrossAccountId): ICrossAccountId {
+    if('Substrate' in account) return {Ethereum: CrossAccountId.translateSubToEth(account.Substrate)};
+    return account;
+  }
+
+  static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {
+    return evmToAddress(address, ss58Format);
+  }
+
+  toSubstrate(ss58Format?: number): CrossAccountId {
+    this.account = CrossAccountId.toSubstrate(this.account, ss58Format);
+    return this;
+  }
+
+  static toSubstrate(account: ICrossAccountId, ss58Format?: number): ICrossAccountId {
+    if('Ethereum' in account) return {Substrate: CrossAccountId.translateEthToSub(account.Ethereum, ss58Format)};
+    return account;
+  }
+
+  toLowerCase(): CrossAccountId {
+    this.account = CrossAccountId.toLowerCase(this.account);
+    return this;
+  }
+
+  static toLowerCase(account: ICrossAccountId) {
+    if('Substrate' in account) return {Substrate: account.Substrate.toLowerCase()};
+    if('Ethereum' in account) return {Ethereum: account.Ethereum.toLowerCase()};
+    return account;
+  }
+
+  toICrossAccountId(): ICrossAccountId {
+    return this.account;
+  }
+}
+
+const nesting = {
+  toChecksumAddress(address: string): string {
+    if(typeof address === 'undefined') return '';
+
+    if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);
+
+    address = address.toLowerCase().replace(/^0x/i, '');
+    const addressHash = keccakAsHex(address).replace(/^0x/i, '');
+    const checksumAddress = ['0x'];
+
+    for(let i = 0; i < address.length; i++) {
+      // If ith character is 8 to f then make it uppercase
+      if(parseInt(addressHash[i], 16) > 7) {
+        checksumAddress.push(address[i].toUpperCase());
+      } else {
+        checksumAddress.push(address[i]);
+      }
+    }
+    return checksumAddress.join('');
+  },
+  tokenIdToAddress(collectionId: number, tokenId: number) {
+    return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);
+  },
+};
+
+class UniqueUtil {
+  static transactionStatus = {
+    NOT_READY: 'NotReady',
+    FAIL: 'Fail',
+    SUCCESS: 'Success',
+  };
+
+  static chainLogType = {
+    EXTRINSIC: 'extrinsic',
+    RPC: 'rpc',
+  };
+
+  static getTokenAccount(token: IToken): ICrossAccountId {
+    return {Ethereum: this.getTokenAddress(token)};
+  }
+
+  static getTokenAddress(token: IToken): string {
+    return nesting.tokenIdToAddress(token.collectionId, token.tokenId);
+  }
+
+  static getDefaultLogger(): ILogger {
+    return {
+      log(msg: any, level = 'INFO') {
+        console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));
+      },
+      level: {
+        ERROR: 'ERROR',
+        WARNING: 'WARNING',
+        INFO: 'INFO',
+      },
+    };
+  }
+
+  static vec2str(arr: string[] | number[]) {
+    return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');
+  }
+
+  static str2vec(string: string) {
+    if(typeof string !== 'string') return string;
+    return Array.from(string).map(x => x.charCodeAt(0));
+  }
+
+  static fromSeed(seed: string, ss58Format = 42) {
+    const keyring = new Keyring({type: 'sr25519', ss58Format});
+    return keyring.addFromUri(seed);
+  }
+
+  static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {
+    if(creationResult.status !== this.transactionStatus.SUCCESS) {
+      throw Error('Unable to create collection!');
+    }
+
+    let collectionId = null;
+    creationResult.result.events.forEach(({event: {data, method, section}}) => {
+      if((section === 'common') && (method === 'CollectionCreated')) {
+        collectionId = parseInt(data[0].toString(), 10);
+      }
+    });
+
+    if(collectionId === null) {
+      throw Error('No CollectionCreated event was found!');
+    }
+
+    return collectionId;
+  }
+
+  static extractTokensFromCreationResult(creationResult: ITransactionResult): {
+    success: boolean,
+    tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],
+  } {
+    if(creationResult.status !== this.transactionStatus.SUCCESS) {
+      throw Error('Unable to create tokens!');
+    }
+    let success = false;
+    const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];
+    creationResult.result.events.forEach(({event: {data, method, section}}) => {
+      if(method === 'ExtrinsicSuccess') {
+        success = true;
+      } else if((section === 'common') && (method === 'ItemCreated')) {
+        tokens.push({
+          collectionId: parseInt(data[0].toString(), 10),
+          tokenId: parseInt(data[1].toString(), 10),
+          owner: data[2].toHuman(),
+          amount: data[3].toBigInt(),
+        });
+      }
+    });
+    return {success, tokens};
+  }
+
+  static extractTokensFromBurnResult(burnResult: ITransactionResult): {
+    success: boolean,
+    tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],
+  } {
+    if(burnResult.status !== this.transactionStatus.SUCCESS) {
+      throw Error('Unable to burn tokens!');
+    }
+    let success = false;
+    const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];
+    burnResult.result.events.forEach(({event: {data, method, section}}: any) => {
+      if(method === 'ExtrinsicSuccess') {
+        success = true;
+      } else if((section === 'common') && (method === 'ItemDestroyed')) {
+        tokens.push({
+          collectionId: parseInt(data[0].toString(), 10),
+          tokenId: parseInt(data[1].toString(), 10),
+          owner: data[2].toHuman(),
+          amount: data[3].toBigInt(),
+        });
+      }
+    });
+    return {success, tokens};
+  }
+
+  static findCollectionInEvents(events: { event: IEvent }[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {
+    let eventId = null;
+    events.forEach(({event: {data, method, section}}) => {
+      if((section === expectedSection) && (method === expectedMethod)) {
+        eventId = parseInt(data[0].toString(), 10);
+      }
+    });
+
+    if(eventId === null) {
+      throw Error(`No ${expectedMethod} event was found!`);
+    }
+    return eventId === collectionId;
+  }
+
+  static isTokenTransferSuccess(events: { event: IEvent }[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
+    const normalizeAddress = (address: string | ICrossAccountId) => {
+      if(typeof address === 'string') return address;
+      if('Substrate' in address) return CrossAccountId.withNormalizedSubstrate(address);
+      if('Ethereum' in address) return CrossAccountId.toLowerCase(address);
+      return address;
+    };
+    let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;
+    events.forEach(({event: {data, method, section}}) => {
+      if((section === 'common') && (method === 'Transfer')) {
+        transfer = {
+          collectionId: data[0].toJSON(),
+          tokenId: data[1].toJSON(),
+          from: normalizeAddress(data[2].toHuman()),
+          to: normalizeAddress(data[3].toHuman()),
+          amount: BigInt(data[4].toJSON()),
+        };
+      }
+    });
+    let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;
+    isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);
+    isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);
+    isSuccess = isSuccess && amount === transfer.amount;
+    return isSuccess;
+  }
+
+  static bigIntToDecimals(number: bigint, decimals = 18) {
+    const numberStr = number.toString();
+    const dotPos = numberStr.length - decimals;
+
+    if(dotPos <= 0) {
+      return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;
+    } else {
+      const intPart = numberStr.substring(0, dotPos);
+      const fractPart = numberStr.substring(dotPos);
+      return intPart + '.' + fractPart;
+    }
+  }
+}
+
+class UniqueEventHelper {
+  static extractIndex(index: any): [number, number] | string {
+    if(index.toRawType() === '[u8;2]') return [index[0], index[1]];
+    return index.toJSON();
+  }
+
+  static extractSub(data: any, subTypes: any): { [key: string]: any } {
+    let obj: any = {};
+    let index = 0;
+
+    if(data.entries) {
+      for(const [key, value] of data.entries()) {
+        obj[key] = this.extractData(value, subTypes[index]);
+        index++;
+      }
+    } else obj = data.toJSON();
+
+    return obj;
+  }
+
+  static toHuman(data: any) {
+    return data && data.toHuman ? data.toHuman() : `${data}`;
+  }
+
+  static extractData(data: any, type: any): any {
+    if(!type) return this.toHuman(data);
+    if(['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();
+    if(['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();
+    if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);
+    return this.toHuman(data);
+  }
+
+  public static extractEvents(events: { event: any, phase: any }[]): IEvent[] {
+    const parsedEvents: IEvent[] = [];
+
+    events.forEach((record) => {
+      const {event, phase} = record;
+      const types = event.typeDef;
+
+      const eventData: IEvent = {
+        section: event.section.toString(),
+        method: event.method.toString(),
+        index: this.extractIndex(event.index),
+        data: [],
+        phase: phase.toJSON(),
+      };
+
+      event.data.forEach((val: any, index: number) => {
+        eventData.data.push(this.extractData(val, types[index]));
+      });
+
+      parsedEvents.push(eventData);
+    });
+
+    return parsedEvents;
+  }
+}
+const InvalidTypeSymbol = Symbol('Invalid type');
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+export type Invalid =
+  | ((
+    invalidType: typeof InvalidTypeSymbol,
+    ..._: typeof InvalidTypeSymbol[]
+  ) => typeof InvalidTypeSymbol)
+  | null
+  | undefined;
+// Has slightly better error messages than Get
+type Get2<T, P extends string, E> =
+  P extends `${infer Key}.${infer Key2}` ? Key extends keyof T ? Key2 extends keyof T[Key] ? T[Key][Key2] : E : E : E;
+type ForceFunction<T> = T extends (...args: any) => any ? T : (...args: any) => Invalid;
+
+export class ChainHelperBase {
+  helperBase: any;
+
+  transactionStatus = UniqueUtil.transactionStatus;
+  chainLogType = UniqueUtil.chainLogType;
+  util: typeof UniqueUtil;
+  eventHelper: typeof UniqueEventHelper;
+  logger: ILogger;
+  api: ApiPromise | null;
+  forcedNetwork: TNetworks | null;
+  network: TNetworks | null;
+  wsEndpoint: string | null;
+  chainLog: IUniqueHelperLog[];
+  children: ChainHelperBase[];
+  address: AddressGroup;
+  chain: ChainGroup;
+
+  constructor(logger?: ILogger, helperBase?: any) {
+    this.helperBase = helperBase;
+
+    this.util = UniqueUtil;
+    this.eventHelper = UniqueEventHelper;
+    if(typeof logger == 'undefined') logger = this.util.getDefaultLogger();
+    this.logger = logger;
+    this.api = null;
+    this.forcedNetwork = null;
+    this.network = null;
+    this.wsEndpoint = null;
+    this.chainLog = [];
+    this.children = [];
+    this.address = new AddressGroup(this);
+    this.chain = new ChainGroup(this);
+  }
+
+  clone(helperCls: ChainHelperBaseConstructor, options: { [key: string]: any } = {}) {
+    Object.setPrototypeOf(helperCls.prototype, this);
+    const newHelper = new helperCls(this.logger, options);
+
+    newHelper.api = this.api;
+    newHelper.network = this.network;
+    newHelper.forceNetwork = this.forceNetwork;
+
+    this.children.push(newHelper);
+
+    return newHelper;
+  }
+
+  getEndpoint(): string {
+    if(this.wsEndpoint === null) throw Error('No connection was established');
+    return this.wsEndpoint;
+  }
+
+  getApi(): ApiPromise {
+    if(this.api === null) throw Error('API not initialized');
+    return this.api;
+  }
+
+  async subscribeEvents(expectedEvents: { section: string, names: string[] }[]) {
+    const collectedEvents: IEvent[] = [];
+    const unsubscribe = await this.getApi().query.system.events((events: any) => {
+      const ievents = this.eventHelper.extractEvents(events);
+      ievents.forEach((event) => {
+        expectedEvents.forEach((e => {
+          if(event.section === e.section && e.names.includes(event.method)) {
+            collectedEvents.push(event);
+          }
+        }));
+      });
+    });
+    return {unsubscribe: unsubscribe as any, collectedEvents};
+  }
+
+  clearChainLog(): void {
+    this.chainLog = [];
+  }
+
+  forceNetwork(value: TNetworks): void {
+    this.forcedNetwork = value;
+  }
+
+  async connect(wsEndpoint: string, listeners?: IApiListeners) {
+    if(this.api !== null) throw Error('Already connected');
+    const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);
+    this.wsEndpoint = wsEndpoint;
+    this.api = api;
+    this.network = network;
+  }
+
+  async disconnect() {
+    for(const child of this.children) {
+      child.clearApi();
+    }
+
+    if(this.api === null) return;
+    await this.api.disconnect();
+    this.clearApi();
+  }
+
+  clearApi() {
+    this.api = null;
+    this.network = null;
+  }
+
+  static async detectNetwork(api: ApiPromise): Promise<TNetworks> {
+    const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;
+    const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];
+
+    if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;
+
+    if(['quartz', 'unique', 'sapphire'].indexOf(spec.specName) > -1) return spec.specName;
+    return 'opal';
+  }
+
+  static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {
+    if(!wsEndpoint) throw new Error('wsEndpoint was not set');
+    const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});
+    await api.isReady;
+
+    const network = await this.detectNetwork(api);
+
+    await api.disconnect();
+
+    return network;
+  }
+
+  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{
+    api: ApiPromise;
+    network: TNetworks;
+  }> {
+    if(typeof network === 'undefined' || network === null) network = 'opal';
+    if(!wsEndpoint) throw new Error('wsEndpoint was not set');
+    const supportedRPC = {
+      opal: {
+        unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,
+      },
+      quartz: {
+        unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,
+      },
+      unique: {
+        unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,
+      },
+      rococo: {},
+      westend: {},
+      moonbeam: {},
+      moonriver: {},
+      acala: {},
+      karura: {},
+      westmint: {},
+    };
+    if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);
+    const rpc = supportedRPC[network] as any;
+
+    // TODO: investigate how to replace rpc in runtime
+    // api._rpcCore.addUserInterfaces(rpc);
+
+    const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});
+
+    await api.isReadyOrError;
+
+    if(typeof listeners === 'undefined') listeners = {};
+    for(const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {
+      if(!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;
+      api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);
+    }
+
+    return {api, network};
+  }
+
+  getTransactionStatus(data: { events: { event: IEvent }[], status: any }) {
+    const {events, status} = data;
+    if(status.isReady) {
+      return this.transactionStatus.NOT_READY;
+    }
+    if(status.isBroadcast) {
+      return this.transactionStatus.NOT_READY;
+    }
+    if(status.isInBlock || status.isFinalized) {
+      const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');
+      if(errors.length > 0) {
+        return this.transactionStatus.FAIL;
+      }
+      if(events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {
+        return this.transactionStatus.SUCCESS;
+      }
+    }
+
+    return this.transactionStatus.FAIL;
+  }
+
+  signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {
+    const sign = (callback: any) => {
+      if(options !== null) return transaction.signAndSend(sender, options, callback);
+      return transaction.signAndSend(sender, callback);
+    };
+    // eslint-disable-next-line no-async-promise-executor
+    return new Promise(async (resolve, reject) => {
+      try {
+        const unsub = await sign((result: any) => {
+          const status = this.getTransactionStatus(result);
+
+          if(status === this.transactionStatus.SUCCESS) {
+            this.logger.log(`${label} successful`);
+            unsub();
+            resolve({result, status, blockHash: result.status.asInBlock.toHuman()});
+          } else if(status === this.transactionStatus.FAIL) {
+            let moduleError = null;
+
+            if(result.hasOwnProperty('dispatchError')) {
+              const dispatchError = result['dispatchError'];
+
+              if(dispatchError) {
+                if(dispatchError.isModule) {
+                  const modErr = dispatchError.asModule;
+                  const errorMeta = dispatchError.registry.findMetaError(modErr);
+
+                  moduleError = `${errorMeta.section}.${errorMeta.name}`;
+                } else if(dispatchError.isToken) {
+                  moduleError = `Token: ${dispatchError.asToken}`;
+                } else {
+                  // May be [object Object] in case of unhandled non-unit enum
+                  moduleError = `Misc: ${dispatchError.toHuman()}`;
+                }
+              } else {
+                this.logger.log(result, this.logger.level.ERROR);
+              }
+            }
+
+            this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);
+            unsub();
+            reject({status, moduleError, result});
+          }
+        });
+      } catch (e) {
+        this.logger.log(e, this.logger.level.ERROR);
+        reject(e);
+      }
+    });
+  }
+
+  async signTransactionWithoutSending(signer: TSigner, tx: any) {
+    const api = this.getApi();
+    const signingInfo = await api.derive.tx.signingInfo(signer.address);
+
+    tx.sign(signer, {
+      blockHash: api.genesisHash,
+      genesisHash: api.genesisHash,
+      runtimeVersion: api.runtimeVersion,
+      nonce: signingInfo.nonce,
+    });
+
+    return tx.toHex();
+  }
+
+  async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {
+    const api = this.getApi();
+    const signingInfo = await api.derive.tx.signingInfo(signer.address);
+
+    // We need to sign the tx because
+    // unsigned transactions does not have an inclusion fee
+    tx.sign(signer, {
+      blockHash: api.genesisHash,
+      genesisHash: api.genesisHash,
+      runtimeVersion: api.runtimeVersion,
+      nonce: signingInfo.nonce,
+    });
+
+    if(len === null) {
+      return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;
+    } else {
+      return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;
+    }
+  }
+
+  constructApiCall(apiCall: string, params: any[]) {
+    if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);
+    let call = this.getApi() as any;
+    for(const part of apiCall.slice(4).split('.')) {
+      call = call[part];
+      if(!call) {
+        const advice = part.includes('_') ? ' Looks like it needs to be converted to camel case.' : '';
+        throw Error(`Function ${part} of api call ${apiCall} not found.${advice}`);
+      }
+    }
+    return call(...params);
+  }
+
+  encodeApiCall(apiCall: string, params: any[]) {
+    return this.constructApiCall(apiCall, params).method.toHex();
+  }
+
+  async executeExtrinsic<
+    E extends string,
+    V extends (
+      ...args: any) => any = ForceFunction<
+        Get2<
+          AugmentedSubmittables<'promise'>,
+          E, (...args: any) => Invalid
+        >
+      >
+  >(
+    sender: TSigner,
+    extrinsic: `api.tx.${E}`,
+    params: Parameters<V>,
+    expectSuccess = true,
+    options: Partial<SignerOptions> | null = null,/*, failureMessage='expected success'*/
+  ): Promise<ITransactionResult> {
+    if(this.api === null) throw Error('API not initialized');
+
+    const startTime = (new Date()).getTime();
+    let result: ITransactionResult;
+    let events: IEvent[] = [];
+    try {
+      result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;
+      events = this.eventHelper.extractEvents(result.result.events);
+      const errorEvent = events.find((event) => event.method == 'ExecutedFailed' || event.method == 'CreatedFailed');
+      if(errorEvent)
+        throw Error(errorEvent.method + ': ' + extrinsic);
+    }
+    catch (e) {
+      if(!(e as object).hasOwnProperty('status')) throw e;
+      result = e as ITransactionResult;
+    }
+
+    const endTime = (new Date()).getTime();
+
+    const log = {
+      executedAt: endTime,
+      executionTime: endTime - startTime,
+      type: this.chainLogType.EXTRINSIC,
+      status: result.status,
+      call: extrinsic,
+      signer: this.getSignerAddress(sender),
+      params,
+    } as IUniqueHelperLog;
+
+    let errorMessage = '';
+
+    if(result.status !== this.transactionStatus.SUCCESS) {
+      if(result.moduleError) {
+        errorMessage = typeof result.moduleError === 'string'
+          ? result.moduleError
+          : `${Object.keys(result.moduleError)[0]}: ${Object.values(result.moduleError)[0]}`;
+        log.moduleError = errorMessage;
+      }
+      else if(result.result.dispatchError) log.dispatchError = result.result.dispatchError;
+    }
+    if(events.length > 0) log.events = events;
+
+    this.chainLog.push(log);
+
+    if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {
+      if(result.moduleError) throw Error(`${errorMessage}`);
+      else if(result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));
+    }
+    return result as any;
+  }
+  executeExtrinsicUncheckedWeight<
+      E extends string,
+      V extends (
+         ...args: any) => any = ForceFunction<
+            Get2<
+               AugmentedSubmittables<'promise'>,
+               E, (...args: any) => Invalid
+            >
+         >
+   >(
+    _sender: TSigner,
+    _extrinsic: `api.tx.${E}`,
+    _params: Parameters<V>,
+    _expectSuccess = true,
+    _options: Partial<SignerOptions> | null = null,/*, failureMessage='expected success'*/
+  ): Promise<ITransactionResult> {
+    throw new Error('executeExtrinsicUncheckedWeight only supported in sudo');
+  }
+
+  async callRpc
+  // TODO: make it strongly typed, or use api.query/api.rpc directly
+  // <
+  // K extends 'rpc' | 'query',
+  // E extends string,
+  // V extends (...args: any) => any = ForceFunction<
+  //   Get2<
+  //     K extends 'rpc' ? DecoratedRpc<'promise', RpcInterface> : QueryableStorage<'promise'>,
+  //     E, (...args: any) => Invalid<'not found'>
+  //   >
+  // >,
+  // P = Parameters<V>,
+  // >
+  (rpc: string, params?: any[]): Promise<any> {
+
+    if(typeof params === 'undefined') params = [] as any;
+    if(this.api === null) throw Error('API not initialized');
+    if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);
+
+    const startTime = (new Date()).getTime();
+    let result;
+    let error = null;
+    const log = {
+      type: this.chainLogType.RPC,
+      call: rpc,
+      params,
+    } as any as IUniqueHelperLog;
+
+    try {
+      result = await this.constructApiCall(rpc, params as any);
+    }
+    catch (e) {
+      error = e;
+    }
+
+    const endTime = (new Date()).getTime();
+
+    log.executedAt = endTime;
+    log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';
+    log.executionTime = endTime - startTime;
+
+    this.chainLog.push(log);
+
+    if(error !== null) throw error;
+
+    return result;
+  }
+
+  getSignerAddress(signer: IKeyringPair | string): string {
+    if(typeof signer === 'string') return signer;
+    return signer.address;
+  }
+
+  fetchAllPalletNames(): string[] {
+    if(this.api === null) throw Error('API not initialized');
+    return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()).sort();
+  }
+
+  fetchMissingPalletNames(requiredPallets: readonly string[]): string[] {
+    const palletNames = this.fetchAllPalletNames();
+    return requiredPallets.filter(p => !palletNames.includes(p));
+  }
+}
+
+
+export class HelperGroup<T extends ChainHelperBase> {
+  helper: T;
+
+  constructor(uniqueHelper: T) {
+    this.helper = uniqueHelper;
+  }
+}
+
+
+class CollectionGroup extends HelperGroup<UniqueHelper> {
+  /**
+ * Get number of blocks when sponsored transaction is available.
+ *
+ * @param collectionId ID of collection
+ * @param tokenId ID of token
+ * @param addressObj address for which the sponsorship is checked
+ * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});
+ * @returns number of blocks or null if sponsorship hasn't been set
+ */
+  async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {
+    return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();
+  }
+
+  /**
+   * Get the number of created collections.
+   *
+   * @returns number of created collections
+   */
+  async getTotalCount(): Promise<number> {
+    return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();
+  }
+
+  /**
+   * Get information about the collection with additional data,
+   * including the number of tokens it contains, its administrators,
+   * the normalized address of the collection's owner, and decoded name and description.
+   *
+   * @param collectionId ID of collection
+   * @example await getData(2)
+   * @returns collection information object
+   */
+  async getData(collectionId: number): Promise<{
+    id: number;
+    name: string;
+    description: string;
+    tokensCount: number;
+    admins: CrossAccountId[];
+    normalizedOwner: TSubstrateAccount;
+    raw: any
+  } | null> {
+    const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);
+    const humanCollection = collection.toHuman(), collectionData = {
+      id: collectionId, name: null, description: null, tokensCount: 0, admins: [],
+      raw: humanCollection,
+    } as any, jsonCollection = collection.toJSON();
+    if(humanCollection === null) return null;
+    collectionData.raw.limits = jsonCollection.limits;
+    collectionData.raw.permissions = jsonCollection.permissions;
+    collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);
+    for(const key of ['name', 'description']) {
+      collectionData[key] = this.helper.util.vec2str(humanCollection[key]);
+    }
+
+    collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))
+      ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)
+      : 0;
+    collectionData.admins = await this.getAdmins(collectionId);
+
+    return collectionData;
+  }
+
+  /**
+   * Get the addresses of the collection's administrators, optionally normalized.
+   *
+   * @param collectionId ID of collection
+   * @param normalize whether to normalize the addresses to the default ss58 format
+   * @example await getAdmins(1)
+   * @returns array of administrators
+   */
+  async getAdmins(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {
+    const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman() as ICrossAccountId[];
+
+    return normalize
+      ? admins.map(address => CrossAccountId.withNormalizedSubstrate(address))
+      : admins;
+  }
+
+  /**
+   * Get the addresses added to the collection allow-list, optionally normalized.
+   * @param collectionId ID of collection
+   * @param normalize whether to normalize the addresses to the default ss58 format
+   * @example await getAllowList(1)
+   * @returns array of allow-listed addresses
+   */
+  async getAllowList(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {
+    const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman() as ICrossAccountId[];
+    return normalize
+      ? allowListed.map(address => CrossAccountId.withNormalizedSubstrate(address))
+      : allowListed;
+  }
+
+  /**
+   * Get the effective limits of the collection instead of null for default values
+   *
+   * @param collectionId ID of collection
+   * @example await getEffectiveLimits(2)
+   * @returns object of collection limits
+   */
+  async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {
+    return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();
+  }
+
+  /**
+   * Burns the collection if the signer has sufficient permissions and collection is empty.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @example await helper.collection.burn(aliceKeyring, 3);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async burn(signer: TSigner, collectionId: number): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.destroyCollection', [collectionId],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');
+  }
+
+  /**
+   * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param sponsorAddress Sponsor substrate address
+   * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');
+  }
+
+  /**
+   * Confirms consent to sponsor the collection on behalf of the signer.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @example confirmSponsorship(aliceKeyring, 10)
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.confirmSponsorship', [collectionId],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');
+  }
+
+  /**
+   * Removes the sponsor of a collection, regardless if it consented or not.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @example removeSponsor(aliceKeyring, 10)
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.removeCollectionSponsor', [collectionId],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');
+  }
+
+  /**
+   * Sets the limits of the collection. At least one limit must be specified for a correct call.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param limits collection limits object
+   * @example
+   * await setLimits(
+   *   aliceKeyring,
+   *   10,
+   *   {
+   *     sponsorTransferTimeout: 0,
+   *     ownerCanDestroy: false
+   *   }
+   * )
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.setCollectionLimits', [collectionId, limits],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');
+  }
+
+  /**
+   * Changes the owner of the collection to the new Substrate address.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param ownerAddress substrate address of new owner
+   * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');
+  }
+
+  /**
+   * Adds a collection administrator.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param adminAddressObj Administrator address (substrate or ethereum)
+   * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');
+  }
+
+  /**
+   * Removes a collection administrator.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param adminAddressObj Administrator address (substrate or ethereum)
+   * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');
+  }
+
+  /**
+   * Check if user is in allow list.
+   *
+   * @param collectionId ID of collection
+   * @param user Account to check
+   * @example await getAdmins(1)
+   * @returns is user in allow list
+   */
+  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {
+    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();
+  }
+
+  /**
+   * Adds an address to allow list
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param addressObj address to add to the allow list
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.addToAllowList', [collectionId, addressObj],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');
+  }
+
+  /**
+   * Removes an address from allow list
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param addressObj address to remove from the allow list
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');
+  }
+
+  /**
+   * Sets onchain permissions for selected collection.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param permissions collection permissions object
+   * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.setCollectionPermissions', [collectionId, permissions],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');
+  }
+
+  /**
+   * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param permissions nesting permissions object
+   * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {
+    return await this.setPermissions(signer, collectionId, {nesting: permissions});
+  }
+
+  /**
+   * Disables nesting for selected collection.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @example disableNesting(aliceKeyring, 10);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {
+    return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});
+  }
+
+  /**
+   * Sets onchain properties to the collection.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param properties array of property objects
+   * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.setCollectionProperties', [collectionId, properties],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');
+  }
+
+  /**
+   * Get collection properties.
+   *
+   * @param collectionId ID of collection
+   * @param propertyKeys optionally filter the returned properties to only these keys
+   * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);
+   * @returns array of key-value pairs
+   */
+  async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {
+    return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();
+  }
+
+  async getPropertiesConsumedSpace(collectionId: number): Promise<number> {
+    const api = this.helper.getApi();
+    const props = (await api.query.common.collectionProperties(collectionId)).toJSON();
+
+    return (props! as any).consumedSpace;
+  }
+
+  async getCollectionOptions(collectionId: number) {
+    return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();
+  }
+
+  /**
+   * Deletes onchain properties from the collection.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param propertyKeys array of property keys to delete
+   * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');
+  }
+
+  /**
+   * Changes the owner of the token.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param addressObj address of a new owner
+   * @param amount amount of tokens to be transfered. For NFT must be set to 1n
+   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})
+   * @returns true if the token success, otherwise false
+   */
+  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],
+      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,
+    );
+
+    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);
+  }
+
+  /**
+   *
+   * Change ownership of a token(s) on behalf of the owner.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param fromAddressObj address on behalf of which the token will be sent
+   * @param toAddressObj new token owner
+   * @param amount amount of tokens to be transfered. For NFT must be set to 1n
+   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})
+   * @returns true if the token success, otherwise false
+   */
+  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],
+      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,
+    );
+    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);
+  }
+
+  /**
+   *
+   * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param amount amount of tokens to be burned. For NFT must be set to 1n
+   * @example burnToken(aliceKeyring, 10, 5);
+   * @returns ```true``` if the extrinsic is successful, otherwise ```false```
+   */
+  async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {
+    const burnResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.burnItem', [collectionId, tokenId, amount],
+      true, // `Unable to burn token for ${label}`,
+    );
+    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);
+    if(burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');
+    return burnedTokens.success;
+  }
+
+  /**
+   * Destroys a concrete instance of NFT on behalf of the owner
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param fromAddressObj address on behalf of which the token will be burnt
+   * @param amount amount of tokens to be burned. For NFT must be set to 1n
+   * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
+    const burnResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],
+      true, // `Unable to burn token from for ${label}`,
+    );
+    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);
+    return burnedTokens.success && burnedTokens.tokens.length > 0;
+  }
+
+  /**
+   * Set, change, or remove approved address to transfer the ownership of the NFT.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens
+   * @param amount amount of token to be approved. For NFT must be set to 1n
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
+    const approveResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],
+      true, // `Unable to approve token for ${label}`,
+    );
+
+    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');
+  }
+
+  /**
+   * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param fromAddressObj Signer's Ethereum address containing her tokens
+   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens
+   * @param amount amount of token to be approved. For NFT must be set to 1n
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async approveTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
+    const approveResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.approveFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],
+      true, // `Unable to approve token for ${label}`,
+    );
+
+    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');
+  }
+
+  /**
+   * Set, change, or remove approved address to transfer the ownership of the NFT from eth mirror.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens
+   * @param amount amount of token to be approved. For NFT must be set to 1n
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async approveTokenFromEth(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
+    const ethMirror = CrossAccountId.fromKeyring(signer).toEthereum().toICrossAccountId();
+    return await this.approveTokenFrom(signer, collectionId, tokenId, ethMirror, toAddressObj, amount);
+  }
+
+  /**
+   * Get the amount of token pieces approved to transfer or burn. Normally 0.
+   *
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param toAccountObj address which is approved to use token pieces
+   * @param fromAccountObj address which may have allowed the use of its owned tokens
+   * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})
+   * @returns number of approved to transfer pieces
+   */
+  async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {
+    return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();
+  }
+
+  /**
+   * Get the last created token ID in a collection
+   *
+   * @param collectionId ID of collection
+   * @example getLastTokenId(10);
+   * @returns id of the last created token
+   */
+  async getLastTokenId(collectionId: number): Promise<number> {
+    return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();
+  }
+
+  /**
+   * Check if token exists
+   *
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @example doesTokenExist(10, 20);
+   * @returns true if the token exists, otherwise false
+   */
+  async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {
+    return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();
+  }
+}
+
+class NFTnRFT extends CollectionGroup {
+  /**
+   * Get tokens owned by account
+   *
+   * @param collectionId ID of collection
+   * @param addressObj tokens owner
+   * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})
+   * @returns array of token ids owned by account
+   */
+  async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {
+    return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();
+  }
+
+  /**
+   * Get token data
+   *
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param propertyKeys optionally filter the token properties to only these keys
+   * @param blockHashAt optionally query the data at some block with this hash
+   * @example getToken(10, 5);
+   * @returns human readable token data
+   */
+  async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{
+    properties: IProperty[];
+    owner: ICrossAccountId;
+    normalizedOwner: ICrossAccountId;
+  } | null> {
+    let args;
+    if(typeof blockHashAt === 'undefined') {
+      args = [collectionId, tokenId];
+    }
+    else {
+      if(propertyKeys.length == 0) {
+        const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();
+        if(!collection) return null;
+        propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);
+      }
+      args = [collectionId, tokenId, propertyKeys, blockHashAt];
+    }
+    const tokenData = (await this.helper.callRpc('api.rpc.unique.tokenData', args)).toHuman();
+    if(tokenData === null || tokenData.owner === null) return null;
+    tokenData.normalizedOwner = CrossAccountId.withNormalizedSubstrate(tokenData.owner);
+    return tokenData;
+  }
+
+  /**
+   * Get token's owner
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param blockHashAt optionally query the data at the block with this hash
+   * @example getTokenOwner(10, 5);
+   * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}
+   */
+  async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId> {
+    let owner;
+    if(typeof blockHashAt === 'undefined') {
+      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);
+    } else {
+      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);
+    }
+    return CrossAccountId.fromLowerCaseKeys(owner.toJSON()).toICrossAccountId();
+  }
+
+  /**
+   * Recursively find the address that owns the token
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param blockHashAt
+   * @example getTokenTopmostOwner(10, 5);
+   * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}
+   */
+  async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId | null> {
+    let owner;
+    if(typeof blockHashAt === 'undefined') {
+      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);
+    } else {
+      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);
+    }
+
+    if(owner === null) return null;
+
+    return owner.toHuman();
+  }
+
+  /**
+   * Nest one token into another
+   * @param signer keyring of signer
+   * @param tokenObj token to be nested
+   * @param rootTokenObj token to be parent
+   * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {
+    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);
+    const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);
+    if(!result) {
+      throw Error('Unable to nest token!');
+    }
+    return result;
+  }
+
+  /**
+     * Remove token from nested state
+     * @param signer keyring of signer
+     * @param tokenObj token to unnest
+     * @param rootTokenObj parent of a token
+     * @param toAddressObj address of a new token owner
+     * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});
+     * @returns ```true``` if extrinsic success, otherwise ```false```
+     */
+  async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {
+    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);
+    const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);
+    if(!result) {
+      throw Error('Unable to unnest token!');
+    }
+    return result;
+  }
+
+  /**
+   * Set permissions to change token properties
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param permissions permissions to change a property by the collection admin or token owner
+   * @example setTokenPropertyPermissions(
+   *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]
+   * )
+   * @returns true if extrinsic success otherwise false
+   */
+  async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');
+  }
+
+  /**
+   * Get token property permissions.
+   *
+   * @param collectionId ID of collection
+   * @param propertyKeys optionally filter the returned property permissions to only these keys
+   * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);
+   * @returns array of key-permission pairs
+   */
+  async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {
+    return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();
+  }
+
+  /**
+   * Set token properties
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection
+   * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');
+  }
+
+  /**
+   * Get properties, metadata assigned to a token.
+   *
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param propertyKeys optionally filter the returned properties to only these keys
+   * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);
+   * @returns array of key-value pairs
+   */
+  async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {
+    return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();
+  }
+
+  /**
+   * Delete the provided properties of a token
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param propertyKeys property keys to be deleted
+   * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],
+      true,
+    );
+
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');
+  }
+
+  /**
+   * Mint new collection
+   *
+   * @param signer keyring of signer
+   * @param collectionOptions basic collection options and properties
+   * @param mode NFT or RFT type of a collection
+   * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")
+   * @returns object of the created collection
+   */
+  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {
+    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
+    collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};
+    for(const key of ['name', 'description', 'tokenPrefix']) {
+      if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);
+    }
+
+    let flags = 0;
+    // convert CollectionFlags to number and join them in one number
+    if(collectionOptions.flags) {
+      for(let i = 0; i < collectionOptions.flags.length; i++){
+        const flag = collectionOptions.flags[i];
+        flags = flags | flag;
+      }
+    }
+    collectionOptions.flags = [flags];
+
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createCollectionEx', [collectionOptions],
+      true, // errorLabel,
+    );
+    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));
+  }
+
+  getCollectionObject(_collectionId: number): any {
+    return null;
+  }
+
+  getTokenObject(_collectionId: number, _tokenId: number): any {
+    return null;
+  }
+
+  /**
+   * Tells whether the given `owner` approves the `operator`.
+   * @param collectionId ID of collection
+   * @param owner owner address
+   * @param operator operator addrees
+   * @returns true if operator is enabled
+   */
+  async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {
+    return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();
+  }
+
+  /** Sets or unsets the approval of a given operator.
+   *  The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
+   *  @param operator Operator
+   *  @param approved Should operator status be granted or revoked?
+   *  @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],
+      true,
+    );
+    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');
+  }
+}
+
+
+class NFTGroup extends NFTnRFT {
+  /**
+   * Get collection object
+   * @param collectionId ID of collection
+   * @example getCollectionObject(2);
+   * @returns instance of UniqueNFTCollection
+   */
+  override getCollectionObject(collectionId: number): UniqueNFTCollection {
+    return new UniqueNFTCollection(collectionId, this.helper);
+  }
+
+  /**
+   * Get token object
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @example getTokenObject(10, 5);
+   * @returns instance of UniqueNFTToken
+   */
+  override getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {
+    return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));
+  }
+
+  /**
+   * Is token approved to transfer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param toAccountObj address to be approved
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {
+    return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, (await this.getTokenOwner(collectionId, tokenId)))) === 1n;
+  }
+
+  /**
+   * Changes the owner of the token.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param addressObj address of a new owner
+   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {
+    return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);
+  }
+
+  /**
+   *
+   * Change ownership of a NFT on behalf of the owner.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param fromAddressObj address on behalf of which the token will be sent
+   * @param toAddressObj new token owner
+   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {
+    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);
+  }
+
+  /**
+   * Get tokens nested in the provided token
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param blockHashAt optionally query the data at the block with this hash
+   * @example getTokenChildren(10, 5);
+   * @returns tokens whose depth of nesting is <= 5
+   */
+  async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {
+    let children;
+    if(typeof blockHashAt === 'undefined') {
+      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);
+    } else {
+      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);
+    }
+
+    return children.toJSON().map((x: any) => ({collectionId: x.collection, tokenId: x.token}));
+  }
+
+  /**
+   * Mint new collection
+   * @param signer keyring of signer
+   * @param collectionOptions Collection options
+   * @example
+   * mintCollection(aliceKeyring, {
+   *   name: 'New',
+   *   description: 'New collection',
+   *   tokenPrefix: 'NEW',
+   * })
+   * @returns object of the created collection
+   */
+  override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {
+    return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;
+  }
+
+  /**
+   * Mint new token
+   * @param signer keyring of signer
+   * @param data token data
+   * @returns created token object
+   */
+  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {
+        NFT: {
+          properties: data.properties,
+        },
+      }],
+      true,
+    );
+    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);
+    if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');
+    if(createdTokens.tokens.length < 1) throw Error('No tokens minted');
+    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);
+  }
+
+  /**
+   * Mint multiple NFT tokens
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokens array of tokens with owner and properties
+   * @example
+   * mintMultipleTokens(aliceKeyring, 10, [{
+   *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},
+   *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],
+   *   },{
+   *     owner: {Ethereum: "0x9F0583DbB855d..."},
+   *     properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],
+   * }]);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],
+      true,
+    );
+    const collection = this.getCollectionObject(collectionId);
+    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
+  }
+
+  /**
+   * Mint multiple NFT tokens with one owner
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param owner tokens owner
+   * @param tokens array of tokens with owner and properties
+   * @example
+   * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{
+   *   properties: [{
+   *   key: "gender",
+   *   value: "female",
+   *  },{
+   *   key: "age",
+   *   value: "33",
+   *  }],
+   * }]);
+   * @returns array of newly created tokens
+   */
+  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {
+    const rawTokens = [];
+    for(const token of tokens) {
+      const raw = {NFT: {properties: token.properties}};
+      rawTokens.push(raw);
+    }
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],
+      true,
+    );
+    const collection = this.getCollectionObject(collectionId);
+    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
+  }
+
+  /**
+   * Set, change, or remove approved address to transfer the ownership of the NFT.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param toAddressObj address to approve
+   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
+    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);
+  }
+}
+
+
+class RFTGroup extends NFTnRFT {
+  /**
+   * Get collection object
+   * @param collectionId ID of collection
+   * @example getCollectionObject(2);
+   * @returns instance of UniqueRFTCollection
+   */
+  override getCollectionObject(collectionId: number): UniqueRFTCollection {
+    return new UniqueRFTCollection(collectionId, this.helper);
+  }
+
+  /**
+   * Get token object
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @example getTokenObject(10, 5);
+   * @returns instance of UniqueNFTToken
+   */
+  override getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {
+    return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));
+  }
+
+  /**
+   * Get top 10 token owners with the largest number of pieces
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @example getTokenTop10Owners(10, 5);
+   * @returns array of top 10 owners
+   */
+  async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<ICrossAccountId[]> {
+    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map((a: CrossAccountId) => a.toICrossAccountId());
+  }
+
+  /**
+   * Get number of pieces owned by address
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param addressObj address token owner
+   * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});
+   * @returns number of pieces ownerd by address
+   */
+  async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {
+    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();
+  }
+
+  /**
+   * Transfer pieces of token to another address
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param addressObj address of a new owner
+   * @param amount number of pieces to be transfered
+   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
+    return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);
+  }
+
+  /**
+   * Change ownership of some pieces of RFT on behalf of the owner.
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param fromAddressObj address on behalf of which the token will be sent
+   * @param toAddressObj new token owner
+   * @param amount number of pieces to be transfered
+   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
+    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);
+  }
+
+  /**
+   * Mint new collection
+   * @param signer keyring of signer
+   * @param collectionOptions Collection options
+   * @example
+   * mintCollection(aliceKeyring, {
+   *   name: 'New',
+   *   description: 'New collection',
+   *   tokenPrefix: 'NEW',
+   * })
+   * @returns object of the created collection
+   */
+  override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {
+    return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;
+  }
+
+  /**
+   * Mint new token
+   * @param signer keyring of signer
+   * @param data token data
+   * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});
+   * @returns created token object
+   */
+  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {
+        ReFungible: {
+          pieces: data.pieces,
+          properties: data.properties,
+        },
+      }],
+      true,
+    );
+    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);
+    if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');
+    if(createdTokens.tokens.length < 1) throw Error('No tokens minted');
+    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);
+  }
+
+  mintMultipleTokens(_signer: TSigner, _collectionId: number, _tokens: { owner: ICrossAccountId, pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {
+    throw Error('Not implemented');
+    // const creationResult = await this.helper.executeExtrinsic(
+    //   signer,
+    //   'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],
+    //   true, // `Unable to mint RFT tokens for ${label}`,
+    // );
+    // const collection = this.getCollectionObject(collectionId);
+    // return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
+  }
+
+  /**
+   * Mint multiple RFT tokens with one owner
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param owner tokens owner
+   * @param tokens array of tokens with properties and pieces
+   * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);
+   * @returns array of newly created RFT tokens
+   */
+  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {
+    const rawTokens = [];
+    for(const token of tokens) {
+      const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};
+      rawTokens.push(raw);
+    }
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],
+      true,
+    );
+    const collection = this.getCollectionObject(collectionId);
+    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
+  }
+
+  /**
+   * Destroys a concrete instance of RFT.
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param amount number of pieces to be burnt
+   * @example burnToken(aliceKeyring, 10, 5);
+   * @returns ```true``` if the extrinsic is successful, otherwise ```false```
+   */
+  override async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {
+    return await super.burnToken(signer, collectionId, tokenId, amount);
+  }
+
+  /**
+   * Destroys a concrete instance of RFT on behalf of the owner.
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param fromAddressObj address on behalf of which the token will be burnt
+   * @param amount number of pieces to be burnt
+   * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  override async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
+    return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);
+  }
+
+  /**
+   * Set, change, or remove approved address to transfer the ownership of the RFT.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param toAddressObj address to approve
+   * @param amount number of pieces to be approved
+   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);
+   * @returns true if the token success, otherwise false
+   */
+  override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
+    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);
+  }
+
+  /**
+   * Get total number of pieces
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @example getTokenTotalPieces(10, 5);
+   * @returns number of pieces
+   */
+  async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {
+    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();
+  }
+
+  /**
+   * Change number of token pieces. Signer must be the owner of all token pieces.
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param tokenId ID of token
+   * @param amount new number of pieces
+   * @example repartitionToken(aliceKeyring, 10, 5, 12345n);
+   * @returns true if the repartion was success, otherwise false
+   */
+  async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {
+    const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);
+    const repartitionResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.repartition', [collectionId, tokenId, amount],
+      true,
+    );
+    if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');
+    return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');
+  }
+}
+
+
+class FTGroup extends CollectionGroup {
+  /**
+   * Get collection object
+   * @param collectionId ID of collection
+   * @example getCollectionObject(2);
+   * @returns instance of UniqueFTCollection
+   */
+  getCollectionObject(collectionId: number): UniqueFTCollection {
+    return new UniqueFTCollection(collectionId, this.helper);
+  }
+
+  /**
+   * Mint new fungible collection
+   * @param signer keyring of signer
+   * @param collectionOptions Collection options
+   * @param decimalPoints number of token decimals
+   * @example
+   * mintCollection(aliceKeyring, {
+   *   name: 'New',
+   *   description: 'New collection',
+   *   tokenPrefix: 'NEW',
+   * }, 18)
+   * @returns newly created fungible collection
+   */
+  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {
+    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object
+    if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');
+    collectionOptions.mode = {fungible: decimalPoints};
+    for(const key of ['name', 'description', 'tokenPrefix']) {
+      if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);
+    }
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createCollectionEx', [collectionOptions],
+      true,
+    );
+    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));
+  }
+
+  /**
+   * Mint tokens
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param owner address owner of new tokens
+   * @param amount amount of tokens to be meanted
+   * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {
+        Fungible: {
+          value: amount,
+        },
+      }],
+      true, // `Unable to mint fungible tokens for ${label}`,
+    );
+    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');
+  }
+
+  /**
+   * Mint multiple Fungible tokens with one owner
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param owner tokens owner
+   * @param tokens array of tokens with properties and pieces
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: { value: bigint }[], owner: ICrossAccountId): Promise<boolean> {
+    const rawTokens = [];
+    for(const token of tokens) {
+      const raw = {Fungible: {Value: token.value}};
+      rawTokens.push(raw);
+    }
+    const creationResult = await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],
+      true,
+    );
+    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');
+  }
+
+  /**
+   * Get the top 10 owners with the largest balance for the Fungible collection
+   * @param collectionId ID of collection
+   * @example getTop10Owners(10);
+   * @returns array of ```ICrossAccountId```
+   */
+  async getTop10Owners(collectionId: number): Promise<ICrossAccountId[]> {
+    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map((a: CrossAccountId) => a.toICrossAccountId());
+  }
+
+  /**
+   * Get account balance
+   * @param collectionId ID of collection
+   * @param addressObj address of owner
+   * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})
+   * @returns amount of fungible tokens owned by address
+   */
+  async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {
+    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();
+  }
+
+  /**
+   * Transfer tokens to address
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param toAddressObj address recipient
+   * @param amount amount of tokens to be sent
+   * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);
+  }
+
+  /**
+   * Transfer some tokens on behalf of the owner.
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param fromAddressObj address on behalf of which tokens will be sent
+   * @param toAddressObj address where token to be sent
+   * @param amount number of tokens to be sent
+   * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);
+  }
+
+  /**
+   * Destroy some amount of tokens
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param amount amount of tokens to be destroyed
+   * @example burnTokens(aliceKeyring, 10, 1000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async burnTokens(signer: IKeyringPair, collectionId: number, amount = 1n): Promise<boolean> {
+    return await super.burnToken(signer, collectionId, 0, amount);
+  }
+
+  /**
+   * Burn some tokens on behalf of the owner.
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param fromAddressObj address on behalf of which tokens will be burnt
+   * @param amount amount of tokens to be burnt
+   * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {
+    return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);
+  }
+
+  /**
+   * Get total collection supply
+   * @param collectionId
+   * @returns
+   */
+  async getTotalPieces(collectionId: number): Promise<bigint> {
+    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();
+  }
+
+  /**
+   * Set, change, or remove approved address to transfer tokens.
+   *
+   * @param signer keyring of signer
+   * @param collectionId ID of collection
+   * @param toAddressObj address to be approved
+   * @param amount amount of tokens to be approved
+   * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {
+    return super.approveToken(signer, collectionId, 0, toAddressObj, amount);
+  }
+
+  /**
+   * Get amount of fungible tokens approved to transfer
+   * @param collectionId ID of collection
+   * @param fromAddressObj owner of tokens
+   * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner
+   * @returns number of tokens approved for the transfer
+   */
+  getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
+    return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);
+  }
+}
+
+
+class ChainGroup extends HelperGroup<ChainHelperBase> {
+  /**
+   * Get system properties of a chain
+   * @example getChainProperties();
+   * @returns ss58Format, token decimals, and token symbol
+   */
+  getChainProperties(): IChainProperties {
+    const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();
+    return {
+      ss58Format: properties.ss58Format.toJSON(),
+      tokenDecimals: properties.tokenDecimals.toJSON(),
+      tokenSymbol: properties.tokenSymbol.toJSON(),
+    };
+  }
+
+  /**
+   * Get chain header
+   * @example getLatestBlockNumber();
+   * @returns the number of the last block
+   */
+  async getLatestBlockNumber(): Promise<number> {
+    return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();
+  }
+
+  /**
+   * Get block hash by block number
+   * @param blockNumber number of block
+   * @example getBlockHashByNumber(12345);
+   * @returns hash of a block
+   */
+  async getBlockHashByNumber(blockNumber: number): Promise<string | null> {
+    const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();
+    if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;
+    return blockHash;
+  }
+
+  // TODO add docs
+  async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {
+    const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);
+    if(!blockHash) return null;
+    return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;
+  }
+
+  /**
+   * Get latest relay block
+   * @returns {number} relay block
+   */
+  async getRelayBlockNumber(): Promise<bigint> {
+    const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;
+    return BigInt(blockNumber);
+  }
+
+  /**
+   * Get account nonce
+   * @param address substrate address
+   * @example getNonce("5GrwvaEF5zXb26Fz...");
+   * @returns number, account's nonce
+   */
+  async getNonce(address: TSubstrateAccount): Promise<number> {
+    return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();
+  }
+}
+
+export class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+  /**
+ * Get substrate address balance
+ * @param address substrate address
+ * @example getSubstrate("5GrwvaEF5zXb26Fz...")
+ * @returns amount of tokens on address
+ */
+  async getSubstrate(address: TSubstrateAccount): Promise<bigint> {
+    return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();
+  }
+
+  /**
+   * Transfer tokens to substrate address
+   * @param signer keyring of signer
+   * @param address substrate address of a recipient
+   * @param amount amount of tokens to be transfered
+   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);
+
+    let transfer = {from: null, to: null, amount: 0n} as any;
+    result.result.events.forEach(({event: {data, method, section}}: any) => {
+      if((section === 'balances') && (method === 'Transfer')) {
+        transfer = {
+          from: this.helper.address.normalizeSubstrate(data[0]),
+          to: this.helper.address.normalizeSubstrate(data[1]),
+          amount: BigInt(data[2]),
+        };
+      }
+    });
+    const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from
+      && this.helper.address.normalizeSubstrate(address) === transfer.to
+      && BigInt(amount) === transfer.amount;
+    return isSuccess;
+  }
+
+  /**
+   * Get full substrate balance including free, frozen, and reserved
+   * @param address substrate address
+   * @returns
+   */
+  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {
+    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;
+    return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};
+  }
+
+  /**
+   * Get total issuance
+   * @returns
+   */
+  async getTotalIssuance(): Promise<bigint> {
+    const total = (await this.helper.callRpc('api.query.balances.totalIssuance', []));
+    return total.toBigInt();
+  }
+
+  async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> {
+    const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();
+    return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons}));
+  }
+  async getFrozen(address: TSubstrateAccount): Promise<{ id: string, amount: bigint }[]> {
+    const locks = (await this.helper.api!.query.balances.freezes(address)) as unknown as Array<any>;
+    return locks.map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()}));
+  }
+}
+
+export class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+  /**
+   * Get ethereum address balance
+   * @param address ethereum address
+   * @example getEthereum("0x9F0583DbB855d...")
+   * @returns amount of tokens on address
+   */
+  async getEthereum(address: TEthereumAccount): Promise<bigint> {
+    return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();
+  }
+
+  /**
+   * Transfer tokens to address
+   * @param signer keyring of signer
+   * @param address Ethereum address of a recipient
+   * @param amount amount of tokens to be transfered
+   * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);
+
+    let transfer = {from: null, to: null, amount: 0n} as any;
+    result.result.events.forEach(({event: {data, method, section}}: any) => {
+      if((section === 'balances') && (method === 'Transfer')) {
+        transfer = {
+          from: data[0].toString(),
+          to: data[1].toString(),
+          amount: BigInt(data[2]),
+        };
+      }
+    });
+    const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from
+      && address === transfer.to
+      && BigInt(amount) === transfer.amount;
+    return isSuccess;
+  }
+}
+
+class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+  subBalanceGroup: SubstrateBalanceGroup<T>;
+  ethBalanceGroup: EthereumBalanceGroup<T>;
+
+  constructor(helper: T) {
+    super(helper);
+    this.subBalanceGroup = new SubstrateBalanceGroup(helper);
+    this.ethBalanceGroup = new EthereumBalanceGroup(helper);
+  }
+
+  getCollectionCreationPrice(): bigint {
+    return 2n * this.getOneTokenNominal();
+  }
+  /**
+   * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).
+   * @example getOneTokenNominal()
+   * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.
+   */
+  getOneTokenNominal(): bigint {
+    const chainProperties = this.helper.chain.getChainProperties();
+    return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);
+  }
+
+  /**
+   * Get substrate address balance
+   * @param address substrate address
+   * @example getSubstrate("5GrwvaEF5zXb26Fz...")
+   * @returns amount of tokens on address
+   */
+  getSubstrate(address: TSubstrateAccount): Promise<bigint> {
+    return this.subBalanceGroup.getSubstrate(address);
+  }
+
+  /**
+   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved
+   * @param address substrate address
+   * @returns
+   */
+  getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {
+    return this.subBalanceGroup.getSubstrateFull(address);
+  }
+
+  /**
+   * Get total issuance
+   * @returns
+   */
+  getTotalIssuance(): Promise<bigint> {
+    return this.subBalanceGroup.getTotalIssuance();
+  }
+
+  /**
+   * Get locked balances
+   * @param address substrate address
+   * @returns locked balances with reason via api.query.balances.locks
+   * @deprecated all the methods should switch to getFrozen
+   */
+  getLocked(address: TSubstrateAccount) {
+    return this.subBalanceGroup.getLocked(address);
+  }
+
+  /**
+   * Get frozen balances
+   * @param address substrate address
+   * @returns frozen balances with id via api.query.balances.freezes
+   */
+  getFrozen(address: TSubstrateAccount) {
+    return this.subBalanceGroup.getFrozen(address);
+  }
+
+  /**
+   * Get ethereum address balance
+   * @param address ethereum address
+   * @example getEthereum("0x9F0583DbB855d...")
+   * @returns amount of tokens on address
+   */
+  getEthereum(address: TEthereumAccount): Promise<bigint> {
+    return this.ethBalanceGroup.getEthereum(address);
+  }
+
+  async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint) {
+    await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceSetBalance', [address, amount], true);
+  }
+
+  /**
+   * Transfer tokens to substrate address
+   * @param signer keyring of signer
+   * @param address substrate address of a recipient
+   * @param amount amount of tokens to be transfered
+   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);
+   * @returns ```true``` if extrinsic success, otherwise ```false```
+   */
+  transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {
+    return this.subBalanceGroup.transferToSubstrate(signer, address, amount);
+  }
+
+  async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {
+    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);
+
+    let transfer = {from: null, to: null, amount: 0n} as any;
+    result.result.events.forEach(({event: {data, method, section}}: any) => {
+      if((section === 'balances') && (method === 'Transfer')) {
+        transfer = {
+          from: this.helper.address.normalizeSubstrate(data[0]),
+          to: this.helper.address.normalizeSubstrate(data[1]),
+          amount: BigInt(data[2]),
+        };
+      }
+    });
+    let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;
+    isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;
+    isSuccess = isSuccess && BigInt(amount) === transfer.amount;
+    return isSuccess;
+  }
+
+  /**
+   * Transfer tokens with the unlock period
+   * @param signer signers Keyring
+   * @param address Substrate address of recipient
+   * @param schedule Schedule params
+   * @example vestedTransfer(signer, recepient.address, 20000, 100, 10, 50 * nominal); // total amount of vested tokens will be 100 * 50 = 5000
+   */
+  async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: { start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }): Promise<void> {
+    const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]);
+    const event = result.result.events
+      .find((e: any) => e.event.section === 'vesting' &&
+        e.event.method === 'VestingScheduleAdded' &&
+        e.event.data[0].toHuman() === signer.address);
+    if(!event) throw Error('Cannot find transfer in events');
+  }
+
+  /**
+   * Get schedule for recepient of vested transfer
+   * @param address Substrate address of recipient
+   * @returns
+   */
+  async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> {
+    const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();
+    return schedule.map((schedule: any) => ({
+      start: BigInt(schedule.start),
+      period: BigInt(schedule.period),
+      periodCount: BigInt(schedule.periodCount),
+      perPeriod: BigInt(schedule.perPeriod),
+    }));
+  }
+
+  /**
+   * Claim vested tokens
+   * @param signer signers Keyring
+   */
+  async claim(signer: TSigner) {
+    const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);
+    const event = result.result.events
+      .find((e: any) => e.event.section === 'vesting' &&
+        e.event.method === 'Claimed' &&
+        e.event.data[0].toHuman() === signer.address);
+    if(!event) throw Error('Cannot find claim in events');
+  }
+}
+
+class AddressGroup extends HelperGroup<ChainHelperBase> {
+  /**
+   * Normalizes the address to the specified ss58 format, by default ```42```.
+   * @param address substrate address
+   * @param ss58Format format for address conversion, by default ```42```
+   * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
+   * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation
+   */
+  normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {
+    return CrossAccountId.normalizeSubstrateAddress({Substrate: address}, ss58Format);
+  }
+
+  /**
+   * Get address in the connected chain format
+   * @param address substrate address
+   * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network
+   * @returns address in chain format
+   */
+  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {
+    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);
+  }
+
+  /**
+   * Get substrate mirror of an ethereum address
+   * @param ethAddress ethereum address
+   * @param toChainFormat false for normalized account
+   * @example ethToSubstrate('0x9F0583DbB855d...')
+   * @returns substrate mirror of a provided ethereum address
+   */
+  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat = false): TSubstrateAccount {
+    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);
+  }
+
+  /**
+   * Get ethereum mirror of a substrate address
+   * @param subAddress substrate account
+   * @example substrateToEth("5DnSF6RRjwteE3BrC...")
+   * @returns ethereum mirror of a provided substrate address
+   */
+  substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {
+    return CrossAccountId.translateSubToEth(subAddress);
+  }
+
+  /**
+   * Encode key to substrate address
+   * @param key key for encoding address
+   * @param ss58Format prefix for encoding to the address of the corresponding network
+   * @returns encoded substrate address
+   */
+  encodeSubstrateAddress(key: Uint8Array | string | bigint, ss58Format = 42): string {
+    const u8a: Uint8Array = typeof key === 'string'
+      ? hexToU8a(key)
+      : typeof key === 'bigint'
+        ? hexToU8a(key.toString(16))
+        : key;
+
+    if(ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {
+      throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);
+    }
+
+    const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];
+    if(!allowedDecodedLengths.includes(u8a.length)) {
+      throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);
+    }
+
+    const u8aPrefix = ss58Format < 64
+      ? new Uint8Array([ss58Format])
+      : new Uint8Array([
+        ((ss58Format & 0xfc) >> 2) | 0x40,
+        (ss58Format >> 8) | ((ss58Format & 0x03) << 6),
+      ]);
+
+    const input = u8aConcat(u8aPrefix, u8a);
+
+    return base58Encode(u8aConcat(
+      input,
+      blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),
+    ));
+  }
+
+  /**
+   * Restore substrate address from bigint representation
+   * @param number decimal representation of substrate address
+   * @returns substrate address
+   */
+  restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {
+    if(this.helper.api === null) {
+      throw 'Not connected';
+    }
+    const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();
+    if(res === undefined || res === null) {
+      throw 'Restore address error';
+    }
+    return res.toString();
+  }
+
+  /**
+   * Convert etherium cross account id to substrate cross account id
+   * @param ethCrossAccount etherium cross account
+   * @returns substrate cross account id
+   */
+  convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {
+    if(ethCrossAccount.sub === '0') {
+      return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};
+    }
+
+    const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));
+    return {Substrate: ss58};
+  }
+
+  paraSiblingSovereignAccount(paraid: number) {
+    // We are getting a *sibling* parachain sovereign account,
+    // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c
+    const siblingPrefix = '0x7369626c';
+
+    const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);
+    const suffix = '000000000000000000000000000000000000000000000000';
+
+    return siblingPrefix + encodedParaId + suffix;
+  }
+}
+
+
+class StakingGroup extends HelperGroup<UniqueHelper> {
+  /**
+   * Stake tokens for App Promotion
+   * @param signer keyring of signer
+   * @param amountToStake amount of tokens to stake
+   * @param label extra label for log
+   * @returns
+   */
+  async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {
+    if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;
+    await this.helper.executeExtrinsic(
+      signer, 'api.tx.appPromotion.stake',
+      [amountToStake], true,
+    );
+    // TODO extract info from stakeResult
+    return true;
+  }
+
+  /**
+   * Unstake all staked tokens
+   * @param signer keyring of signer
+   * @param amountToUnstake amount of tokens to unstake
+   * @param label extra label for log
+   * @returns block hash where unstake happened
+   */
+  async unstakeAll(signer: TSigner, label?: string): Promise<string> {
+    if(typeof label === 'undefined') label = `${signer.address}`;
+    const unstakeResult = await this.helper.executeExtrinsic(
+      signer, 'api.tx.appPromotion.unstakeAll',
+      [], true,
+    );
+    return unstakeResult.blockHash;
+  }
+
+  /**
+   * Unstake the part of a staked tokens
+   * @param signer keyring of signer
+   * @param amount amount of tokens to unstake
+   * @param label extra label for log
+   * @returns block hash where unstake happened
+   */
+  async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise<string> {
+    if(typeof label === 'undefined') label = `${signer.address}`;
+    const unstakeResult = await this.helper.executeExtrinsic(
+      signer, 'api.tx.appPromotion.unstakePartial',
+      [amount], true,
+    );
+    return unstakeResult.blockHash;
+  }
+
+  /**
+   * Get total number of active stakes
+   * @param address substrate address
+   * @returns {number}
+   */
+  async getStakesNumber(address: ICrossAccountId): Promise<number> {
+    if('Ethereum' in address) throw Error('only substrate address');
+    return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber();
+  }
+
+  /**
+   * Get total staked amount for address
+   * @param address substrate or ethereum address
+   * @returns total staked amount
+   */
+  async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {
+    if(address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();
+    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();
+  }
+
+  /**
+   * Get total staked per block
+   * @param address substrate or ethereum address
+   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block
+   */
+  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
+    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);
+    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({
+      block: block.toBigInt(),
+      amount: amount.toBigInt(),
+    }));
+  }
+
+  /**
+   * Get total pending unstake amount for address
+   * @param address substrate or ethereum address
+   * @returns total pending unstake amount
+   */
+  async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {
+    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();
+  }
+
+  /**
+   * Get pending unstake amount per block for address
+   * @param address substrate or ethereum address
+   * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block
+   */
+  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {
+    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);
+    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({
+      block: block.toBigInt(),
+      amount: amount.toBigInt(),
+    }));
+    return result;
+  }
+}
+
+
+class PreimageGroup extends HelperGroup<UniqueHelper> {
+  async getPreimageInfo(h256: string) {
+    return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON();
+  }
+
+  /**
+   * Create a preimage from an API call.
+   * @param signer keyring of the signer.
+   * @param call an extrinsic call
+   * @example await notePreimageFromCall(preimageMaker,
+   *   helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd])
+   * );
+   * @returns promise of extrinsic execution.
+   */
+  notePreimageFromCall(signer: TSigner, call: any, returnPreimageHash = false) {
+    return this.notePreimage(signer, call.method.toHex(), returnPreimageHash);
+  }
+
+  /**
+   * Create a preimage with a hex or a byte array.
+   * @param signer keyring of the signer.
+   * @param bytes preimage encoded in hex or a byte array, e.g. an extrinsic call.
+   * @example await notePreimage(preimageMaker,
+   *   helper.constructApiCall('api.tx.identity.forceInsertIdentities', [identitiesToAdd]).method.toHex()
+   * );
+   * @returns promise of extrinsic execution.
+   */
+  async notePreimage(signer: TSigner, bytes: string | Uint8Array, returnPreimageHash = false) {
+    const promise = this.helper.executeExtrinsic(signer, 'api.tx.preimage.notePreimage', [bytes]);
+    if(returnPreimageHash) {
+      const result = await promise;
+      const events = result.result.events.filter((x: any) => x.event.method === 'Noted' && x.event.section === 'preimage');
+      const preimageHash = events[0].event.data[0].toHuman();
+      return preimageHash;
+    }
+    return promise;
+  }
+
+  /**
+   * Delete an existing preimage and return the deposit.
+   * @param signer keyring of the signer - either the owner or the preimage manager (sudo).
+   * @param h256 hash of the preimage.
+   * @returns promise of extrinsic execution.
+   */
+  unnotePreimage(signer: TSigner, h256: string) {
+    return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unnotePreimage', [h256]);
+  }
+
+  /**
+   * Request a preimage be uploaded to the chain without paying any fees or deposits.
+   * @param signer keyring of the signer - either the owner or the preimage manager (sudo).
+   * @param h256 hash of the preimage.
+   * @returns promise of extrinsic execution.
+   */
+  requestPreimage(signer: TSigner, h256: string) {
+    return this.helper.executeExtrinsic(signer, 'api.tx.preimage.requestPreimage', [h256]);
+  }
+
+  /**
+   * Clear a previously made request for a preimage.
+   * @param signer keyring of the signer - either the owner or the preimage manager (sudo).
+   * @param h256 hash of the preimage.
+   * @returns promise of extrinsic execution.
+   */
+  unrequestPreimage(signer: TSigner, h256: string) {
+    return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]);
+  }
+}
+
+class UtilityGroup<T extends ChainHelperBase> extends HelperGroup<T> {
+  async batch(signer: TSigner, txs: any[]) {
+    return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batch', [txs]);
+  }
+
+  async batchAll(signer: TSigner, txs: any[]) {
+    return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batchAll', [txs]);
+  }
+
+  batchAllCall(txs: any[]) {
+    return this.helper.constructApiCall('api.tx.utility.batchAll', [txs]);
+  }
+}
+
+export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase;
+export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper;
+
+export class UniqueHelper extends ChainHelperBase {
+  balance: BalanceGroup<UniqueHelper>;
+  collection: CollectionGroup;
+  nft: NFTGroup;
+  rft: RFTGroup;
+  ft: FTGroup;
+  staking: StakingGroup;
+  preimage: PreimageGroup;
+  utility: UtilityGroup<UniqueHelper>;
+
+  constructor(logger?: ILogger, options: { [key: string]: any } = {}) {
+    super(logger, options.helperBase ?? UniqueHelper);
+
+    this.balance = new BalanceGroup(this);
+    this.collection = new CollectionGroup(this);
+    this.nft = new NFTGroup(this);
+    this.rft = new RFTGroup(this);
+    this.ft = new FTGroup(this);
+    this.staking = new StakingGroup(this);
+    this.preimage = new PreimageGroup(this);
+    this.utility = new UtilityGroup(this);
+  }
+}
+
+export class UniqueBaseCollection {
+  helper: UniqueHelper;
+  collectionId: number;
+
+  constructor(collectionId: number, uniqueHelper: UniqueHelper) {
+    this.collectionId = collectionId;
+    this.helper = uniqueHelper;
+  }
+
+  async getData() {
+    return await this.helper.collection.getData(this.collectionId);
+  }
+
+  async getLastTokenId() {
+    return await this.helper.collection.getLastTokenId(this.collectionId);
+  }
+
+  async doesTokenExist(tokenId: number) {
+    return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);
+  }
+
+  async getAdmins() {
+    return await this.helper.collection.getAdmins(this.collectionId);
+  }
+
+  async getAllowList() {
+    return await this.helper.collection.getAllowList(this.collectionId);
+  }
+
+  async getEffectiveLimits() {
+    return await this.helper.collection.getEffectiveLimits(this.collectionId);
+  }
+
+  async getProperties(propertyKeys?: string[] | null) {
+    return await this.helper.collection.getProperties(this.collectionId, propertyKeys);
+  }
+
+  async getPropertiesConsumedSpace() {
+    return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);
+  }
+
+  async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {
+    return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);
+  }
+
+  async getOptions() {
+    return await this.helper.collection.getCollectionOptions(this.collectionId);
+  }
+
+  async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {
+    return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);
+  }
+
+  async confirmSponsorship(signer: TSigner) {
+    return await this.helper.collection.confirmSponsorship(signer, this.collectionId);
+  }
+
+  async removeSponsor(signer: TSigner) {
+    return await this.helper.collection.removeSponsor(signer, this.collectionId);
+  }
+
+  async setLimits(signer: TSigner, limits: ICollectionLimits) {
+    return await this.helper.collection.setLimits(signer, this.collectionId, limits);
+  }
+
+  async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {
+    return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);
+  }
+
+  async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {
+    return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);
+  }
+
+  async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {
+    return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);
+  }
+
+  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {
+    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);
+  }
+
+  async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {
+    return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);
+  }
+
+  async setProperties(signer: TSigner, properties: IProperty[]) {
+    return await this.helper.collection.setProperties(signer, this.collectionId, properties);
+  }
+
+  async deleteProperties(signer: TSigner, propertyKeys: string[]) {
+    return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);
+  }
+
+  async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {
+    return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);
+  }
+
+  async enableNesting(signer: TSigner, permissions: INestingPermissions) {
+    return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);
+  }
+
+  async disableNesting(signer: TSigner) {
+    return await this.helper.collection.disableNesting(signer, this.collectionId);
+  }
+
+  async burn(signer: TSigner) {
+    return await this.helper.collection.burn(signer, this.collectionId);
+  }
+}
+
+export class UniqueNFTCollection extends UniqueBaseCollection {
+  getTokenObject(tokenId: number) {
+    return new UniqueNFToken(tokenId, this);
+  }
+
+  async getTokensByAddress(addressObj: ICrossAccountId) {
+    return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);
+  }
+
+  async getToken(tokenId: number, blockHashAt?: string) {
+    return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);
+  }
+
+  async getTokenOwner(tokenId: number, blockHashAt?: string) {
+    return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);
+  }
+
+  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {
+    return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);
+  }
+
+  async getTokenChildren(tokenId: number, blockHashAt?: string) {
+    return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);
+  }
+
+  async getPropertyPermissions(propertyKeys: string[] | null = null) {
+    return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);
+  }
+
+  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {
+    return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
+  }
+
+  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {
+    const api = this.helper.getApi();
+    const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any;
+
+    return props?.consumedSpace ?? 0;
+  }
+
+  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {
+    return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);
+  }
+
+  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
+    return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);
+  }
+
+  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {
+    return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);
+  }
+
+  async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {
+    return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);
+  }
+
+  async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {
+    return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});
+  }
+
+  async mintMultipleTokens(signer: TSigner, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]) {
+    return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);
+  }
+
+  async burnToken(signer: TSigner, tokenId: number) {
+    return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);
+  }
+
+  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {
+    return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);
+  }
+
+  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
+    return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);
+  }
+
+  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {
+    return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);
+  }
+
+  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {
+    return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);
+  }
+
+  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {
+    return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);
+  }
+
+  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
+    return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
+  }
+}
+
+export class UniqueRFTCollection extends UniqueBaseCollection {
+  getTokenObject(tokenId: number) {
+    return new UniqueRFToken(tokenId, this);
+  }
+
+  async getToken(tokenId: number, blockHashAt?: string) {
+    return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);
+  }
+
+  async getTokenOwner(tokenId: number, blockHashAt?: string) {
+    return await this.helper.rft.getTokenOwner(this.collectionId, tokenId, blockHashAt);
+  }
+
+  async getTokensByAddress(addressObj: ICrossAccountId) {
+    return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);
+  }
+
+  async getTop10TokenOwners(tokenId: number) {
+    return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);
+  }
+
+  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {
+    return await this.helper.rft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);
+  }
+
+  async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {
+    return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);
+  }
+
+  async getTokenTotalPieces(tokenId: number) {
+    return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);
+  }
+
+  async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
+    return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);
+  }
+
+  async getPropertyPermissions(propertyKeys: string[] | null = null) {
+    return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);
+  }
+
+  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {
+    return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);
+  }
+
+  async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {
+    const api = this.helper.getApi();
+    const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any;
+
+    return props?.consumedSpace ?? 0;
+  }
+
+  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);
+  }
+
+  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);
+  }
+
+  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);
+  }
+
+  async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {
+    return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);
+  }
+
+  async mintToken(signer: TSigner, pieces = 1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {
+    return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});
+  }
+
+  async mintMultipleTokens(signer: TSigner, tokens: { pieces: bigint, owner: ICrossAccountId, properties?: IProperty[] }[]) {
+    return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);
+  }
+
+  async burnToken(signer: TSigner, tokenId: number, amount = 1n) {
+    return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);
+  }
+
+  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);
+  }
+
+  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {
+    return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);
+  }
+
+  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {
+    return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);
+  }
+
+  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {
+    return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);
+  }
+
+  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {
+    return await this.helper.rft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);
+  }
+
+  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
+    return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);
+  }
+}
+
+export class UniqueFTCollection extends UniqueBaseCollection {
+  async getBalance(addressObj: ICrossAccountId) {
+    return await this.helper.ft.getBalance(this.collectionId, addressObj);
+  }
+
+  async getTotalPieces() {
+    return await this.helper.ft.getTotalPieces(this.collectionId);
+  }
+
+  async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
+    return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);
+  }
+
+  async getTop10Owners() {
+    return await this.helper.ft.getTop10Owners(this.collectionId);
+  }
+
+  async mint(signer: TSigner, amount = 1n, owner: ICrossAccountId = {Substrate: signer.address}) {
+    return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);
+  }
+
+  async mintWithOneOwner(signer: TSigner, tokens: { value: bigint }[], owner: ICrossAccountId = {Substrate: signer.address}) {
+    return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);
+  }
+
+  async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);
+  }
+
+  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);
+  }
+
+  async burnTokens(signer: TSigner, amount = 1n) {
+    return await this.helper.ft.burnTokens(signer, this.collectionId, amount);
+  }
+
+  async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);
+  }
+
+  async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);
+  }
+}
+
+export class UniqueBaseToken {
+  collection: UniqueNFTCollection | UniqueRFTCollection;
+  collectionId: number;
+  tokenId: number;
+
+  constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {
+    this.collection = collection;
+    this.collectionId = collection.collectionId;
+    this.tokenId = tokenId;
+  }
+
+  async getNextSponsored(addressObj: ICrossAccountId) {
+    return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);
+  }
+
+  async getProperties(propertyKeys?: string[] | null) {
+    return await this.collection.getTokenProperties(this.tokenId, propertyKeys);
+  }
+
+  async getTokenPropertiesConsumedSpace() {
+    return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);
+  }
+
+  async setProperties(signer: TSigner, properties: IProperty[]) {
+    return await this.collection.setTokenProperties(signer, this.tokenId, properties);
+  }
+
+  async deleteProperties(signer: TSigner, propertyKeys: string[]) {
+    return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);
+  }
+
+  async doesExist() {
+    return await this.collection.doesTokenExist(this.tokenId);
+  }
+
+  nestingAccount(): ICrossAccountId {
+    return this.collection.helper.util.getTokenAccount(this);
+  }
+}
+
+export class UniqueNFToken extends UniqueBaseToken {
+  declare collection: UniqueNFTCollection;
+
+  constructor(tokenId: number, collection: UniqueNFTCollection) {
+    super(tokenId, collection);
+    this.collection = collection;
+  }
+
+  async getData(blockHashAt?: string) {
+    return await this.collection.getToken(this.tokenId, blockHashAt);
+  }
+
+  async getOwner(blockHashAt?: string) {
+    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);
+  }
+
+  async getTopmostOwner(blockHashAt?: string) {
+    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);
+  }
+
+  async getChildren(blockHashAt?: string) {
+    return await this.collection.getTokenChildren(this.tokenId, blockHashAt);
+  }
+
+  async nest(signer: TSigner, toTokenObj: IToken) {
+    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);
+  }
+
+  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
+    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);
+  }
+
+  async transfer(signer: TSigner, addressObj: ICrossAccountId) {
+    return await this.collection.transferToken(signer, this.tokenId, addressObj);
+  }
+
+  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {
+    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);
+  }
+
+  async approve(signer: TSigner, toAddressObj: ICrossAccountId) {
+    return await this.collection.approveToken(signer, this.tokenId, toAddressObj);
+  }
+
+  async isApproved(toAddressObj: ICrossAccountId) {
+    return await this.collection.isTokenApproved(this.tokenId, toAddressObj);
+  }
+
+  async burn(signer: TSigner) {
+    return await this.collection.burnToken(signer, this.tokenId);
+  }
+
+  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {
+    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);
+  }
+}
+
+export class UniqueRFToken extends UniqueBaseToken {
+  declare collection: UniqueRFTCollection;
+
+  constructor(tokenId: number, collection: UniqueRFTCollection) {
+    super(tokenId, collection);
+    this.collection = collection;
+  }
+
+  async getData(blockHashAt?: string) {
+    return await this.collection.getToken(this.tokenId, blockHashAt);
+  }
+
+  async getOwner(blockHashAt?: string) {
+    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);
+  }
+
+  async getTop10Owners() {
+    return await this.collection.getTop10TokenOwners(this.tokenId);
+  }
+
+  async getTopmostOwner(blockHashAt?: string) {
+    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);
+  }
+
+  async nest(signer: TSigner, toTokenObj: IToken) {
+    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);
+  }
+
+  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {
+    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);
+  }
+
+  async getBalance(addressObj: ICrossAccountId) {
+    return await this.collection.getTokenBalance(this.tokenId, addressObj);
+  }
+
+  async getTotalPieces() {
+    return await this.collection.getTokenTotalPieces(this.tokenId);
+  }
+
+  async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {
+    return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);
+  }
+
+  async transfer(signer: TSigner, addressObj: ICrossAccountId, amount = 1n) {
+    return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);
+  }
+
+  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);
+  }
+
+  async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);
+  }
+
+  async repartition(signer: TSigner, amount: bigint) {
+    return await this.collection.repartitionToken(signer, this.tokenId, amount);
+  }
+
+  async burn(signer: TSigner, amount = 1n) {
+    return await this.collection.burnToken(signer, this.tokenId, amount);
+  }
+
+  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {
+    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);
+  }
+}
addedjs-packages/playgrounds/unique.xcm.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/playgrounds/unique.xcm.ts
@@ -0,0 +1,375 @@
+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, IForeignAssetMetadata, 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 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 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, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {
+    await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.foreignAssets.registerForeignAsset',
+      [ownerAddress, location, metadata],
+      true,
+    );
+  }
+
+  async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {
+    await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.foreignAssets.updateForeignAsset',
+      [foreignAssetId, location, metadata],
+      true,
+    );
+  }
+}
+
+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 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);
+  }
+}
+
addedjs-packages/scripts/benchmarks/mintFee/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/mintFee/index.ts
@@ -0,0 +1,399 @@
+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 {IKeyringPair} from '@polkadot/types/types';
+import {UniqueNFTCollection} from '@unique/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 type {ContractImports} from '@unique/tests/eth/util/playgrounds/types.js';
+
+const {dirname} = makeNames(import.meta.url);
+
+export const CONTRACT_IMPORT: ContractImports[] = [
+  {
+    fsPath: `${dirname}/../../../../tests/eth/api/CollectionHelpers.sol`,
+    solPath: 'eth/api/CollectionHelpers.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../tests/eth/api/ContractHelpers.sol`,
+    solPath: 'eth/api/ContractHelpers.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../tests/eth/api/UniqueRefungibleToken.sol`,
+    solPath: 'eth/api/UniqueRefungibleToken.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../tests/eth/api/UniqueRefungible.sol`,
+    solPath: 'eth/api/UniqueRefungible.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../tests/eth/api/UniqueNFT.sol`,
+    solPath: 'eth/api/UniqueNFT.sol',
+  },
+];
+
+interface IBenchmarkResultForProp {
+	propertiesNumber: number;
+	substrateFee: number;
+	ethFee: number;
+	ethBulkFee: number;
+  ethMintCrossFee: number;
+	evmProxyContractFee: number;
+	evmProxyContractBulkFee: number;
+}
+
+const main = async () => {
+  const benchmarks = [
+    'substrateFee',
+    'ethFee',
+    'ethBulkFee',
+    'ethMintCrossFee',
+    'evmProxyContractFee',
+    'evmProxyContractBulkFee',
+  ];
+  const headers = [
+    'propertiesNumber',
+    ...benchmarks,
+  ];
+
+
+  const csvWriter = createObjectCsvWriter({
+    path: 'properties.csv',
+    header: headers,
+  });
+
+  await usingEthPlaygrounds(async (helper, privateKey) => {
+    const CONTRACT_SOURCE = (
+      await readFile(`${dirname}/proxyContract.sol`)
+    ).toString();
+
+    const donor = await privateKey('//Alice'); // Seed from account with balance on this network
+    const ethSigner = await helper.eth.createAccountWithBalance(donor);
+
+    const contract = await helper.ethContract.deployByCode(
+      ethSigner,
+      'ProxyMint',
+      CONTRACT_SOURCE,
+      CONTRACT_IMPORT,
+    );
+
+    const fees = await benchMintFee(helper, privateKey, contract);
+    console.log('Minting without properties');
+    console.table(fees);
+
+    const result: IBenchmarkResultForProp[] = [];
+    const csvResult: IBenchmarkResultForProp[] = [];
+
+    for(let i = 1; i <= 20; i++) {
+      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {
+        propertiesNumber: i,
+      }) as any;
+
+      csvResult.push(benchResult);
+
+      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));
+      for(const key of benchmarks) {
+        const keyPercent = Math.round((benchResult[key] / minFee) * 100);
+        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;
+      }
+
+      result.push(benchResult);
+    }
+
+    await csvWriter.writeRecords(csvResult);
+
+    console.log('Minting with properties');
+    console.table(result, headers);
+  });
+};
+
+main()
+  .then(() => process.exit(0))
+  .catch((e) => {
+    console.log(e);
+    process.exit(1);
+  });
+
+
+
+async function benchMintFee(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  proxyContract: Contract,
+): Promise<{
+	substrateFee: number;
+	ethFee: number;
+	evmProxyContractFee: number;
+}> {
+  const donor = await privateKey('//Alice');
+  const substrateReceiver = await privateKey('//Bob');
+  const ethSigner = await helper.eth.createAccountWithBalance(donor);
+
+  const nominal = helper.balance.getOneTokenNominal();
+
+  await helper.eth.transferBalanceFromSubstrate(
+    donor,
+    proxyContract.options.address,
+    100n,
+  );
+
+  const collection = (await createCollectionForBenchmarks(
+    'nft',
+    helper,
+    privateKey,
+    ethSigner,
+    proxyContract.options.address,
+    PERMISSIONS,
+  )) as UniqueNFTCollection;
+
+  const substrateFee = await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),
+  );
+
+  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+  const collectionContract = await helper.ethNativeContract.collection(
+    collectionEthAddress,
+    'nft',
+  );
+
+  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);
+
+  const encodedCall = collectionContract.methods
+    .mint(receiverEthAddress)
+    .encodeABI();
+
+  const ethFee = await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    async () => {
+      await helper.eth.sendEVM(
+        donor,
+        collectionContract.options.address,
+        encodedCall,
+        '0',
+      );
+    },
+  );
+
+  const evmProxyContractFee = await helper.arrange.calculcateFee(
+    {Ethereum: ethSigner},
+    async () => {
+      await proxyContract.methods
+        .mintToSubstrate(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          substrateReceiver.addressRaw,
+        )
+        .send({from: ethSigner});
+    },
+  );
+
+  return {
+    substrateFee: convertToTokens(substrateFee, nominal),
+    ethFee: convertToTokens(ethFee, nominal),
+    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),
+  };
+}
+
+async function benchMintWithProperties(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  proxyContract: Contract,
+  setup: { propertiesNumber: number },
+): Promise<IBenchmarkResultForProp> {
+  const donor = await privateKey('//Alice'); // Seed from account with balance on this network
+  const ethSigner = await helper.eth.createAccountWithBalance(donor);
+
+  const susbstrateReceiver = await privateKey('//Bob');
+  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);
+
+  const nominal = helper.balance.getOneTokenNominal();
+
+  const substrateFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await collection.mintToken(
+        donor,
+        {Substrate: susbstrateReceiver.address},
+        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => ({key: p.key, value: Buffer.from(p.value).toString()})),
+      );
+    },
+  );
+
+  const ethFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = await helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+        undefined,
+        true,
+      );
+
+      const subTokenId = await evmContract.methods.nextTokenId().call();
+
+      let encodedCall = evmContract.methods
+        .mint(receiverEthAddress)
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+
+      for(const val of PROPERTIES.slice(0, setup.propertiesNumber)) {
+        encodedCall = await evmContract.methods
+          .setProperty(subTokenId, val.key, Buffer.from(val.value))
+          .encodeABI();
+
+        await helper.eth.sendEVM(
+          donor,
+          evmContract.options.address,
+          encodedCall,
+          '0',
+        );
+      }
+    },
+  );
+
+  const ethBulkFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Substrate: donor.address},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = await helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+      );
+
+      const subTokenId = await evmContract.methods.nextTokenId().call();
+
+      let encodedCall = evmContract.methods
+        .mint(receiverEthAddress)
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+
+      encodedCall = await evmContract.methods
+        .setProperties(
+          subTokenId,
+          PROPERTIES.slice(0, setup.propertiesNumber),
+        )
+        .encodeABI();
+
+      await helper.eth.sendEVM(
+        donor,
+        evmContract.options.address,
+        encodedCall,
+        '0',
+      );
+    },
+  );
+
+  const ethMintCrossFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      const evmContract = await helper.ethNativeContract.collection(
+        helper.ethAddress.fromCollectionId(collection.collectionId),
+        'nft',
+      );
+
+      await evmContract.methods.mintCross(
+        helper.ethCrossAccount.fromAddress(receiverEthAddress),
+        PROPERTIES.slice(0, setup.propertiesNumber),
+      )
+        .send({from: ethSigner});
+    },
+  );
+
+  const proxyContractFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await proxyContract.methods
+        .mintToSubstrateWithProperty(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          susbstrateReceiver.addressRaw,
+          PROPERTIES.slice(0, setup.propertiesNumber),
+        )
+        .send({from: ethSigner});
+    },
+  );
+
+  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(
+    helper,
+    privateKey,
+    {Ethereum: ethSigner},
+    ethSigner,
+    proxyContract.options.address,
+    async (collection) => {
+      await proxyContract.methods
+        .mintToSubstrateBulkProperty(
+          helper.ethAddress.fromCollectionId(collection.collectionId),
+          susbstrateReceiver.addressRaw,
+          PROPERTIES.slice(0, setup.propertiesNumber),
+        )
+        .send({from: ethSigner});
+    },
+  );
+
+  return {
+    propertiesNumber: setup.propertiesNumber,
+    substrateFee: convertToTokens(substrateFee, nominal),
+    ethFee: convertToTokens(ethFee, nominal),
+    ethBulkFee: convertToTokens(ethBulkFee, nominal),
+    ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),
+    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),
+    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),
+  };
+}
+
+async function calculateFeeNftMintWithProperties(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  payer: ICrossAccountId,
+  ethSigner: string,
+  proxyContractAddress: string,
+  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,
+): Promise<bigint> {
+  const collection = (await createCollectionForBenchmarks(
+    'nft',
+    helper,
+    privateKey,
+    ethSigner,
+    proxyContractAddress,
+    PERMISSIONS,
+  )) as UniqueNFTCollection;
+  return helper.arrange.calculcateFee(payer, async () => {
+    await calculatedCall(collection);
+  });
+}
+
+
+
addedjs-packages/scripts/benchmarks/mintFee/proxyContract.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/mintFee/proxyContract.sol
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier:  Apache License
+pragma solidity >=0.8.0;
+import {CollectionHelpers} from "eth/api/CollectionHelpers.sol";
+import {ContractHelpers} from "eth/api/ContractHelpers.sol";
+import {UniqueRefungibleToken} from "eth/api/UniqueRefungibleToken.sol";
+import {UniqueRefungible, Collection, CrossAddress as RftCrossAccountId, Property as RftProperty} from "eth/api/UniqueRefungible.sol";
+import {UniqueNFT, CrossAddress as NftCrossAccountId, Property as NftProperty} from "eth/api/UniqueNFT.sol";
+
+struct Property {
+	string key;
+	bytes value;
+}
+
+interface SoftDeprecatedMethods {
+	/// @notice Set token property value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @param value Property value.
+	/// @dev EVM selector for this function is: 0x1752d67b,
+	///  or in textual repr: setProperty(uint256,string,bytes)
+	function setProperty(
+		uint256 tokenId,
+		string memory key,
+		bytes memory value
+	) external;
+}
+
+interface BenchUniqueRefungible is UniqueRefungible, SoftDeprecatedMethods {}
+interface BenchUniqueNFT is UniqueNFT, SoftDeprecatedMethods {}
+
+
+
+contract ProxyMint {
+	bytes32 constant REFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("ReFungible"));
+	bytes32 constant NONFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("NFT"));
+
+	modifier checkRestrictions(address _collection) {
+		Collection commonContract = Collection(_collection);
+		require(
+			commonContract.isOwnerOrAdminCross(RftCrossAccountId(msg.sender, 0)),
+			"Only collection admin/owner can call this method"
+		);
+		_;
+	}
+
+	/// @dev This emits when a mint to a substrate address has been made.
+	event MintToSub(address _toEth, uint256 _toSub, address _collection, uint256 _tokenId);
+
+	function mintToSubstrate(address _collection, uint256 _substrateReceiver) external checkRestrictions(_collection) {
+		Collection commonContract = Collection(_collection);
+		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
+		uint256 tokenId;
+
+		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
+			UniqueRefungible rftCollection = UniqueRefungible(_collection);
+
+			tokenId = rftCollection.mint(address(this));
+
+			rftCollection.transferFromCross(
+				RftCrossAccountId(address(this), 0),
+				RftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
+			UniqueNFT nftCollection = UniqueNFT(_collection);
+			tokenId = nftCollection.mint(address(this));
+
+			nftCollection.transferFromCross(
+				NftCrossAccountId(address(this), 0),
+				NftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else {
+			revert("Wrong collection type. Works only with NFT or RFT");
+		}
+
+		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
+	}
+
+	function mintToSubstrateWithProperty(
+		address _collection,
+		uint256 _substrateReceiver,
+		Property[] calldata _properties
+	) external checkRestrictions(_collection) {
+		uint256 propertiesLength = _properties.length;
+		require(propertiesLength > 0, "Properies is empty");
+
+		Collection commonContract = Collection(_collection);
+		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
+		uint256 tokenId;
+
+		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
+			BenchUniqueRefungible rftCollection = BenchUniqueRefungible(_collection);
+			tokenId = rftCollection.nextTokenId();
+			rftCollection.mint(address(this));
+
+			for (uint256 i = 0; i < propertiesLength; ++i) {
+				rftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value);
+			}
+			rftCollection.transferFromCross(
+				RftCrossAccountId(address(this), 0),
+				RftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
+			BenchUniqueNFT nftCollection = BenchUniqueNFT(_collection);
+			tokenId = nftCollection.mint(address(this));
+			for (uint256 i = 0; i < propertiesLength; ++i) {
+				nftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value);
+			}
+			nftCollection.transferFromCross(
+				NftCrossAccountId(address(this), 0),
+				NftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else {
+			revert("Wrong collection type. Works only with NFT or RFT");
+		}
+
+		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
+	}
+
+	function mintToSubstrateBulkProperty(
+		address _collection,
+		uint256 _substrateReceiver,
+		NftProperty[] calldata _properties
+	) external checkRestrictions(_collection) {
+		uint256 propertiesLength = _properties.length;
+		require(propertiesLength > 0, "Properies is empty");
+
+		Collection commonContract = Collection(_collection);
+		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
+		uint256 tokenId;
+
+		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
+			UniqueNFT nftCollection = UniqueNFT(_collection);
+			tokenId = nftCollection.mint(address(this));
+
+			nftCollection.setProperties(tokenId, _properties);
+
+			nftCollection.transferFromCross(
+				NftCrossAccountId(address(this), 0),
+				NftCrossAccountId(address(0), _substrateReceiver),
+				tokenId
+			);
+		} else {
+			revert("Wrong collection type. Works only with NFT or RFT");
+		}
+
+		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
+	}
+}
addedjs-packages/scripts/benchmarks/nesting/ABIGEN/RMRKNestableMintable.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/nesting/ABIGEN/RMRKNestableMintable.ts
@@ -0,0 +1,354 @@
+/* Autogenerated file. Do not edit manually. */
+/* tslint:disable */
+/* eslint-disable */
+
+import type BN from "bn.js";
+import type { ContractOptions } from "web3-eth-contract";
+import type { EventLog } from "web3-core";
+import type { EventEmitter } from "events";
+import type {
+  Callback,
+  NonPayableTransactionObject,
+  BlockType,
+  ContractEventLog,
+  BaseContract,
+} from "./types.js";
+
+export interface EventOptions {
+  filter?: object;
+  fromBlock?: BlockType;
+  topics?: string[];
+}
+
+export type AllChildrenRejected = ContractEventLog<{
+  tokenId: string;
+  0: string;
+}>;
+export type Approval = ContractEventLog<{
+  owner: string;
+  approved: string;
+  tokenId: string;
+  0: string;
+  1: string;
+  2: string;
+}>;
+export type ApprovalForAll = ContractEventLog<{
+  owner: string;
+  operator: string;
+  approved: boolean;
+  0: string;
+  1: string;
+  2: boolean;
+}>;
+export type ChildAccepted = ContractEventLog<{
+  tokenId: string;
+  childIndex: string;
+  childAddress: string;
+  childId: string;
+  0: string;
+  1: string;
+  2: string;
+  3: string;
+}>;
+export type ChildProposed = ContractEventLog<{
+  tokenId: string;
+  childIndex: string;
+  childAddress: string;
+  childId: string;
+  0: string;
+  1: string;
+  2: string;
+  3: string;
+}>;
+export type ChildTransferred = ContractEventLog<{
+  tokenId: string;
+  childIndex: string;
+  childAddress: string;
+  childId: string;
+  fromPending: boolean;
+  toZero: boolean;
+  0: string;
+  1: string;
+  2: string;
+  3: string;
+  4: boolean;
+  5: boolean;
+}>;
+export type NestTransfer = ContractEventLog<{
+  from: string;
+  to: string;
+  fromTokenId: string;
+  toTokenId: string;
+  tokenId: string;
+  0: string;
+  1: string;
+  2: string;
+  3: string;
+  4: string;
+}>;
+export type Transfer = ContractEventLog<{
+  from: string;
+  to: string;
+  tokenId: string;
+  0: string;
+  1: string;
+  2: string;
+}>;
+
+export interface RMRKNestableMintable extends BaseContract {
+  constructor(
+    jsonInterface: any[],
+    address?: string,
+    options?: ContractOptions
+  ): RMRKNestableMintable;
+  clone(): RMRKNestableMintable;
+  methods: {
+    RMRK_INTERFACE(): NonPayableTransactionObject<string>;
+
+    VERSION(): NonPayableTransactionObject<string>;
+
+    acceptChild(
+      parentId: number | string | BN,
+      childIndex: number | string | BN,
+      childAddress: string,
+      childId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    addChild(
+      parentId: number | string | BN,
+      childId: number | string | BN,
+      data: string | number[]
+    ): NonPayableTransactionObject<void>;
+
+    approve(
+      to: string,
+      tokenId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    balanceOf(owner: string): NonPayableTransactionObject<string>;
+
+    "burn(uint256)"(
+      tokenId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    "burn(uint256,uint256)"(
+      tokenId: number | string | BN,
+      maxChildrenBurns: number | string | BN
+    ): NonPayableTransactionObject<string>;
+
+    childOf(
+      parentId: number | string | BN,
+      index: number | string | BN
+    ): NonPayableTransactionObject<[string, string]>;
+
+    childrenOf(
+      parentId: number | string | BN
+    ): NonPayableTransactionObject<[string, string][]>;
+
+    directOwnerOf(tokenId: number | string | BN): NonPayableTransactionObject<{
+      0: string;
+      1: string;
+      2: boolean;
+    }>;
+
+    getApproved(
+      tokenId: number | string | BN
+    ): NonPayableTransactionObject<string>;
+
+    isApprovedForAll(
+      owner: string,
+      operator: string
+    ): NonPayableTransactionObject<boolean>;
+
+    mint(
+      to: string,
+      tokenId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    name(): NonPayableTransactionObject<string>;
+
+    nestMint(
+      to: string,
+      tokenId: number | string | BN,
+      destinationId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    nestTransfer(
+      to: string,
+      tokenId: number | string | BN,
+      destinationId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    nestTransferFrom(
+      from: string,
+      to: string,
+      tokenId: number | string | BN,
+      destinationId: number | string | BN,
+      data: string | number[]
+    ): NonPayableTransactionObject<void>;
+
+    onERC721Received(
+      _operator: string,
+      _from: string,
+      _tokenId: number | string | BN,
+      _data: string | number[]
+    ): NonPayableTransactionObject<string>;
+
+    ownerOf(tokenId: number | string | BN): NonPayableTransactionObject<string>;
+
+    pendingChildOf(
+      parentId: number | string | BN,
+      index: number | string | BN
+    ): NonPayableTransactionObject<[string, string]>;
+
+    pendingChildrenOf(
+      parentId: number | string | BN
+    ): NonPayableTransactionObject<[string, string][]>;
+
+    rejectAllChildren(
+      tokenId: number | string | BN,
+      maxRejections: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    safeMint(to: string): NonPayableTransactionObject<void>;
+
+    "safeTransferFrom(address,address,uint256)"(
+      from: string,
+      to: string,
+      tokenId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    "safeTransferFrom(address,address,uint256,bytes)"(
+      from: string,
+      to: string,
+      tokenId: number | string | BN,
+      data: string | number[]
+    ): NonPayableTransactionObject<void>;
+
+    setApprovalForAll(
+      operator: string,
+      approved: boolean
+    ): NonPayableTransactionObject<void>;
+
+    supportsInterface(
+      interfaceId: string | number[]
+    ): NonPayableTransactionObject<boolean>;
+
+    symbol(): NonPayableTransactionObject<string>;
+
+    transfer(
+      to: string,
+      tokenId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+
+    transferChild(
+      tokenId: number | string | BN,
+      to: string,
+      destinationId: number | string | BN,
+      childIndex: number | string | BN,
+      childAddress: string,
+      childId: number | string | BN,
+      isPending: boolean,
+      data: string | number[]
+    ): NonPayableTransactionObject<void>;
+
+    transferFrom(
+      from: string,
+      to: string,
+      tokenId: number | string | BN
+    ): NonPayableTransactionObject<void>;
+  };
+  events: {
+    AllChildrenRejected(cb?: Callback<AllChildrenRejected>): EventEmitter;
+    AllChildrenRejected(
+      options?: EventOptions,
+      cb?: Callback<AllChildrenRejected>
+    ): EventEmitter;
+
+    Approval(cb?: Callback<Approval>): EventEmitter;
+    Approval(options?: EventOptions, cb?: Callback<Approval>): EventEmitter;
+
+    ApprovalForAll(cb?: Callback<ApprovalForAll>): EventEmitter;
+    ApprovalForAll(
+      options?: EventOptions,
+      cb?: Callback<ApprovalForAll>
+    ): EventEmitter;
+
+    ChildAccepted(cb?: Callback<ChildAccepted>): EventEmitter;
+    ChildAccepted(
+      options?: EventOptions,
+      cb?: Callback<ChildAccepted>
+    ): EventEmitter;
+
+    ChildProposed(cb?: Callback<ChildProposed>): EventEmitter;
+    ChildProposed(
+      options?: EventOptions,
+      cb?: Callback<ChildProposed>
+    ): EventEmitter;
+
+    ChildTransferred(cb?: Callback<ChildTransferred>): EventEmitter;
+    ChildTransferred(
+      options?: EventOptions,
+      cb?: Callback<ChildTransferred>
+    ): EventEmitter;
+
+    NestTransfer(cb?: Callback<NestTransfer>): EventEmitter;
+    NestTransfer(
+      options?: EventOptions,
+      cb?: Callback<NestTransfer>
+    ): EventEmitter;
+
+    Transfer(cb?: Callback<Transfer>): EventEmitter;
+    Transfer(options?: EventOptions, cb?: Callback<Transfer>): EventEmitter;
+
+    allEvents(options?: EventOptions, cb?: Callback<EventLog>): EventEmitter;
+  };
+
+  once(event: "AllChildrenRejected", cb: Callback<AllChildrenRejected>): void;
+  once(
+    event: "AllChildrenRejected",
+    options: EventOptions,
+    cb: Callback<AllChildrenRejected>
+  ): void;
+
+  once(event: "Approval", cb: Callback<Approval>): void;
+  once(event: "Approval", options: EventOptions, cb: Callback<Approval>): void;
+
+  once(event: "ApprovalForAll", cb: Callback<ApprovalForAll>): void;
+  once(
+    event: "ApprovalForAll",
+    options: EventOptions,
+    cb: Callback<ApprovalForAll>
+  ): void;
+
+  once(event: "ChildAccepted", cb: Callback<ChildAccepted>): void;
+  once(
+    event: "ChildAccepted",
+    options: EventOptions,
+    cb: Callback<ChildAccepted>
+  ): void;
+
+  once(event: "ChildProposed", cb: Callback<ChildProposed>): void;
+  once(
+    event: "ChildProposed",
+    options: EventOptions,
+    cb: Callback<ChildProposed>
+  ): void;
+
+  once(event: "ChildTransferred", cb: Callback<ChildTransferred>): void;
+  once(
+    event: "ChildTransferred",
+    options: EventOptions,
+    cb: Callback<ChildTransferred>
+  ): void;
+
+  once(event: "NestTransfer", cb: Callback<NestTransfer>): void;
+  once(
+    event: "NestTransfer",
+    options: EventOptions,
+    cb: Callback<NestTransfer>
+  ): void;
+
+  once(event: "Transfer", cb: Callback<Transfer>): void;
+  once(event: "Transfer", options: EventOptions, cb: Callback<Transfer>): void;
+}
addedjs-packages/scripts/benchmarks/nesting/ABIGEN/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/nesting/ABIGEN/index.ts
@@ -0,0 +1,4 @@
+/* Autogenerated file. Do not edit manually. */
+/* tslint:disable */
+/* eslint-disable */
+export type { RMRKNestableMintable } from "./RMRKNestableMintable.js";
addedjs-packages/scripts/benchmarks/nesting/ABIGEN/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/nesting/ABIGEN/types.ts
@@ -0,0 +1,73 @@
+/* Autogenerated file. Do not edit manually. */
+/* tslint:disable */
+/* eslint-disable */
+import type BN from "bn.js";
+import type { EventEmitter } from "events";
+import type { EventLog, PromiEvent, TransactionReceipt } from "web3-core";
+import type { Contract } from "web3-eth-contract";
+
+export interface EstimateGasOptions {
+  from?: string;
+  gas?: number;
+  value?: number | string | BN;
+}
+
+export interface EventOptions {
+  filter?: object;
+  fromBlock?: BlockType;
+  topics?: string[];
+}
+
+export type Callback<T> = (error: Error, result: T) => void;
+export interface ContractEventLog<T> extends EventLog {
+  returnValues: T;
+}
+export interface ContractEventEmitter<T> extends EventEmitter {
+  on(event: "connected", listener: (subscriptionId: string) => void): this;
+  on(
+    event: "data" | "changed",
+    listener: (event: ContractEventLog<T>) => void
+  ): this;
+  on(event: "error", listener: (error: Error) => void): this;
+}
+
+export interface NonPayableTx {
+  nonce?: string | number | BN;
+  chainId?: string | number | BN;
+  from?: string;
+  to?: string;
+  data?: string;
+  gas?: string | number | BN;
+  maxPriorityFeePerGas?: string | number | BN;
+  maxFeePerGas?: string | number | BN;
+  gasPrice?: string | number | BN;
+}
+
+export interface PayableTx extends NonPayableTx {
+  value?: string | number | BN;
+}
+
+export interface NonPayableTransactionObject<T> {
+  arguments: any[];
+  call(tx?: NonPayableTx, block?: BlockType): Promise<T>;
+  send(tx?: NonPayableTx): PromiEvent<TransactionReceipt>;
+  estimateGas(tx?: NonPayableTx): Promise<number>;
+  encodeABI(): string;
+}
+
+export interface PayableTransactionObject<T> {
+  arguments: any[];
+  call(tx?: PayableTx, block?: BlockType): Promise<T>;
+  send(tx?: PayableTx): PromiEvent<TransactionReceipt>;
+  estimateGas(tx?: PayableTx): Promise<number>;
+  encodeABI(): string;
+}
+
+export type BlockType =
+  | "latest"
+  | "pending"
+  | "genesis"
+  | "earliest"
+  | number
+  | BN;
+export type BaseContract = Omit<Contract, "clone" | "once">;
addedjs-packages/scripts/benchmarks/nesting/RMRKNestableMintable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/nesting/RMRKNestableMintable.sol
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.18;
+
+import "@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol";
+import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
+
+contract RMRKNestableMintable is RMRKNestable, IERC721Receiver {
+	uint256 private _counter;
+
+	constructor() RMRKNestable("RMRK", "nesting") {
+		_counter = 1;
+	}
+
+	function safeMint(address to) external {
+		_safeMint(to, _counter, "");
+		_counter++;
+	}
+
+	function mint(address to, uint256 tokenId) external {
+		_mint(to, tokenId, "");
+	}
+
+	function nestMint(address to, uint256 tokenId, uint256 destinationId) external {
+		_nestMint(to, tokenId, destinationId, "");
+	}
+
+	function nestTransfer(address to, uint256 tokenId, uint256 destinationId) public virtual {
+		nestTransferFrom(_msgSender(), to, tokenId, destinationId, "");
+	}
+
+	function transfer(address to, uint256 tokenId) public virtual {
+		transferFrom(_msgSender(), to, tokenId);
+	}
+
+	function onERC721Received(
+		address _operator,
+		address _from,
+		uint256 _tokenId,
+		bytes calldata _data
+	) external returns (bytes4) {
+		return IERC721Receiver.onERC721Received.selector;
+	}
+}
addedjs-packages/scripts/benchmarks/nesting/abigen.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/nesting/abigen.ts
@@ -0,0 +1,20 @@
+import {runTypeChain, glob, DEFAULT_FLAGS} from 'typechain';
+
+
+async function main() {
+  const cwd = process.cwd();
+
+  // find all files matching the glob
+  const allFiles = glob(cwd, ['./ABI/*.abi']);
+
+  await runTypeChain({
+    cwd,
+    filesToProcess: allFiles,
+    allFiles,
+    outDir: 'ABIGEN',
+    target: 'web3-v1',
+    flags: {...DEFAULT_FLAGS, alwaysGenerateOverloads: true, tsNocheck: false},
+  });
+}
+
+main().catch(console.error);
\ No newline at end of file
addedjs-packages/scripts/benchmarks/nesting/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/nesting/index.ts
@@ -0,0 +1,222 @@
+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 {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 type {ContractImports} from '@unique/tests/eth/util/playgrounds/types.js';
+import type {RMRKNestableMintable} from './ABIGEN/index.js';
+
+const {dirname} = makeNames(import.meta.url);
+
+export const CONTRACT_IMPORT: ContractImports[] = [
+  {
+    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol`,
+    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol`,
+    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol`,
+    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol`,
+    solPath: '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`,
+    solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol`,
+    solPath: '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Address.sol`,
+    solPath: '@openzeppelin/contracts/utils/Address.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`,
+    solPath: '@openzeppelin/contracts/utils/Context.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`,
+    solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol`,
+    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol',
+  },
+  {
+    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol`,
+    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol',
+  },
+  {
+    fsPath: `${dirname}/RMRKNestableMintable.sol`,
+    solPath: 'RMRKNestableMintable.sol',
+  },
+];
+
+
+const main = async () => {
+
+  await usingEthPlaygrounds(async (helper, privateKey) => {
+
+    const donor = await privateKey('//Alice'); // Seed from account with balance on this network
+
+    const eth = await measureEth(helper, donor);
+    const sub = await measureSub(helper, donor);
+    const rmrk = await measureRMRK(helper, donor);
+    console.table({susbtrate: sub, eth: eth, rmrk: rmrk});
+  });
+};
+
+async function measureRMRK(helper: EthUniqueHelper, donor: IKeyringPair) {
+  const CONTRACT_SOURCE = (
+    await readFile(`${dirname}/RMRKNestableMintable.sol`)
+  ).toString();
+  const RELAYER_SOURCE = (await readFile(`${dirname}/relayer.sol`)).toString();
+
+  const ethSigner = await helper.eth.createAccountWithBalance(donor);
+
+  const contract = await helper.ethContract.deployByCode(
+    ethSigner,
+    'RMRKNestableMintable',
+    CONTRACT_SOURCE,
+    CONTRACT_IMPORT,
+    5000000,
+  );
+
+  const relayer = await helper.ethContract.deployByCode(
+    ethSigner,
+    'Relayer',
+    RELAYER_SOURCE,
+    CONTRACT_IMPORT,
+    5000000,
+    [contract.options.address],
+  );
+
+  const relayerAddress = relayer.options.address;
+
+  const rmrk = contract as any as RMRKNestableMintable;
+  const createTokenFor = async (receiver: string) => {
+    const tokenReceipt = await rmrk.methods.safeMint(receiver).send({from: ethSigner});
+    return tokenReceipt.events!['Transfer'].returnValues.tokenId as number;
+  };
+
+
+  const nestId = await createTokenFor(ethSigner);
+  const outerCollectionNestedId = 10;
+  const contractOwnedNestId = await createTokenFor(relayerAddress);
+  const nextTokenId = contractOwnedNestId + 1;
+
+  const nestTransfer = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {
+    const addChildData = rmrk.methods.addChild(nestId, outerCollectionNestedId, []).encodeABI();
+    await relayer.methods.relay(addChildData).send({from: ethSigner});
+    await rmrk.methods.acceptChild(nestId, 0, relayerAddress, outerCollectionNestedId).send({from: ethSigner});
+  });
+
+
+  const nestMint = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {
+    const nestMintData = rmrk.methods.nestMint(rmrk.options.address, nextTokenId, contractOwnedNestId).encodeABI();
+    await relayer.methods.relay(nestMintData).send({from: ethSigner});
+    const acceptNestedToken = rmrk.methods.acceptChild(contractOwnedNestId, 0, rmrk.options.address, nextTokenId).encodeABI();
+    await relayer.methods.relay(acceptNestedToken).send({from: ethSigner});
+  });
+
+
+  const unnestToken = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {
+    const unnestData = rmrk.methods.transferChild(contractOwnedNestId, ethSigner, 0, 0, rmrk.options.address, nextTokenId, false, []).encodeABI();
+    await relayer.methods.relay(unnestData).send({from: ethSigner});
+  });
+
+  return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)};
+}
+
+async function measureEth(helper: EthUniqueHelper, donor: IKeyringPair) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const {collectionId, contract} = await createNestingCollection(helper, owner);
+
+  // Create a token to be nested to
+  const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+  const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;
+  const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);
+
+  // Create a nested token
+  const nestMint = await helper.arrange.calculcateFee({Ethereum: owner}, async () => {
+    await contract.methods.mint(targetNftTokenAddress).send({from: owner});
+  });
+
+  // Create a token to be nested and nest
+  const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+  const nestedTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;
+
+  const nestTransfer = await helper.arrange.calculcateFee({Ethereum: owner}, async () => {
+    await contract.methods.transfer(targetNftTokenAddress, nestedTokenId).send({from: owner});
+  });
+
+  const unnestToken = await helper.arrange.calculcateFee({Ethereum: owner}, async () => {
+    await contract.methods.transferFrom(targetNftTokenAddress, owner, nestedTokenId).send({from: owner});
+  });
+  return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)};
+}
+
+async function measureSub(helper: EthUniqueHelper, donor: IKeyringPair) {
+
+  const [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+
+  const targetNFTCollection = await helper.nft.mintCollection(alice);
+  const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
+
+
+  const collectionForNesting = await helper.nft.mintCollection(bob);
+  // permissions should be set:
+  await targetNFTCollection.setPermissions(alice, {
+    nesting: {tokenOwner: true},
+  });
+
+  const nestMint = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => {
+    await (collectionForNesting.mintToken(bob, targetTokenBob.nestingAccount()));
+  });
+
+  const nestedToken2 = await collectionForNesting.mintToken(bob);
+  const nestTransfer = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => {
+    await nestedToken2.nest(bob, targetTokenBob);
+  });
+
+  const unnestToken = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => {
+    await nestedToken2.transferFrom(bob, targetTokenBob.nestingAccount(), {Substrate: bob.address});
+  });
+
+  return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)};
+}
+
+
+const createNestingCollection = async (
+  helper: EthUniqueHelper,
+  owner: string,
+): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {
+  const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+  const contract =  helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+  await contract.methods.setCollectionNesting(true).send({from: owner});
+
+  return {collectionId, collectionAddress, contract};
+};
+
+main()
+  .then(() => process.exit(0))
+  .catch((e) => {
+    console.log(e);
+    process.exit(1);
+  });
+
+
+
+
+
+
addedjs-packages/scripts/benchmarks/nesting/relayer.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/nesting/relayer.sol
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.18;
+
+import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
+import "./RMRKNestableMintable.sol";
+
+contract Relayer is RMRKNestableMintable {
+	address _receiver;
+
+	constructor(address recevier) {
+		_receiver = recevier;
+	}
+
+	function relay(bytes calldata payload) external {
+		(bool succes, bytes memory _returnData) = address(_receiver).call(payload);
+		require(succes);
+	}
+}
addedjs-packages/scripts/benchmarks/opsFee/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/opsFee/index.ts
@@ -0,0 +1,892 @@
+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 {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 {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';
+
+
+const {dirname} = makeNames(import.meta.url);
+
+const main = async () => {
+
+  const headers = [
+    'function',
+    'ethFee',
+    'ethGas',
+    'substrate',
+    'zeppelinFee',
+    'zeppelinGas',
+  ];
+
+  const csvWriter20 = createObjectCsvWriter({
+    path: 'erc20.csv',
+    header: headers,
+  });
+
+  const csvWriter721 = createObjectCsvWriter({
+    path: 'erc721.csv',
+    header: headers,
+  });
+
+  await usingEthPlaygrounds(async (helper, privateKey) => {
+    console.log('\n ERC20 ops fees');
+    const erc20 = FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey));
+    console.table(erc20);
+    await csvWriter20.writeRecords(FunctionFeeVM.toCsv(erc20));
+
+    console.log('\n ERC721 ops fees');
+    const erc721 = FunctionFeeVM.fromModel(await erc721CalculateFeeGas(helper, privateKey));
+    console.table(erc721);
+
+    await csvWriter721.writeRecords(FunctionFeeVM.toCsv(erc721));
+  });
+};
+
+main()
+  .then(() => process.exit(0))
+  .catch((e) => {
+    console.log(e);
+    process.exit(1);
+  });
+
+async function erc721CalculateFeeGas(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+): Promise<IFunctionFee> {
+  const res: IFunctionFee = {};
+  const donor = await privateKey('//Alice');
+  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);
+  const ethSigner = await helper.eth.createAccountWithBalance(donor);
+  const ethReceiver = await helper.eth.createAccountWithBalance(donor);
+  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);
+  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);
+  const collection = (await createCollectionForBenchmarks(
+    'nft',
+    helper,
+    privateKey,
+    ethSigner,
+    null,
+    PERMISSIONS,
+  )) as UniqueNFTCollection;
+
+  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);
+  let zeppelelinContract: Contract | null = null;
+  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.bin`)).toString();
+  const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.abi`)).toString());
+
+  const evmContract = await helper.ethNativeContract.collection(
+    helper.ethAddress.fromCollectionId(collection.collectionId),
+    'nft',
+    ethSigner,
+    true,
+  );
+
+  res['createCollection'] = await helper.arrange.calculcateFeeGas(
+    {Ethereum: ethSigner},
+    () => helperContract.methods.createNFTCollection('test','test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),
+  );
+
+  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => helper.nft.mintCollection(
+      donor,
+      {name: 'test', description: 'test', tokenPrefix: 'test'},
+    ),
+  )));
+
+  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(
+    {Ethereum: ethSigner},
+    async () => {
+      zeppelelinContract = await helper.ethContract.deployByAbi(
+        ethSigner,
+        ZEPPELIN_ABI,
+        ZEPPELIN_OBJECT,
+      );
+    },
+  );
+
+  res['mint'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.mint(ethSigner).send(),
+    );
+
+  res['mint'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.safeMint(ethSigner, '').send({from: ethSigner}),
+    );
+
+  res['mintCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.mintCross(crossSigner, []).send(),
+    );
+
+  res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.mintToken(
+      donor,
+      {Substrate: donor.address},
+    ),
+  )));
+
+  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.mintMultipleTokens(donor, [{
+      owner: {Substrate: donor.address},
+    }]),
+  )));
+
+  res['mintWithTokenURI'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(),
+    );
+
+  res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.mintToken(
+      donor,
+      {Ethereum: ethSigner},
+      [{key: 'URI', value: 'Test URI'}],
+    ),
+  )));
+
+  res['mintWithTokenURI'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.safeMint(ethSigner, 'Test URI').send({from: ethSigner}),
+    );
+
+  res['setProperties'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(),
+    );
+
+  res['deleteProperties'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(),
+    );
+
+  res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setTokenProperties(
+      donor,
+      1,
+      SUBS_PROPERTIES.slice(0, 1),
+    ),
+  )));
+
+  res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.deleteTokenProperties(
+      donor,
+      1,
+      SUBS_PROPERTIES.slice(0, 1)
+        .map(p => p.key),
+    ),
+  )));
+
+  res['transfer'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.transfer(ethReceiver, 1).send(),
+    );
+
+  res['safeTransferFrom*'] = {
+    zeppelin:
+      await helper.arrange.calculcateFeeGas(
+        {Ethereum: ethSigner},
+        () => zeppelelinContract!.methods.safeTransferFrom(ethSigner, ethReceiver, 0).send({from: ethSigner}),
+      ),
+  };
+
+  res['transferCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),
+    );
+
+  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.transferToken(
+      donor,
+      3,
+      {Substrate: subReceiver.address},
+    ),
+  )));
+  await collection.approveToken(subReceiver, 3, {Substrate: donor.address});
+
+  res['transferFrom*'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),
+    );
+
+  res['transferFrom*'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),
+    );
+
+
+  res['transferFromCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),
+    );
+
+  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}),
+  )));
+
+  res['burn'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.burn(1).send(),
+    );
+
+  res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.burnToken(donor, 3),
+  )));
+
+  res['approve*'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.approve(ethReceiver, 2).send(),
+    );
+
+  res['approve*'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.approve(ethReceiver, 0).send({from: ethSigner}),
+    );
+
+  res['approveCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.approveCross(crossReceiver, 2).send(),
+    );
+
+  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}),
+  )));
+
+  res['setApprovalForAll*'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.setApprovalForAll(ethReceiver, true).send(),
+    );
+
+  res['setApprovalForAll*'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.setApprovalForAll(ethReceiver, true).send({from: ethSigner}),
+    );
+
+  res['setApprovalForAll*'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => helper.nft.setAllowanceForAll(donor, collection.collectionId, {Substrate: subReceiver.address}, true),
+  )));
+
+  res['burnFromCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}),
+    );
+
+  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: subReceiver.address},
+    () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}),
+  )));
+
+  res['setTokenPropertyPermissions'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setTokenPropertyPermissions([
+        ['url', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+      ]).send(),
+    );
+
+  res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: {
+      tokenOwner: true,
+      collectionAdmin: true,
+      mutable: true,
+    }}]),
+  )));
+
+  res['setCollectionSponsorCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),
+    );
+
+  res['confirmCollectionSponsorship'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),
+    );
+
+  res['removeCollectionSponsor'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.removeCollectionSponsor().send(),
+    );
+
+  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setSponsor(donor, subReceiver.address),
+  )));
+
+  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: subReceiver.address},
+    () => collection.confirmSponsorship(subReceiver),
+  )));
+
+  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.removeSponsor(donor),
+  )));
+
+  res['setCollectionProperties'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),
+    );
+
+  res['deleteCollectionProperties'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),
+    );
+  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
+      .map(p => ({key: p.key, value: p.value.toString()}))),
+  )));
+
+  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)
+      .map(p => p.key)),
+  )));
+
+  res['setCollectionLimit'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),
+    );
+
+  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),
+  )));
+
+  const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send();
+  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);
+
+
+  res['addCollectionAdminCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),
+    );
+
+  res['removeCollectionAdminCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),
+    );
+
+  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['setCollectionNesting'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionNesting(true).send(),
+    );
+
+  res['setCollectionNesting[]'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),
+    );
+
+  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.disableNesting(donor),
+  )));
+
+  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setPermissions(
+      donor,
+      {
+        nesting: {
+          tokenOwner: true,
+          restricted: [collection.collectionId],
+        },
+      },
+    ),
+  )));
+
+  res['setCollectionAccess'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionAccess(1).send(),
+    );
+
+  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setPermissions(donor, {access: 'AllowList'}),
+  )));
+
+  res['addToCollectionAllowListCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),
+    );
+
+  res['removeFromCollectionAllowListCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),
+    );
+
+  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['setCollectionMintMode'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionMintMode(true).send(),
+    );
+
+  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setPermissions(donor, {mintMode: false}),
+  )));
+
+  res['changeCollectionOwnerCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),
+    );
+
+  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.changeOwner(donor, subReceiver.address),
+  )));
+
+  return res;
+}
+
+async function erc20CalculateFeeGas(
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+
+): Promise<IFunctionFee> {
+  const res: IFunctionFee = {};
+  const donor = await privateKey('//Alice');
+  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);
+  const ethSigner = await helper.eth.createAccountWithBalance(donor);
+  const ethReceiver = await helper.eth.createAccountWithBalance(donor);
+  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);
+  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);
+  const collection = (await createCollectionForBenchmarks(
+    'ft',
+    helper,
+    privateKey,
+    ethSigner,
+    null,
+    PERMISSIONS,
+  )) as UniqueFTCollection;
+
+  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);
+  let zeppelelinContract: Contract | null = null;
+  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.bin`)).toString();
+  const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.abi`)).toString());
+
+  const evmContract = await helper.ethNativeContract.collection(
+    helper.ethAddress.fromCollectionId(collection.collectionId),
+    'ft',
+    ethSigner,
+    true,
+  );
+
+  res['createCollection'] = await helper.arrange.calculcateFeeGas(
+    {Ethereum: ethSigner},
+    () => helperContract.methods.createFTCollection('test', 18,'test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),
+  );
+
+  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => helper.ft.mintCollection(
+      donor,
+      {name: 'test', description: 'test', tokenPrefix: 'test'},
+      18,
+    ),
+  )));
+
+  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(
+    {Ethereum: ethSigner},
+    async () => {
+      zeppelelinContract = await helper.ethContract.deployByAbi(
+        ethSigner,
+        ZEPPELIN_ABI,
+        ZEPPELIN_OBJECT,
+      );
+    },
+  );
+
+  res['mint'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.mint(ethSigner, 1).send(),
+    );
+
+  res['mint'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.mint(ethSigner, 1).send(),
+    );
+
+  res['mintCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.mintCross(crossSigner, 1).send(),
+    );
+
+  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.mint(
+      donor,
+      10n,
+      {Substrate: donor.address},
+    ),
+  )));
+
+  res['mintBulk'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.mintBulk([{to: ethSigner, amount: 1}]).send(),
+    );
+
+  res['mintBulk'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => helper.executeExtrinsic(donor, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, {
+      Fungible: new Map([
+        [JSON.stringify({Ethereum: ethSigner}), 1],
+      ]),
+    }], true),
+  )));
+
+  res['transfer*'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.transfer(ethReceiver, 1).send(),
+    );
+
+  res['transfer*'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.transfer(ethReceiver, 1).send(),
+    );
+
+  res['transferCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),
+    );
+
+  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.transfer(
+      donor,
+      {Substrate: subReceiver.address},
+      1n,
+    ),
+  )));
+  await collection.approveTokens(subReceiver, {Substrate: donor.address}, 1n);
+
+  res['transferFrom*'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),
+    );
+
+  await zeppelelinContract!.methods.approve(ethSigner, 10).send({from: ethReceiver});
+
+  res['transferFrom*'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 1).send({from: ethSigner}),
+    );
+
+  res['transferFromCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),
+    );
+
+  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.transferFrom(donor, {Substrate: subReceiver.address}, {Substrate: donor.address}, 1n),
+  )));
+
+
+  res['burnTokens'] = {fee: 0n, gas: 0n};
+  res['burnTokens'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.burnTokens(donor, 1n),
+  )));
+
+
+  res['approve*'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.approve(ethReceiver, 2).send(),
+    );
+
+  res['approve*'].zeppelin =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => zeppelelinContract!.methods.approve(ethReceiver, 10).send(),
+    );
+
+  res['approveCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.approveCross(crossReceiver, 2).send(),
+    );
+
+  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.approveTokens(donor, {Substrate: subReceiver.address}, 1n),
+  )));
+
+  res['burnFromCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () => evmContract.methods.burnFromCross(crossSigner, 1).send({from:ethReceiver}),
+    );
+
+  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: subReceiver.address},
+    () => collection.burnTokensFrom(subReceiver, {Substrate: donor.address}, 1n),
+  )));
+
+  res['setCollectionSponsorCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),
+    );
+
+  res['confirmCollectionSponsorship'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethReceiver},
+      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),
+    );
+
+  res['removeCollectionSponsor'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.removeCollectionSponsor().send(),
+    );
+
+  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setSponsor(donor, subReceiver.address),
+  )));
+
+  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: subReceiver.address},
+    () => collection.confirmSponsorship(subReceiver),
+  )));
+
+  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.removeSponsor(donor),
+  )));
+
+  res['setCollectionProperties'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),
+    );
+
+  res['deleteCollectionProperties'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),
+    );
+  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
+      .map(p => ({key: p.key, value: p.value.toString()}))),
+  )));
+
+  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)
+      .map(p => p.key)),
+  )));
+
+  res['setCollectionLimit'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),
+    );
+
+  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),
+  )));
+
+  const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send();
+  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);
+
+
+  res['addCollectionAdminCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),
+    );
+
+  res['removeCollectionAdminCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),
+    );
+
+  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['setCollectionNesting'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionNesting(true).send(),
+    );
+
+  res['setCollectionNesting[]'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),
+    );
+
+  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.disableNesting(donor),
+  )));
+
+  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setPermissions(
+      donor,
+      {
+        nesting: {
+          tokenOwner: true,
+          restricted: [collection.collectionId],
+        },
+      },
+    ),
+  )));
+
+  res['setCollectionAccess'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionAccess(1).send(),
+    );
+
+  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setPermissions(donor, {access: 'AllowList'}),
+  )));
+
+  res['addToCollectionAllowListCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),
+    );
+
+  res['removeFromCollectionAllowListCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),
+    );
+
+  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),
+  )));
+
+  res['setCollectionMintMode'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  evmContract.methods.setCollectionMintMode(true).send(),
+    );
+
+  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.setPermissions(donor, {mintMode: false}),
+  )));
+
+  res['changeCollectionOwnerCross'] =
+    await helper.arrange.calculcateFeeGas(
+      {Ethereum: ethSigner},
+      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),
+    );
+
+  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
+    {Substrate: donor.address},
+    () => collection.changeOwner(donor, subReceiver.address),
+  )));
+
+  return res;
+}
addedjs-packages/scripts/benchmarks/utils/common.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/common.ts
@@ -0,0 +1,88 @@
+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 type {IKeyringPair} from '@polkadot/types/types';
+
+export const PROPERTIES = Array(40)
+  .fill(0)
+  .map((_, i) => ({
+    key: `key_${i}`,
+    value: Uint8Array.from(Buffer.from(`value_${i}`)),
+  }));
+
+export const SUBS_PROPERTIES = Array(40)
+  .fill(0)
+  .map((_, i) => ({
+    key: `key_${i}`,
+    value: `value_${i}`,
+  }));
+
+export const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => ({
+  key: p.key,
+  permission: {
+    tokenOwner: true,
+    collectionAdmin: true,
+    mutable: true,
+  },
+}));
+
+export function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number {
+  return Number((value * 1000n) / nominal) / 1000;
+}
+
+export async function createCollectionForBenchmarks(
+  mode : TCollectionMode,
+  helper: EthUniqueHelper,
+  privateKey: (seed: string) => Promise<IKeyringPair>,
+  ethSigner: string,
+  proxyContract: string | null,
+  permissions: ITokenPropertyPermission[],
+) {
+  const donor = await privateKey('//Alice');
+
+  const collection = await helper[mode].mintCollection(donor, {
+    name: 'test mintToSubstrate',
+    description: 'EVMHelpers',
+    tokenPrefix: mode,
+    ...(mode != 'ft' ? {
+      tokenPropertyPermissions: [
+        {
+          key: 'url',
+          permission: {
+            tokenOwner: true,
+            collectionAdmin: true,
+            mutable: true,
+          },
+        },
+        {
+          key: 'URI',
+          permission: {
+            tokenOwner: true,
+            collectionAdmin: true,
+            mutable: true,
+          },
+        },
+      ],
+    } : {}),
+    limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},
+    permissions: {mintMode: true},
+  });
+
+  await collection.addToAllowList(donor, {
+    Ethereum: helper.address.substrateToEth(donor.address),
+  });
+  await collection.addToAllowList(donor, {Substrate: donor.address});
+  await collection.addAdmin(donor, {Ethereum: ethSigner});
+  await collection.addAdmin(donor, {
+    Ethereum: helper.address.substrateToEth(donor.address),
+  });
+
+  if(proxyContract) {
+    await collection.addToAllowList(donor, {Ethereum: proxyContract});
+    await collection.addAdmin(donor, {Ethereum: proxyContract});
+  }
+  if(collection instanceof UniqueNFTCollection || collection instanceof UniqueRFTCollection)
+    await collection.setTokenPropertyPermissions(donor, permissions);
+
+  return collection;
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/README.mddiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/README.md
@@ -0,0 +1,12 @@
+# OpenZeppelin Contracts
+
+The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1.
+
+They are not meant to be edited.
+
+The originals can be found on [GitHub] and [npm].
+
+[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1
+[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1
+
+Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard).
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/access/Ownable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/access/Ownable.sol
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
+
+pragma solidity ^0.8.0;
+
+import "../utils/Context.sol";
+
+/**
+ * @dev Contract module which provides a basic access control mechanism, where
+ * there is an account (an owner) that can be granted exclusive access to
+ * specific functions.
+ *
+ * By default, the owner account will be the one that deploys the contract. This
+ * can later be changed with {transferOwnership}.
+ *
+ * This module is used through inheritance. It will make available the modifier
+ * `onlyOwner`, which can be applied to your functions to restrict their use to
+ * the owner.
+ */
+abstract contract Ownable is Context {
+    address private _owner;
+
+    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+
+    /**
+     * @dev Initializes the contract setting the deployer as the initial owner.
+     */
+    constructor() {
+        _transferOwnership(_msgSender());
+    }
+
+    /**
+     * @dev Throws if called by any account other than the owner.
+     */
+    modifier onlyOwner() {
+        _checkOwner();
+        _;
+    }
+
+    /**
+     * @dev Returns the address of the current owner.
+     */
+    function owner() public view virtual returns (address) {
+        return _owner;
+    }
+
+    /**
+     * @dev Throws if the sender is not the owner.
+     */
+    function _checkOwner() internal view virtual {
+        require(owner() == _msgSender(), "Ownable: caller is not the owner");
+    }
+
+    /**
+     * @dev Leaves the contract without owner. It will not be possible to call
+     * `onlyOwner` functions anymore. Can only be called by the current owner.
+     *
+     * NOTE: Renouncing ownership will leave the contract without an owner,
+     * thereby removing any functionality that is only available to the owner.
+     */
+    function renounceOwnership() public virtual onlyOwner {
+        _transferOwnership(address(0));
+    }
+
+    /**
+     * @dev Transfers ownership of the contract to a new account (`newOwner`).
+     * Can only be called by the current owner.
+     */
+    function transferOwnership(address newOwner) public virtual onlyOwner {
+        require(newOwner != address(0), "Ownable: new owner is the zero address");
+        _transferOwnership(newOwner);
+    }
+
+    /**
+     * @dev Transfers ownership of the contract to a new account (`newOwner`).
+     * Internal function without access restriction.
+     */
+    function _transferOwnership(address newOwner) internal virtual {
+        address oldOwner = _owner;
+        _owner = newOwner;
+        emit OwnershipTransferred(oldOwner, newOwner);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/governance/utils/IVotes.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/governance/utils/IVotes.sol
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
+ *
+ * _Available since v4.5._
+ */
+interface IVotes {
+    /**
+     * @dev Emitted when an account changes their delegate.
+     */
+    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
+
+    /**
+     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
+     */
+    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
+
+    /**
+     * @dev Returns the current amount of votes that `account` has.
+     */
+    function getVotes(address account) external view returns (uint256);
+
+    /**
+     * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
+     */
+    function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
+
+    /**
+     * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
+     *
+     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
+     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
+     * vote.
+     */
+    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
+
+    /**
+     * @dev Returns the delegate that `account` has chosen.
+     */
+    function delegates(address account) external view returns (address);
+
+    /**
+     * @dev Delegates votes from the sender to `delegatee`.
+     */
+    function delegate(address delegatee) external;
+
+    /**
+     * @dev Delegates votes from signer to `delegatee`.
+     */
+    function delegateBySig(
+        address delegatee,
+        uint256 nonce,
+        uint256 expiry,
+        uint8 v,
+        bytes32 r,
+        bytes32 s
+    ) external;
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Interface of the ERC3156 FlashBorrower, as defined in
+ * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
+ *
+ * _Available since v4.1._
+ */
+interface IERC3156FlashBorrower {
+    /**
+     * @dev Receive a flash loan.
+     * @param initiator The initiator of the loan.
+     * @param token The loan currency.
+     * @param amount The amount of tokens lent.
+     * @param fee The additional amount of tokens to repay.
+     * @param data Arbitrary data structure, intended to contain user-defined parameters.
+     * @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan"
+     */
+    function onFlashLoan(
+        address initiator,
+        address token,
+        uint256 amount,
+        uint256 fee,
+        bytes calldata data
+    ) external returns (bytes32);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashLender.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol)
+
+pragma solidity ^0.8.0;
+
+import "./IERC3156FlashBorrower.sol";
+
+/**
+ * @dev Interface of the ERC3156 FlashLender, as defined in
+ * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
+ *
+ * _Available since v4.1._
+ */
+interface IERC3156FlashLender {
+    /**
+     * @dev The amount of currency available to be lended.
+     * @param token The loan currency.
+     * @return The amount of `token` that can be borrowed.
+     */
+    function maxFlashLoan(address token) external view returns (uint256);
+
+    /**
+     * @dev The fee to be charged for a given loan.
+     * @param token The loan currency.
+     * @param amount The amount of tokens lent.
+     * @return The amount of `token` to be charged for the loan, on top of the returned principal.
+     */
+    function flashFee(address token, uint256 amount) external view returns (uint256);
+
+    /**
+     * @dev Initiate a flash loan.
+     * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
+     * @param token The loan currency.
+     * @param amount The amount of tokens lent.
+     * @param data Arbitrary data structure, intended to contain user-defined parameters.
+     */
+    function flashLoan(
+        IERC3156FlashBorrower receiver,
+        address token,
+        uint256 amount,
+        bytes calldata data
+    ) external returns (bool);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/security/Pausable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/security/Pausable.sol
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
+
+pragma solidity ^0.8.0;
+
+import "../utils/Context.sol";
+
+/**
+ * @dev Contract module which allows children to implement an emergency stop
+ * mechanism that can be triggered by an authorized account.
+ *
+ * This module is used through inheritance. It will make available the
+ * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
+ * the functions of your contract. Note that they will not be pausable by
+ * simply including this module, only once the modifiers are put in place.
+ */
+abstract contract Pausable is Context {
+    /**
+     * @dev Emitted when the pause is triggered by `account`.
+     */
+    event Paused(address account);
+
+    /**
+     * @dev Emitted when the pause is lifted by `account`.
+     */
+    event Unpaused(address account);
+
+    bool private _paused;
+
+    /**
+     * @dev Initializes the contract in unpaused state.
+     */
+    constructor() {
+        _paused = false;
+    }
+
+    /**
+     * @dev Modifier to make a function callable only when the contract is not paused.
+     *
+     * Requirements:
+     *
+     * - The contract must not be paused.
+     */
+    modifier whenNotPaused() {
+        _requireNotPaused();
+        _;
+    }
+
+    /**
+     * @dev Modifier to make a function callable only when the contract is paused.
+     *
+     * Requirements:
+     *
+     * - The contract must be paused.
+     */
+    modifier whenPaused() {
+        _requirePaused();
+        _;
+    }
+
+    /**
+     * @dev Returns true if the contract is paused, and false otherwise.
+     */
+    function paused() public view virtual returns (bool) {
+        return _paused;
+    }
+
+    /**
+     * @dev Throws if the contract is paused.
+     */
+    function _requireNotPaused() internal view virtual {
+        require(!paused(), "Pausable: paused");
+    }
+
+    /**
+     * @dev Throws if the contract is not paused.
+     */
+    function _requirePaused() internal view virtual {
+        require(paused(), "Pausable: not paused");
+    }
+
+    /**
+     * @dev Triggers stopped state.
+     *
+     * Requirements:
+     *
+     * - The contract must not be paused.
+     */
+    function _pause() internal virtual whenNotPaused {
+        _paused = true;
+        emit Paused(_msgSender());
+    }
+
+    /**
+     * @dev Returns to normal state.
+     *
+     * Requirements:
+     *
+     * - The contract must be paused.
+     */
+    function _unpause() internal virtual whenPaused {
+        _paused = false;
+        emit Unpaused(_msgSender());
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/ERC20.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/ERC20.sol
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
+
+pragma solidity ^0.8.0;
+
+import "./IERC20.sol";
+import "./extensions/IERC20Metadata.sol";
+import "../../utils/Context.sol";
+
+/**
+ * @dev Implementation of the {IERC20} interface.
+ *
+ * This implementation is agnostic to the way tokens are created. This means
+ * that a supply mechanism has to be added in a derived contract using {_mint}.
+ * For a generic mechanism see {ERC20PresetMinterPauser}.
+ *
+ * TIP: For a detailed writeup see our guide
+ * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
+ * to implement supply mechanisms].
+ *
+ * We have followed general OpenZeppelin Contracts guidelines: functions revert
+ * instead returning `false` on failure. This behavior is nonetheless
+ * conventional and does not conflict with the expectations of ERC20
+ * applications.
+ *
+ * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
+ * This allows applications to reconstruct the allowance for all accounts just
+ * by listening to said events. Other implementations of the EIP may not emit
+ * these events, as it isn't required by the specification.
+ *
+ * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
+ * functions have been added to mitigate the well-known issues around setting
+ * allowances. See {IERC20-approve}.
+ */
+contract ERC20 is Context, IERC20, IERC20Metadata {
+    mapping(address => uint256) private _balances;
+
+    mapping(address => mapping(address => uint256)) private _allowances;
+
+    uint256 private _totalSupply;
+
+    string private _name;
+    string private _symbol;
+
+    /**
+     * @dev Sets the values for {name} and {symbol}.
+     *
+     * The default value of {decimals} is 18. To select a different value for
+     * {decimals} you should overload it.
+     *
+     * All two of these values are immutable: they can only be set once during
+     * construction.
+     */
+    constructor(string memory name_, string memory symbol_) {
+        _name = name_;
+        _symbol = symbol_;
+    }
+
+    /**
+     * @dev Returns the name of the token.
+     */
+    function name() public view virtual override returns (string memory) {
+        return _name;
+    }
+
+    /**
+     * @dev Returns the symbol of the token, usually a shorter version of the
+     * name.
+     */
+    function symbol() public view virtual override returns (string memory) {
+        return _symbol;
+    }
+
+    /**
+     * @dev Returns the number of decimals used to get its user representation.
+     * For example, if `decimals` equals `2`, a balance of `505` tokens should
+     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
+     *
+     * Tokens usually opt for a value of 18, imitating the relationship between
+     * Ether and Wei. This is the value {ERC20} uses, unless this function is
+     * overridden;
+     *
+     * NOTE: This information is only used for _display_ purposes: it in
+     * no way affects any of the arithmetic of the contract, including
+     * {IERC20-balanceOf} and {IERC20-transfer}.
+     */
+    function decimals() public view virtual override returns (uint8) {
+        return 18;
+    }
+
+    /**
+     * @dev See {IERC20-totalSupply}.
+     */
+    function totalSupply() public view virtual override returns (uint256) {
+        return _totalSupply;
+    }
+
+    /**
+     * @dev See {IERC20-balanceOf}.
+     */
+    function balanceOf(address account) public view virtual override returns (uint256) {
+        return _balances[account];
+    }
+
+    /**
+     * @dev See {IERC20-transfer}.
+     *
+     * Requirements:
+     *
+     * - `to` cannot be the zero address.
+     * - the caller must have a balance of at least `amount`.
+     */
+    function transfer(address to, uint256 amount) public virtual override returns (bool) {
+        address owner = _msgSender();
+        _transfer(owner, to, amount);
+        return true;
+    }
+
+    /**
+     * @dev See {IERC20-allowance}.
+     */
+    function allowance(address owner, address spender) public view virtual override returns (uint256) {
+        return _allowances[owner][spender];
+    }
+
+    /**
+     * @dev See {IERC20-approve}.
+     *
+     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
+     * `transferFrom`. This is semantically equivalent to an infinite approval.
+     *
+     * Requirements:
+     *
+     * - `spender` cannot be the zero address.
+     */
+    function approve(address spender, uint256 amount) public virtual override returns (bool) {
+        address owner = _msgSender();
+        _approve(owner, spender, amount);
+        return true;
+    }
+
+    /**
+     * @dev See {IERC20-transferFrom}.
+     *
+     * Emits an {Approval} event indicating the updated allowance. This is not
+     * required by the EIP. See the note at the beginning of {ERC20}.
+     *
+     * NOTE: Does not update the allowance if the current allowance
+     * is the maximum `uint256`.
+     *
+     * Requirements:
+     *
+     * - `from` and `to` cannot be the zero address.
+     * - `from` must have a balance of at least `amount`.
+     * - the caller must have allowance for ``from``'s tokens of at least
+     * `amount`.
+     */
+    function transferFrom(
+        address from,
+        address to,
+        uint256 amount
+    ) public virtual override returns (bool) {
+        address spender = _msgSender();
+        _spendAllowance(from, spender, amount);
+        _transfer(from, to, amount);
+        return true;
+    }
+
+    /**
+     * @dev Atomically increases the allowance granted to `spender` by the caller.
+     *
+     * This is an alternative to {approve} that can be used as a mitigation for
+     * problems described in {IERC20-approve}.
+     *
+     * Emits an {Approval} event indicating the updated allowance.
+     *
+     * Requirements:
+     *
+     * - `spender` cannot be the zero address.
+     */
+    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
+        address owner = _msgSender();
+        _approve(owner, spender, allowance(owner, spender) + addedValue);
+        return true;
+    }
+
+    /**
+     * @dev Atomically decreases the allowance granted to `spender` by the caller.
+     *
+     * This is an alternative to {approve} that can be used as a mitigation for
+     * problems described in {IERC20-approve}.
+     *
+     * Emits an {Approval} event indicating the updated allowance.
+     *
+     * Requirements:
+     *
+     * - `spender` cannot be the zero address.
+     * - `spender` must have allowance for the caller of at least
+     * `subtractedValue`.
+     */
+    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
+        address owner = _msgSender();
+        uint256 currentAllowance = allowance(owner, spender);
+        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
+        unchecked {
+            _approve(owner, spender, currentAllowance - subtractedValue);
+        }
+
+        return true;
+    }
+
+    /**
+     * @dev Moves `amount` of tokens from `from` to `to`.
+     *
+     * This internal function is equivalent to {transfer}, and can be used to
+     * e.g. implement automatic token fees, slashing mechanisms, etc.
+     *
+     * Emits a {Transfer} event.
+     *
+     * Requirements:
+     *
+     * - `from` cannot be the zero address.
+     * - `to` cannot be the zero address.
+     * - `from` must have a balance of at least `amount`.
+     */
+    function _transfer(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual {
+        require(from != address(0), "ERC20: transfer from the zero address");
+        require(to != address(0), "ERC20: transfer to the zero address");
+
+        _beforeTokenTransfer(from, to, amount);
+
+        uint256 fromBalance = _balances[from];
+        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
+        unchecked {
+            _balances[from] = fromBalance - amount;
+            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
+            // decrementing then incrementing.
+            _balances[to] += amount;
+        }
+
+        emit Transfer(from, to, amount);
+
+        _afterTokenTransfer(from, to, amount);
+    }
+
+    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
+     * the total supply.
+     *
+     * Emits a {Transfer} event with `from` set to the zero address.
+     *
+     * Requirements:
+     *
+     * - `account` cannot be the zero address.
+     */
+    function _mint(address account, uint256 amount) internal virtual {
+        require(account != address(0), "ERC20: mint to the zero address");
+
+        _beforeTokenTransfer(address(0), account, amount);
+
+        _totalSupply += amount;
+        unchecked {
+            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
+            _balances[account] += amount;
+        }
+        emit Transfer(address(0), account, amount);
+
+        _afterTokenTransfer(address(0), account, amount);
+    }
+
+    /**
+     * @dev Destroys `amount` tokens from `account`, reducing the
+     * total supply.
+     *
+     * Emits a {Transfer} event with `to` set to the zero address.
+     *
+     * Requirements:
+     *
+     * - `account` cannot be the zero address.
+     * - `account` must have at least `amount` tokens.
+     */
+    function _burn(address account, uint256 amount) internal virtual {
+        require(account != address(0), "ERC20: burn from the zero address");
+
+        _beforeTokenTransfer(account, address(0), amount);
+
+        uint256 accountBalance = _balances[account];
+        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
+        unchecked {
+            _balances[account] = accountBalance - amount;
+            // Overflow not possible: amount <= accountBalance <= totalSupply.
+            _totalSupply -= amount;
+        }
+
+        emit Transfer(account, address(0), amount);
+
+        _afterTokenTransfer(account, address(0), amount);
+    }
+
+    /**
+     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
+     *
+     * This internal function is equivalent to `approve`, and can be used to
+     * e.g. set automatic allowances for certain subsystems, etc.
+     *
+     * Emits an {Approval} event.
+     *
+     * Requirements:
+     *
+     * - `owner` cannot be the zero address.
+     * - `spender` cannot be the zero address.
+     */
+    function _approve(
+        address owner,
+        address spender,
+        uint256 amount
+    ) internal virtual {
+        require(owner != address(0), "ERC20: approve from the zero address");
+        require(spender != address(0), "ERC20: approve to the zero address");
+
+        _allowances[owner][spender] = amount;
+        emit Approval(owner, spender, amount);
+    }
+
+    /**
+     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
+     *
+     * Does not update the allowance amount in case of infinite allowance.
+     * Revert if not enough allowance is available.
+     *
+     * Might emit an {Approval} event.
+     */
+    function _spendAllowance(
+        address owner,
+        address spender,
+        uint256 amount
+    ) internal virtual {
+        uint256 currentAllowance = allowance(owner, spender);
+        if (currentAllowance != type(uint256).max) {
+            require(currentAllowance >= amount, "ERC20: insufficient allowance");
+            unchecked {
+                _approve(owner, spender, currentAllowance - amount);
+            }
+        }
+    }
+
+    /**
+     * @dev Hook that is called before any transfer of tokens. This includes
+     * minting and burning.
+     *
+     * Calling conditions:
+     *
+     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
+     * will be transferred to `to`.
+     * - when `from` is zero, `amount` tokens will be minted for `to`.
+     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
+     * - `from` and `to` are never both zero.
+     *
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
+     */
+    function _beforeTokenTransfer(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual {}
+
+    /**
+     * @dev Hook that is called after any transfer of tokens. This includes
+     * minting and burning.
+     *
+     * Calling conditions:
+     *
+     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
+     * has been transferred to `to`.
+     * - when `from` is zero, `amount` tokens have been minted for `to`.
+     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
+     * - `from` and `to` are never both zero.
+     *
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
+     */
+    function _afterTokenTransfer(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual {}
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/IERC20.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/IERC20.sol
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Interface of the ERC20 standard as defined in the EIP.
+ */
+interface IERC20 {
+    /**
+     * @dev Emitted when `value` tokens are moved from one account (`from`) to
+     * another (`to`).
+     *
+     * Note that `value` may be zero.
+     */
+    event Transfer(address indexed from, address indexed to, uint256 value);
+
+    /**
+     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
+     * a call to {approve}. `value` is the new allowance.
+     */
+    event Approval(address indexed owner, address indexed spender, uint256 value);
+
+    /**
+     * @dev Returns the amount of tokens in existence.
+     */
+    function totalSupply() external view returns (uint256);
+
+    /**
+     * @dev Returns the amount of tokens owned by `account`.
+     */
+    function balanceOf(address account) external view returns (uint256);
+
+    /**
+     * @dev Moves `amount` tokens from the caller's account to `to`.
+     *
+     * Returns a boolean value indicating whether the operation succeeded.
+     *
+     * Emits a {Transfer} event.
+     */
+    function transfer(address to, uint256 amount) external returns (bool);
+
+    /**
+     * @dev Returns the remaining number of tokens that `spender` will be
+     * allowed to spend on behalf of `owner` through {transferFrom}. This is
+     * zero by default.
+     *
+     * This value changes when {approve} or {transferFrom} are called.
+     */
+    function allowance(address owner, address spender) external view returns (uint256);
+
+    /**
+     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
+     *
+     * Returns a boolean value indicating whether the operation succeeded.
+     *
+     * IMPORTANT: Beware that changing an allowance with this method brings the risk
+     * that someone may use both the old and the new allowance by unfortunate
+     * transaction ordering. One possible solution to mitigate this race
+     * condition is to first reduce the spender's allowance to 0 and set the
+     * desired value afterwards:
+     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+     *
+     * Emits an {Approval} event.
+     */
+    function approve(address spender, uint256 amount) external returns (bool);
+
+    /**
+     * @dev Moves `amount` tokens from `from` to `to` using the
+     * allowance mechanism. `amount` is then deducted from the caller's
+     * allowance.
+     *
+     * Returns a boolean value indicating whether the operation succeeded.
+     *
+     * Emits a {Transfer} event.
+     */
+    function transferFrom(
+        address from,
+        address to,
+        uint256 amount
+    ) external returns (bool);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
+
+pragma solidity ^0.8.0;
+
+import "../ERC20.sol";
+import "../../../utils/Context.sol";
+
+/**
+ * @dev Extension of {ERC20} that allows token holders to destroy both their own
+ * tokens and those that they have an allowance for, in a way that can be
+ * recognized off-chain (via event analysis).
+ */
+abstract contract ERC20Burnable is Context, ERC20 {
+    /**
+     * @dev Destroys `amount` tokens from the caller.
+     *
+     * See {ERC20-_burn}.
+     */
+    function burn(uint256 amount) public virtual {
+        _burn(_msgSender(), amount);
+    }
+
+    /**
+     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
+     * allowance.
+     *
+     * See {ERC20-_burn} and {ERC20-allowance}.
+     *
+     * Requirements:
+     *
+     * - the caller must have allowance for ``accounts``'s tokens of at least
+     * `amount`.
+     */
+    function burnFrom(address account, uint256 amount) public virtual {
+        _spendAllowance(account, _msgSender(), amount);
+        _burn(account, amount);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol)
+
+pragma solidity ^0.8.0;
+
+import "../../../interfaces/IERC3156FlashBorrower.sol";
+import "../../../interfaces/IERC3156FlashLender.sol";
+import "../ERC20.sol";
+
+/**
+ * @dev Implementation of the ERC3156 Flash loans extension, as defined in
+ * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
+ *
+ * Adds the {flashLoan} method, which provides flash loan support at the token
+ * level. By default there is no fee, but this can be changed by overriding {flashFee}.
+ *
+ * _Available since v4.1._
+ */
+abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
+    bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
+
+    /**
+     * @dev Returns the maximum amount of tokens available for loan.
+     * @param token The address of the token that is requested.
+     * @return The amount of token that can be loaned.
+     */
+    function maxFlashLoan(address token) public view virtual override returns (uint256) {
+        return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;
+    }
+
+    /**
+     * @dev Returns the fee applied when doing flash loans. This function calls
+     * the {_flashFee} function which returns the fee applied when doing flash
+     * loans.
+     * @param token The token to be flash loaned.
+     * @param amount The amount of tokens to be loaned.
+     * @return The fees applied to the corresponding flash loan.
+     */
+    function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {
+        require(token == address(this), "ERC20FlashMint: wrong token");
+        return _flashFee(token, amount);
+    }
+
+    /**
+     * @dev Returns the fee applied when doing flash loans. By default this
+     * implementation has 0 fees. This function can be overloaded to make
+     * the flash loan mechanism deflationary.
+     * @param token The token to be flash loaned.
+     * @param amount The amount of tokens to be loaned.
+     * @return The fees applied to the corresponding flash loan.
+     */
+    function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {
+        // silence warning about unused variable without the addition of bytecode.
+        token;
+        amount;
+        return 0;
+    }
+
+    /**
+     * @dev Returns the receiver address of the flash fee. By default this
+     * implementation returns the address(0) which means the fee amount will be burnt.
+     * This function can be overloaded to change the fee receiver.
+     * @return The address for which the flash fee will be sent to.
+     */
+    function _flashFeeReceiver() internal view virtual returns (address) {
+        return address(0);
+    }
+
+    /**
+     * @dev Performs a flash loan. New tokens are minted and sent to the
+     * `receiver`, who is required to implement the {IERC3156FlashBorrower}
+     * interface. By the end of the flash loan, the receiver is expected to own
+     * amount + fee tokens and have them approved back to the token contract itself so
+     * they can be burned.
+     * @param receiver The receiver of the flash loan. Should implement the
+     * {IERC3156FlashBorrower-onFlashLoan} interface.
+     * @param token The token to be flash loaned. Only `address(this)` is
+     * supported.
+     * @param amount The amount of tokens to be loaned.
+     * @param data An arbitrary datafield that is passed to the receiver.
+     * @return `true` if the flash loan was successful.
+     */
+    // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount
+    // minted at the beginning is always recovered and burned at the end, or else the entire function will revert.
+    // slither-disable-next-line reentrancy-no-eth
+    function flashLoan(
+        IERC3156FlashBorrower receiver,
+        address token,
+        uint256 amount,
+        bytes calldata data
+    ) public virtual override returns (bool) {
+        require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan");
+        uint256 fee = flashFee(token, amount);
+        _mint(address(receiver), amount);
+        require(
+            receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,
+            "ERC20FlashMint: invalid return value"
+        );
+        address flashFeeReceiver = _flashFeeReceiver();
+        _spendAllowance(address(receiver), address(this), amount + fee);
+        if (fee == 0 || flashFeeReceiver == address(0)) {
+            _burn(address(receiver), amount + fee);
+        } else {
+            _burn(address(receiver), amount);
+            _transfer(address(receiver), flashFeeReceiver, fee);
+        }
+        return true;
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC20Votes.sol)
+
+pragma solidity ^0.8.0;
+
+import "./draft-ERC20Permit.sol";
+import "../../../utils/math/Math.sol";
+import "../../../governance/utils/IVotes.sol";
+import "../../../utils/math/SafeCast.sol";
+import "../../../utils/cryptography/ECDSA.sol";
+
+/**
+ * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's,
+ * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1.
+ *
+ * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module.
+ *
+ * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
+ * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
+ * power can be queried through the public accessors {getVotes} and {getPastVotes}.
+ *
+ * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
+ * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
+ *
+ * _Available since v4.2._
+ */
+abstract contract ERC20Votes is IVotes, ERC20Permit {
+    struct Checkpoint {
+        uint32 fromBlock;
+        uint224 votes;
+    }
+
+    bytes32 private constant _DELEGATION_TYPEHASH =
+        keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
+
+    mapping(address => address) private _delegates;
+    mapping(address => Checkpoint[]) private _checkpoints;
+    Checkpoint[] private _totalSupplyCheckpoints;
+
+    /**
+     * @dev Get the `pos`-th checkpoint for `account`.
+     */
+    function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) {
+        return _checkpoints[account][pos];
+    }
+
+    /**
+     * @dev Get number of checkpoints for `account`.
+     */
+    function numCheckpoints(address account) public view virtual returns (uint32) {
+        return SafeCast.toUint32(_checkpoints[account].length);
+    }
+
+    /**
+     * @dev Get the address `account` is currently delegating to.
+     */
+    function delegates(address account) public view virtual override returns (address) {
+        return _delegates[account];
+    }
+
+    /**
+     * @dev Gets the current votes balance for `account`
+     */
+    function getVotes(address account) public view virtual override returns (uint256) {
+        uint256 pos = _checkpoints[account].length;
+        return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
+    }
+
+    /**
+     * @dev Retrieve the number of votes for `account` at the end of `blockNumber`.
+     *
+     * Requirements:
+     *
+     * - `blockNumber` must have been already mined
+     */
+    function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
+        require(blockNumber < block.number, "ERC20Votes: block not yet mined");
+        return _checkpointsLookup(_checkpoints[account], blockNumber);
+    }
+
+    /**
+     * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
+     * It is but NOT the sum of all the delegated votes!
+     *
+     * Requirements:
+     *
+     * - `blockNumber` must have been already mined
+     */
+    function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
+        require(blockNumber < block.number, "ERC20Votes: block not yet mined");
+        return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
+    }
+
+    /**
+     * @dev Lookup a value in a list of (sorted) checkpoints.
+     */
+    function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) {
+        // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
+        //
+        // Initially we check if the block is recent to narrow the search range.
+        // During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
+        // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
+        // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
+        // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
+        // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
+        // out of bounds (in which case we're looking too far in the past and the result is 0).
+        // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
+        // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
+        // the same.
+        uint256 length = ckpts.length;
+
+        uint256 low = 0;
+        uint256 high = length;
+
+        if (length > 5) {
+            uint256 mid = length - Math.sqrt(length);
+            if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
+                high = mid;
+            } else {
+                low = mid + 1;
+            }
+        }
+
+        while (low < high) {
+            uint256 mid = Math.average(low, high);
+            if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
+                high = mid;
+            } else {
+                low = mid + 1;
+            }
+        }
+
+        return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes;
+    }
+
+    /**
+     * @dev Delegate votes from the sender to `delegatee`.
+     */
+    function delegate(address delegatee) public virtual override {
+        _delegate(_msgSender(), delegatee);
+    }
+
+    /**
+     * @dev Delegates votes from signer to `delegatee`
+     */
+    function delegateBySig(
+        address delegatee,
+        uint256 nonce,
+        uint256 expiry,
+        uint8 v,
+        bytes32 r,
+        bytes32 s
+    ) public virtual override {
+        require(block.timestamp <= expiry, "ERC20Votes: signature expired");
+        address signer = ECDSA.recover(
+            _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
+            v,
+            r,
+            s
+        );
+        require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
+        _delegate(signer, delegatee);
+    }
+
+    /**
+     * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1).
+     */
+    function _maxSupply() internal view virtual returns (uint224) {
+        return type(uint224).max;
+    }
+
+    /**
+     * @dev Snapshots the totalSupply after it has been increased.
+     */
+    function _mint(address account, uint256 amount) internal virtual override {
+        super._mint(account, amount);
+        require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes");
+
+        _writeCheckpoint(_totalSupplyCheckpoints, _add, amount);
+    }
+
+    /**
+     * @dev Snapshots the totalSupply after it has been decreased.
+     */
+    function _burn(address account, uint256 amount) internal virtual override {
+        super._burn(account, amount);
+
+        _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount);
+    }
+
+    /**
+     * @dev Move voting power when tokens are transferred.
+     *
+     * Emits a {IVotes-DelegateVotesChanged} event.
+     */
+    function _afterTokenTransfer(
+        address from,
+        address to,
+        uint256 amount
+    ) internal virtual override {
+        super._afterTokenTransfer(from, to, amount);
+
+        _moveVotingPower(delegates(from), delegates(to), amount);
+    }
+
+    /**
+     * @dev Change delegation for `delegator` to `delegatee`.
+     *
+     * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}.
+     */
+    function _delegate(address delegator, address delegatee) internal virtual {
+        address currentDelegate = delegates(delegator);
+        uint256 delegatorBalance = balanceOf(delegator);
+        _delegates[delegator] = delegatee;
+
+        emit DelegateChanged(delegator, currentDelegate, delegatee);
+
+        _moveVotingPower(currentDelegate, delegatee, delegatorBalance);
+    }
+
+    function _moveVotingPower(
+        address src,
+        address dst,
+        uint256 amount
+    ) private {
+        if (src != dst && amount > 0) {
+            if (src != address(0)) {
+                (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
+                emit DelegateVotesChanged(src, oldWeight, newWeight);
+            }
+
+            if (dst != address(0)) {
+                (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
+                emit DelegateVotesChanged(dst, oldWeight, newWeight);
+            }
+        }
+    }
+
+    function _writeCheckpoint(
+        Checkpoint[] storage ckpts,
+        function(uint256, uint256) view returns (uint256) op,
+        uint256 delta
+    ) private returns (uint256 oldWeight, uint256 newWeight) {
+        uint256 pos = ckpts.length;
+
+        Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1);
+
+        oldWeight = oldCkpt.votes;
+        newWeight = op(oldWeight, delta);
+
+        if (pos > 0 && oldCkpt.fromBlock == block.number) {
+            _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight);
+        } else {
+            ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)}));
+        }
+    }
+
+    function _add(uint256 a, uint256 b) private pure returns (uint256) {
+        return a + b;
+    }
+
+    function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
+        return a - b;
+    }
+
+    /**
+     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
+     */
+    function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) {
+        assembly {
+            mstore(0, ckpts.slot)
+            result.slot := add(keccak256(0, 0x20), pos)
+        }
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
+
+pragma solidity ^0.8.0;
+
+import "../IERC20.sol";
+
+/**
+ * @dev Interface for the optional metadata functions from the ERC20 standard.
+ *
+ * _Available since v4.1._
+ */
+interface IERC20Metadata is IERC20 {
+    /**
+     * @dev Returns the name of the token.
+     */
+    function name() external view returns (string memory);
+
+    /**
+     * @dev Returns the symbol of the token.
+     */
+    function symbol() external view returns (string memory);
+
+    /**
+     * @dev Returns the decimals places of the token.
+     */
+    function decimals() external view returns (uint8);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)
+
+pragma solidity ^0.8.0;
+
+import "./draft-IERC20Permit.sol";
+import "../ERC20.sol";
+import "../../../utils/cryptography/ECDSA.sol";
+import "../../../utils/cryptography/EIP712.sol";
+import "../../../utils/Counters.sol";
+
+/**
+ * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
+ * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
+ *
+ * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
+ * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
+ * need to send a transaction, and thus is not required to hold Ether at all.
+ *
+ * _Available since v3.4._
+ */
+abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
+    using Counters for Counters.Counter;
+
+    mapping(address => Counters.Counter) private _nonces;
+
+    // solhint-disable-next-line var-name-mixedcase
+    bytes32 private constant _PERMIT_TYPEHASH =
+        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
+    /**
+     * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
+     * However, to ensure consistency with the upgradeable transpiler, we will continue
+     * to reserve a slot.
+     * @custom:oz-renamed-from _PERMIT_TYPEHASH
+     */
+    // solhint-disable-next-line var-name-mixedcase
+    bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
+
+    /**
+     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
+     *
+     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
+     */
+    constructor(string memory name) EIP712(name, "1") {}
+
+    /**
+     * @dev See {IERC20Permit-permit}.
+     */
+    function permit(
+        address owner,
+        address spender,
+        uint256 value,
+        uint256 deadline,
+        uint8 v,
+        bytes32 r,
+        bytes32 s
+    ) public virtual override {
+        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
+
+        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
+
+        bytes32 hash = _hashTypedDataV4(structHash);
+
+        address signer = ECDSA.recover(hash, v, r, s);
+        require(signer == owner, "ERC20Permit: invalid signature");
+
+        _approve(owner, spender, value);
+    }
+
+    /**
+     * @dev See {IERC20Permit-nonces}.
+     */
+    function nonces(address owner) public view virtual override returns (uint256) {
+        return _nonces[owner].current();
+    }
+
+    /**
+     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
+     */
+    // solhint-disable-next-line func-name-mixedcase
+    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
+        return _domainSeparatorV4();
+    }
+
+    /**
+     * @dev "Consume a nonce": return the current value and increment.
+     *
+     * _Available since v4.1._
+     */
+    function _useNonce(address owner) internal virtual returns (uint256 current) {
+        Counters.Counter storage nonce = _nonces[owner];
+        current = nonce.current();
+        nonce.increment();
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
+ * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
+ *
+ * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
+ * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
+ * need to send a transaction, and thus is not required to hold Ether at all.
+ */
+interface IERC20Permit {
+    /**
+     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
+     * given ``owner``'s signed approval.
+     *
+     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
+     * ordering also apply here.
+     *
+     * Emits an {Approval} event.
+     *
+     * Requirements:
+     *
+     * - `spender` cannot be the zero address.
+     * - `deadline` must be a timestamp in the future.
+     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
+     * over the EIP712-formatted function arguments.
+     * - the signature must use ``owner``'s current nonce (see {nonces}).
+     *
+     * For more information on the signature format, see the
+     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
+     * section].
+     */
+    function permit(
+        address owner,
+        address spender,
+        uint256 value,
+        uint256 deadline,
+        uint8 v,
+        bytes32 r,
+        bytes32 s
+    ) external;
+
+    /**
+     * @dev Returns the current nonce for `owner`. This value must be
+     * included whenever a signature is generated for {permit}.
+     *
+     * Every successful call to {permit} increases ``owner``'s nonce by one. This
+     * prevents a signature from being used multiple times.
+     */
+    function nonces(address owner) external view returns (uint256);
+
+    /**
+     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
+     */
+    // solhint-disable-next-line func-name-mixedcase
+    function DOMAIN_SEPARATOR() external view returns (bytes32);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Context.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Context.sol
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Provides information about the current execution context, including the
+ * sender of the transaction and its data. While these are generally available
+ * via msg.sender and msg.data, they should not be accessed in such a direct
+ * manner, since when dealing with meta-transactions the account sending and
+ * paying for execution may not be the actual sender (as far as an application
+ * is concerned).
+ *
+ * This contract is only required for intermediate, library-like contracts.
+ */
+abstract contract Context {
+    function _msgSender() internal view virtual returns (address) {
+        return msg.sender;
+    }
+
+    function _msgData() internal view virtual returns (bytes calldata) {
+        return msg.data;
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Counters.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Counters.sol
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @title Counters
+ * @author Matt Condon (@shrugs)
+ * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
+ * of elements in a mapping, issuing ERC721 ids, or counting request ids.
+ *
+ * Include with `using Counters for Counters.Counter;`
+ */
+library Counters {
+    struct Counter {
+        // This variable should never be directly accessed by users of the library: interactions must be restricted to
+        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
+        // this feature: see https://github.com/ethereum/solidity/issues/4637
+        uint256 _value; // default: 0
+    }
+
+    function current(Counter storage counter) internal view returns (uint256) {
+        return counter._value;
+    }
+
+    function increment(Counter storage counter) internal {
+        unchecked {
+            counter._value += 1;
+        }
+    }
+
+    function decrement(Counter storage counter) internal {
+        uint256 value = counter._value;
+        require(value > 0, "Counter: decrement overflow");
+        unchecked {
+            counter._value = value - 1;
+        }
+    }
+
+    function reset(Counter storage counter) internal {
+        counter._value = 0;
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Strings.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Strings.sol
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
+
+pragma solidity ^0.8.0;
+
+import "./math/Math.sol";
+
+/**
+ * @dev String operations.
+ */
+library Strings {
+    bytes16 private constant _SYMBOLS = "0123456789abcdef";
+    uint8 private constant _ADDRESS_LENGTH = 20;
+
+    /**
+     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
+     */
+    function toString(uint256 value) internal pure returns (string memory) {
+        unchecked {
+            uint256 length = Math.log10(value) + 1;
+            string memory buffer = new string(length);
+            uint256 ptr;
+            /// @solidity memory-safe-assembly
+            assembly {
+                ptr := add(buffer, add(32, length))
+            }
+            while (true) {
+                ptr--;
+                /// @solidity memory-safe-assembly
+                assembly {
+                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
+                }
+                value /= 10;
+                if (value == 0) break;
+            }
+            return buffer;
+        }
+    }
+
+    /**
+     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
+     */
+    function toHexString(uint256 value) internal pure returns (string memory) {
+        unchecked {
+            return toHexString(value, Math.log256(value) + 1);
+        }
+    }
+
+    /**
+     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
+     */
+    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
+        bytes memory buffer = new bytes(2 * length + 2);
+        buffer[0] = "0";
+        buffer[1] = "x";
+        for (uint256 i = 2 * length + 1; i > 1; --i) {
+            buffer[i] = _SYMBOLS[value & 0xf];
+            value >>= 4;
+        }
+        require(value == 0, "Strings: hex length insufficient");
+        return string(buffer);
+    }
+
+    /**
+     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
+     */
+    function toHexString(address addr) internal pure returns (string memory) {
+        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/ECDSA.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/ECDSA.sol
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
+
+pragma solidity ^0.8.0;
+
+import "../Strings.sol";
+
+/**
+ * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
+ *
+ * These functions can be used to verify that a message was signed by the holder
+ * of the private keys of a given address.
+ */
+library ECDSA {
+    enum RecoverError {
+        NoError,
+        InvalidSignature,
+        InvalidSignatureLength,
+        InvalidSignatureS,
+        InvalidSignatureV // Deprecated in v4.8
+    }
+
+    function _throwError(RecoverError error) private pure {
+        if (error == RecoverError.NoError) {
+            return; // no error: do nothing
+        } else if (error == RecoverError.InvalidSignature) {
+            revert("ECDSA: invalid signature");
+        } else if (error == RecoverError.InvalidSignatureLength) {
+            revert("ECDSA: invalid signature length");
+        } else if (error == RecoverError.InvalidSignatureS) {
+            revert("ECDSA: invalid signature 's' value");
+        }
+    }
+
+    /**
+     * @dev Returns the address that signed a hashed message (`hash`) with
+     * `signature` or error string. This address can then be used for verification purposes.
+     *
+     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
+     * this function rejects them by requiring the `s` value to be in the lower
+     * half order, and the `v` value to be either 27 or 28.
+     *
+     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
+     * verification to be secure: it is possible to craft signatures that
+     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
+     * this is by receiving a hash of the original message (which may otherwise
+     * be too long), and then calling {toEthSignedMessageHash} on it.
+     *
+     * Documentation for signature generation:
+     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
+     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
+     *
+     * _Available since v4.3._
+     */
+    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
+        if (signature.length == 65) {
+            bytes32 r;
+            bytes32 s;
+            uint8 v;
+            // ecrecover takes the signature parameters, and the only way to get them
+            // currently is to use assembly.
+            /// @solidity memory-safe-assembly
+            assembly {
+                r := mload(add(signature, 0x20))
+                s := mload(add(signature, 0x40))
+                v := byte(0, mload(add(signature, 0x60)))
+            }
+            return tryRecover(hash, v, r, s);
+        } else {
+            return (address(0), RecoverError.InvalidSignatureLength);
+        }
+    }
+
+    /**
+     * @dev Returns the address that signed a hashed message (`hash`) with
+     * `signature`. This address can then be used for verification purposes.
+     *
+     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
+     * this function rejects them by requiring the `s` value to be in the lower
+     * half order, and the `v` value to be either 27 or 28.
+     *
+     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
+     * verification to be secure: it is possible to craft signatures that
+     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
+     * this is by receiving a hash of the original message (which may otherwise
+     * be too long), and then calling {toEthSignedMessageHash} on it.
+     */
+    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
+        (address recovered, RecoverError error) = tryRecover(hash, signature);
+        _throwError(error);
+        return recovered;
+    }
+
+    /**
+     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
+     *
+     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
+     *
+     * _Available since v4.3._
+     */
+    function tryRecover(
+        bytes32 hash,
+        bytes32 r,
+        bytes32 vs
+    ) internal pure returns (address, RecoverError) {
+        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
+        uint8 v = uint8((uint256(vs) >> 255) + 27);
+        return tryRecover(hash, v, r, s);
+    }
+
+    /**
+     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
+     *
+     * _Available since v4.2._
+     */
+    function recover(
+        bytes32 hash,
+        bytes32 r,
+        bytes32 vs
+    ) internal pure returns (address) {
+        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
+        _throwError(error);
+        return recovered;
+    }
+
+    /**
+     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
+     * `r` and `s` signature fields separately.
+     *
+     * _Available since v4.3._
+     */
+    function tryRecover(
+        bytes32 hash,
+        uint8 v,
+        bytes32 r,
+        bytes32 s
+    ) internal pure returns (address, RecoverError) {
+        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
+        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
+        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
+        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
+        //
+        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
+        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
+        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
+        // these malleable signatures as well.
+        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
+            return (address(0), RecoverError.InvalidSignatureS);
+        }
+
+        // If the signature is valid (and not malleable), return the signer address
+        address signer = ecrecover(hash, v, r, s);
+        if (signer == address(0)) {
+            return (address(0), RecoverError.InvalidSignature);
+        }
+
+        return (signer, RecoverError.NoError);
+    }
+
+    /**
+     * @dev Overload of {ECDSA-recover} that receives the `v`,
+     * `r` and `s` signature fields separately.
+     */
+    function recover(
+        bytes32 hash,
+        uint8 v,
+        bytes32 r,
+        bytes32 s
+    ) internal pure returns (address) {
+        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
+        _throwError(error);
+        return recovered;
+    }
+
+    /**
+     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
+     * produces hash corresponding to the one signed with the
+     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
+     * JSON-RPC method as part of EIP-191.
+     *
+     * See {recover}.
+     */
+    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
+        // 32 is the length in bytes of hash,
+        // enforced by the type signature above
+        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
+    }
+
+    /**
+     * @dev Returns an Ethereum Signed Message, created from `s`. This
+     * produces hash corresponding to the one signed with the
+     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
+     * JSON-RPC method as part of EIP-191.
+     *
+     * See {recover}.
+     */
+    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
+        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
+    }
+
+    /**
+     * @dev Returns an Ethereum Signed Typed Data, created from a
+     * `domainSeparator` and a `structHash`. This produces hash corresponding
+     * to the one signed with the
+     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
+     * JSON-RPC method as part of EIP-712.
+     *
+     * See {recover}.
+     */
+    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
+        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/EIP712.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/EIP712.sol
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
+
+pragma solidity ^0.8.0;
+
+import "./ECDSA.sol";
+
+/**
+ * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
+ *
+ * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
+ * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
+ * they need in their contracts using a combination of `abi.encode` and `keccak256`.
+ *
+ * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
+ * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
+ * ({_hashTypedDataV4}).
+ *
+ * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
+ * the chain id to protect against replay attacks on an eventual fork of the chain.
+ *
+ * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
+ * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
+ *
+ * _Available since v3.4._
+ */
+abstract contract EIP712 {
+    /* solhint-disable var-name-mixedcase */
+    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
+    // invalidate the cached domain separator if the chain id changes.
+    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
+    uint256 private immutable _CACHED_CHAIN_ID;
+    address private immutable _CACHED_THIS;
+
+    bytes32 private immutable _HASHED_NAME;
+    bytes32 private immutable _HASHED_VERSION;
+    bytes32 private immutable _TYPE_HASH;
+
+    /* solhint-enable var-name-mixedcase */
+
+    /**
+     * @dev Initializes the domain separator and parameter caches.
+     *
+     * The meaning of `name` and `version` is specified in
+     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
+     *
+     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
+     * - `version`: the current major version of the signing domain.
+     *
+     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
+     * contract upgrade].
+     */
+    constructor(string memory name, string memory version) {
+        bytes32 hashedName = keccak256(bytes(name));
+        bytes32 hashedVersion = keccak256(bytes(version));
+        bytes32 typeHash = keccak256(
+            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
+        );
+        _HASHED_NAME = hashedName;
+        _HASHED_VERSION = hashedVersion;
+        _CACHED_CHAIN_ID = block.chainid;
+        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
+        _CACHED_THIS = address(this);
+        _TYPE_HASH = typeHash;
+    }
+
+    /**
+     * @dev Returns the domain separator for the current chain.
+     */
+    function _domainSeparatorV4() internal view returns (bytes32) {
+        if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
+            return _CACHED_DOMAIN_SEPARATOR;
+        } else {
+            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
+        }
+    }
+
+    function _buildDomainSeparator(
+        bytes32 typeHash,
+        bytes32 nameHash,
+        bytes32 versionHash
+    ) private view returns (bytes32) {
+        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
+    }
+
+    /**
+     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
+     * function returns the hash of the fully encoded EIP712 message for this domain.
+     *
+     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
+     *
+     * ```solidity
+     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
+     *     keccak256("Mail(address to,string contents)"),
+     *     mailTo,
+     *     keccak256(bytes(mailContents))
+     * )));
+     * address signer = ECDSA.recover(digest, signature);
+     * ```
+     */
+    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
+        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/Math.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/Math.sol
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Standard math utilities missing in the Solidity language.
+ */
+library Math {
+    enum Rounding {
+        Down, // Toward negative infinity
+        Up, // Toward infinity
+        Zero // Toward zero
+    }
+
+    /**
+     * @dev Returns the largest of two numbers.
+     */
+    function max(uint256 a, uint256 b) internal pure returns (uint256) {
+        return a > b ? a : b;
+    }
+
+    /**
+     * @dev Returns the smallest of two numbers.
+     */
+    function min(uint256 a, uint256 b) internal pure returns (uint256) {
+        return a < b ? a : b;
+    }
+
+    /**
+     * @dev Returns the average of two numbers. The result is rounded towards
+     * zero.
+     */
+    function average(uint256 a, uint256 b) internal pure returns (uint256) {
+        // (a + b) / 2 can overflow.
+        return (a & b) + (a ^ b) / 2;
+    }
+
+    /**
+     * @dev Returns the ceiling of the division of two numbers.
+     *
+     * This differs from standard division with `/` in that it rounds up instead
+     * of rounding down.
+     */
+    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
+        // (a + b - 1) / b can overflow on addition, so we distribute.
+        return a == 0 ? 0 : (a - 1) / b + 1;
+    }
+
+    /**
+     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
+     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
+     * with further edits by Uniswap Labs also under MIT license.
+     */
+    function mulDiv(
+        uint256 x,
+        uint256 y,
+        uint256 denominator
+    ) internal pure returns (uint256 result) {
+        unchecked {
+            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
+            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
+            // variables such that product = prod1 * 2^256 + prod0.
+            uint256 prod0; // Least significant 256 bits of the product
+            uint256 prod1; // Most significant 256 bits of the product
+            assembly {
+                let mm := mulmod(x, y, not(0))
+                prod0 := mul(x, y)
+                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
+            }
+
+            // Handle non-overflow cases, 256 by 256 division.
+            if (prod1 == 0) {
+                return prod0 / denominator;
+            }
+
+            // Make sure the result is less than 2^256. Also prevents denominator == 0.
+            require(denominator > prod1);
+
+            ///////////////////////////////////////////////
+            // 512 by 256 division.
+            ///////////////////////////////////////////////
+
+            // Make division exact by subtracting the remainder from [prod1 prod0].
+            uint256 remainder;
+            assembly {
+                // Compute remainder using mulmod.
+                remainder := mulmod(x, y, denominator)
+
+                // Subtract 256 bit number from 512 bit number.
+                prod1 := sub(prod1, gt(remainder, prod0))
+                prod0 := sub(prod0, remainder)
+            }
+
+            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
+            // See https://cs.stackexchange.com/q/138556/92363.
+
+            // Does not overflow because the denominator cannot be zero at this stage in the function.
+            uint256 twos = denominator & (~denominator + 1);
+            assembly {
+                // Divide denominator by twos.
+                denominator := div(denominator, twos)
+
+                // Divide [prod1 prod0] by twos.
+                prod0 := div(prod0, twos)
+
+                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
+                twos := add(div(sub(0, twos), twos), 1)
+            }
+
+            // Shift in bits from prod1 into prod0.
+            prod0 |= prod1 * twos;
+
+            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
+            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
+            // four bits. That is, denominator * inv = 1 mod 2^4.
+            uint256 inverse = (3 * denominator) ^ 2;
+
+            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
+            // in modular arithmetic, doubling the correct bits in each step.
+            inverse *= 2 - denominator * inverse; // inverse mod 2^8
+            inverse *= 2 - denominator * inverse; // inverse mod 2^16
+            inverse *= 2 - denominator * inverse; // inverse mod 2^32
+            inverse *= 2 - denominator * inverse; // inverse mod 2^64
+            inverse *= 2 - denominator * inverse; // inverse mod 2^128
+            inverse *= 2 - denominator * inverse; // inverse mod 2^256
+
+            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
+            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
+            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
+            // is no longer required.
+            result = prod0 * inverse;
+            return result;
+        }
+    }
+
+    /**
+     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
+     */
+    function mulDiv(
+        uint256 x,
+        uint256 y,
+        uint256 denominator,
+        Rounding rounding
+    ) internal pure returns (uint256) {
+        uint256 result = mulDiv(x, y, denominator);
+        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
+            result += 1;
+        }
+        return result;
+    }
+
+    /**
+     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
+     *
+     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
+     */
+    function sqrt(uint256 a) internal pure returns (uint256) {
+        if (a == 0) {
+            return 0;
+        }
+
+        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
+        //
+        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
+        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
+        //
+        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
+        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
+        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
+        //
+        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
+        uint256 result = 1 << (log2(a) >> 1);
+
+        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
+        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
+        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
+        // into the expected uint128 result.
+        unchecked {
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            return min(result, a / result);
+        }
+    }
+
+    /**
+     * @notice Calculates sqrt(a), following the selected rounding direction.
+     */
+    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = sqrt(a);
+            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
+        }
+    }
+
+    /**
+     * @dev Return the log in base 2, rounded down, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log2(uint256 value) internal pure returns (uint256) {
+        uint256 result = 0;
+        unchecked {
+            if (value >> 128 > 0) {
+                value >>= 128;
+                result += 128;
+            }
+            if (value >> 64 > 0) {
+                value >>= 64;
+                result += 64;
+            }
+            if (value >> 32 > 0) {
+                value >>= 32;
+                result += 32;
+            }
+            if (value >> 16 > 0) {
+                value >>= 16;
+                result += 16;
+            }
+            if (value >> 8 > 0) {
+                value >>= 8;
+                result += 8;
+            }
+            if (value >> 4 > 0) {
+                value >>= 4;
+                result += 4;
+            }
+            if (value >> 2 > 0) {
+                value >>= 2;
+                result += 2;
+            }
+            if (value >> 1 > 0) {
+                result += 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = log2(value);
+            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
+        }
+    }
+
+    /**
+     * @dev Return the log in base 10, rounded down, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log10(uint256 value) internal pure returns (uint256) {
+        uint256 result = 0;
+        unchecked {
+            if (value >= 10**64) {
+                value /= 10**64;
+                result += 64;
+            }
+            if (value >= 10**32) {
+                value /= 10**32;
+                result += 32;
+            }
+            if (value >= 10**16) {
+                value /= 10**16;
+                result += 16;
+            }
+            if (value >= 10**8) {
+                value /= 10**8;
+                result += 8;
+            }
+            if (value >= 10**4) {
+                value /= 10**4;
+                result += 4;
+            }
+            if (value >= 10**2) {
+                value /= 10**2;
+                result += 2;
+            }
+            if (value >= 10**1) {
+                result += 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = log10(value);
+            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
+        }
+    }
+
+    /**
+     * @dev Return the log in base 256, rounded down, of a positive value.
+     * Returns 0 if given 0.
+     *
+     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
+     */
+    function log256(uint256 value) internal pure returns (uint256) {
+        uint256 result = 0;
+        unchecked {
+            if (value >> 128 > 0) {
+                value >>= 128;
+                result += 16;
+            }
+            if (value >> 64 > 0) {
+                value >>= 64;
+                result += 8;
+            }
+            if (value >> 32 > 0) {
+                value >>= 32;
+                result += 4;
+            }
+            if (value >> 16 > 0) {
+                value >>= 16;
+                result += 2;
+            }
+            if (value >> 8 > 0) {
+                result += 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = log256(value);
+            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
+        }
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/SafeCast.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/SafeCast.sol
@@ -0,0 +1,1136 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
+// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
+ * checks.
+ *
+ * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
+ * easily result in undesired exploitation or bugs, since developers usually
+ * assume that overflows raise errors. `SafeCast` restores this intuition by
+ * reverting the transaction when such an operation overflows.
+ *
+ * Using this library instead of the unchecked operations eliminates an entire
+ * class of bugs, so it's recommended to use it always.
+ *
+ * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
+ * all math on `uint256` and `int256` and then downcasting.
+ */
+library SafeCast {
+    /**
+     * @dev Returns the downcasted uint248 from uint256, reverting on
+     * overflow (when the input is greater than largest uint248).
+     *
+     * Counterpart to Solidity's `uint248` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 248 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint248(uint256 value) internal pure returns (uint248) {
+        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
+        return uint248(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint240 from uint256, reverting on
+     * overflow (when the input is greater than largest uint240).
+     *
+     * Counterpart to Solidity's `uint240` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 240 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint240(uint256 value) internal pure returns (uint240) {
+        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
+        return uint240(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint232 from uint256, reverting on
+     * overflow (when the input is greater than largest uint232).
+     *
+     * Counterpart to Solidity's `uint232` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 232 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint232(uint256 value) internal pure returns (uint232) {
+        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
+        return uint232(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint224 from uint256, reverting on
+     * overflow (when the input is greater than largest uint224).
+     *
+     * Counterpart to Solidity's `uint224` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 224 bits
+     *
+     * _Available since v4.2._
+     */
+    function toUint224(uint256 value) internal pure returns (uint224) {
+        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
+        return uint224(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint216 from uint256, reverting on
+     * overflow (when the input is greater than largest uint216).
+     *
+     * Counterpart to Solidity's `uint216` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 216 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint216(uint256 value) internal pure returns (uint216) {
+        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
+        return uint216(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint208 from uint256, reverting on
+     * overflow (when the input is greater than largest uint208).
+     *
+     * Counterpart to Solidity's `uint208` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 208 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint208(uint256 value) internal pure returns (uint208) {
+        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
+        return uint208(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint200 from uint256, reverting on
+     * overflow (when the input is greater than largest uint200).
+     *
+     * Counterpart to Solidity's `uint200` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 200 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint200(uint256 value) internal pure returns (uint200) {
+        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
+        return uint200(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint192 from uint256, reverting on
+     * overflow (when the input is greater than largest uint192).
+     *
+     * Counterpart to Solidity's `uint192` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 192 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint192(uint256 value) internal pure returns (uint192) {
+        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
+        return uint192(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint184 from uint256, reverting on
+     * overflow (when the input is greater than largest uint184).
+     *
+     * Counterpart to Solidity's `uint184` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 184 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint184(uint256 value) internal pure returns (uint184) {
+        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
+        return uint184(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint176 from uint256, reverting on
+     * overflow (when the input is greater than largest uint176).
+     *
+     * Counterpart to Solidity's `uint176` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 176 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint176(uint256 value) internal pure returns (uint176) {
+        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
+        return uint176(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint168 from uint256, reverting on
+     * overflow (when the input is greater than largest uint168).
+     *
+     * Counterpart to Solidity's `uint168` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 168 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint168(uint256 value) internal pure returns (uint168) {
+        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
+        return uint168(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint160 from uint256, reverting on
+     * overflow (when the input is greater than largest uint160).
+     *
+     * Counterpart to Solidity's `uint160` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 160 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint160(uint256 value) internal pure returns (uint160) {
+        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
+        return uint160(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint152 from uint256, reverting on
+     * overflow (when the input is greater than largest uint152).
+     *
+     * Counterpart to Solidity's `uint152` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 152 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint152(uint256 value) internal pure returns (uint152) {
+        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
+        return uint152(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint144 from uint256, reverting on
+     * overflow (when the input is greater than largest uint144).
+     *
+     * Counterpart to Solidity's `uint144` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 144 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint144(uint256 value) internal pure returns (uint144) {
+        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
+        return uint144(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint136 from uint256, reverting on
+     * overflow (when the input is greater than largest uint136).
+     *
+     * Counterpart to Solidity's `uint136` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 136 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint136(uint256 value) internal pure returns (uint136) {
+        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
+        return uint136(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint128 from uint256, reverting on
+     * overflow (when the input is greater than largest uint128).
+     *
+     * Counterpart to Solidity's `uint128` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 128 bits
+     *
+     * _Available since v2.5._
+     */
+    function toUint128(uint256 value) internal pure returns (uint128) {
+        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
+        return uint128(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint120 from uint256, reverting on
+     * overflow (when the input is greater than largest uint120).
+     *
+     * Counterpart to Solidity's `uint120` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 120 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint120(uint256 value) internal pure returns (uint120) {
+        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
+        return uint120(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint112 from uint256, reverting on
+     * overflow (when the input is greater than largest uint112).
+     *
+     * Counterpart to Solidity's `uint112` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 112 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint112(uint256 value) internal pure returns (uint112) {
+        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
+        return uint112(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint104 from uint256, reverting on
+     * overflow (when the input is greater than largest uint104).
+     *
+     * Counterpart to Solidity's `uint104` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 104 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint104(uint256 value) internal pure returns (uint104) {
+        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
+        return uint104(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint96 from uint256, reverting on
+     * overflow (when the input is greater than largest uint96).
+     *
+     * Counterpart to Solidity's `uint96` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 96 bits
+     *
+     * _Available since v4.2._
+     */
+    function toUint96(uint256 value) internal pure returns (uint96) {
+        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
+        return uint96(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint88 from uint256, reverting on
+     * overflow (when the input is greater than largest uint88).
+     *
+     * Counterpart to Solidity's `uint88` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 88 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint88(uint256 value) internal pure returns (uint88) {
+        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
+        return uint88(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint80 from uint256, reverting on
+     * overflow (when the input is greater than largest uint80).
+     *
+     * Counterpart to Solidity's `uint80` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 80 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint80(uint256 value) internal pure returns (uint80) {
+        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
+        return uint80(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint72 from uint256, reverting on
+     * overflow (when the input is greater than largest uint72).
+     *
+     * Counterpart to Solidity's `uint72` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 72 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint72(uint256 value) internal pure returns (uint72) {
+        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
+        return uint72(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint64 from uint256, reverting on
+     * overflow (when the input is greater than largest uint64).
+     *
+     * Counterpart to Solidity's `uint64` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 64 bits
+     *
+     * _Available since v2.5._
+     */
+    function toUint64(uint256 value) internal pure returns (uint64) {
+        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
+        return uint64(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint56 from uint256, reverting on
+     * overflow (when the input is greater than largest uint56).
+     *
+     * Counterpart to Solidity's `uint56` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 56 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint56(uint256 value) internal pure returns (uint56) {
+        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
+        return uint56(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint48 from uint256, reverting on
+     * overflow (when the input is greater than largest uint48).
+     *
+     * Counterpart to Solidity's `uint48` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 48 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint48(uint256 value) internal pure returns (uint48) {
+        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
+        return uint48(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint40 from uint256, reverting on
+     * overflow (when the input is greater than largest uint40).
+     *
+     * Counterpart to Solidity's `uint40` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 40 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint40(uint256 value) internal pure returns (uint40) {
+        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
+        return uint40(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint32 from uint256, reverting on
+     * overflow (when the input is greater than largest uint32).
+     *
+     * Counterpart to Solidity's `uint32` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 32 bits
+     *
+     * _Available since v2.5._
+     */
+    function toUint32(uint256 value) internal pure returns (uint32) {
+        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
+        return uint32(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint24 from uint256, reverting on
+     * overflow (when the input is greater than largest uint24).
+     *
+     * Counterpart to Solidity's `uint24` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 24 bits
+     *
+     * _Available since v4.7._
+     */
+    function toUint24(uint256 value) internal pure returns (uint24) {
+        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
+        return uint24(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint16 from uint256, reverting on
+     * overflow (when the input is greater than largest uint16).
+     *
+     * Counterpart to Solidity's `uint16` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 16 bits
+     *
+     * _Available since v2.5._
+     */
+    function toUint16(uint256 value) internal pure returns (uint16) {
+        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
+        return uint16(value);
+    }
+
+    /**
+     * @dev Returns the downcasted uint8 from uint256, reverting on
+     * overflow (when the input is greater than largest uint8).
+     *
+     * Counterpart to Solidity's `uint8` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 8 bits
+     *
+     * _Available since v2.5._
+     */
+    function toUint8(uint256 value) internal pure returns (uint8) {
+        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
+        return uint8(value);
+    }
+
+    /**
+     * @dev Converts a signed int256 into an unsigned uint256.
+     *
+     * Requirements:
+     *
+     * - input must be greater than or equal to 0.
+     *
+     * _Available since v3.0._
+     */
+    function toUint256(int256 value) internal pure returns (uint256) {
+        require(value >= 0, "SafeCast: value must be positive");
+        return uint256(value);
+    }
+
+    /**
+     * @dev Returns the downcasted int248 from int256, reverting on
+     * overflow (when the input is less than smallest int248 or
+     * greater than largest int248).
+     *
+     * Counterpart to Solidity's `int248` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 248 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt248(int256 value) internal pure returns (int248 downcasted) {
+        downcasted = int248(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int240 from int256, reverting on
+     * overflow (when the input is less than smallest int240 or
+     * greater than largest int240).
+     *
+     * Counterpart to Solidity's `int240` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 240 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt240(int256 value) internal pure returns (int240 downcasted) {
+        downcasted = int240(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int232 from int256, reverting on
+     * overflow (when the input is less than smallest int232 or
+     * greater than largest int232).
+     *
+     * Counterpart to Solidity's `int232` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 232 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt232(int256 value) internal pure returns (int232 downcasted) {
+        downcasted = int232(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int224 from int256, reverting on
+     * overflow (when the input is less than smallest int224 or
+     * greater than largest int224).
+     *
+     * Counterpart to Solidity's `int224` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 224 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt224(int256 value) internal pure returns (int224 downcasted) {
+        downcasted = int224(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int216 from int256, reverting on
+     * overflow (when the input is less than smallest int216 or
+     * greater than largest int216).
+     *
+     * Counterpart to Solidity's `int216` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 216 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt216(int256 value) internal pure returns (int216 downcasted) {
+        downcasted = int216(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int208 from int256, reverting on
+     * overflow (when the input is less than smallest int208 or
+     * greater than largest int208).
+     *
+     * Counterpart to Solidity's `int208` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 208 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt208(int256 value) internal pure returns (int208 downcasted) {
+        downcasted = int208(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int200 from int256, reverting on
+     * overflow (when the input is less than smallest int200 or
+     * greater than largest int200).
+     *
+     * Counterpart to Solidity's `int200` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 200 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt200(int256 value) internal pure returns (int200 downcasted) {
+        downcasted = int200(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int192 from int256, reverting on
+     * overflow (when the input is less than smallest int192 or
+     * greater than largest int192).
+     *
+     * Counterpart to Solidity's `int192` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 192 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt192(int256 value) internal pure returns (int192 downcasted) {
+        downcasted = int192(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int184 from int256, reverting on
+     * overflow (when the input is less than smallest int184 or
+     * greater than largest int184).
+     *
+     * Counterpart to Solidity's `int184` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 184 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt184(int256 value) internal pure returns (int184 downcasted) {
+        downcasted = int184(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int176 from int256, reverting on
+     * overflow (when the input is less than smallest int176 or
+     * greater than largest int176).
+     *
+     * Counterpart to Solidity's `int176` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 176 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt176(int256 value) internal pure returns (int176 downcasted) {
+        downcasted = int176(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int168 from int256, reverting on
+     * overflow (when the input is less than smallest int168 or
+     * greater than largest int168).
+     *
+     * Counterpart to Solidity's `int168` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 168 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt168(int256 value) internal pure returns (int168 downcasted) {
+        downcasted = int168(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int160 from int256, reverting on
+     * overflow (when the input is less than smallest int160 or
+     * greater than largest int160).
+     *
+     * Counterpart to Solidity's `int160` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 160 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt160(int256 value) internal pure returns (int160 downcasted) {
+        downcasted = int160(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int152 from int256, reverting on
+     * overflow (when the input is less than smallest int152 or
+     * greater than largest int152).
+     *
+     * Counterpart to Solidity's `int152` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 152 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt152(int256 value) internal pure returns (int152 downcasted) {
+        downcasted = int152(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int144 from int256, reverting on
+     * overflow (when the input is less than smallest int144 or
+     * greater than largest int144).
+     *
+     * Counterpart to Solidity's `int144` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 144 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt144(int256 value) internal pure returns (int144 downcasted) {
+        downcasted = int144(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int136 from int256, reverting on
+     * overflow (when the input is less than smallest int136 or
+     * greater than largest int136).
+     *
+     * Counterpart to Solidity's `int136` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 136 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt136(int256 value) internal pure returns (int136 downcasted) {
+        downcasted = int136(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int128 from int256, reverting on
+     * overflow (when the input is less than smallest int128 or
+     * greater than largest int128).
+     *
+     * Counterpart to Solidity's `int128` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 128 bits
+     *
+     * _Available since v3.1._
+     */
+    function toInt128(int256 value) internal pure returns (int128 downcasted) {
+        downcasted = int128(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int120 from int256, reverting on
+     * overflow (when the input is less than smallest int120 or
+     * greater than largest int120).
+     *
+     * Counterpart to Solidity's `int120` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 120 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt120(int256 value) internal pure returns (int120 downcasted) {
+        downcasted = int120(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int112 from int256, reverting on
+     * overflow (when the input is less than smallest int112 or
+     * greater than largest int112).
+     *
+     * Counterpart to Solidity's `int112` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 112 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt112(int256 value) internal pure returns (int112 downcasted) {
+        downcasted = int112(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int104 from int256, reverting on
+     * overflow (when the input is less than smallest int104 or
+     * greater than largest int104).
+     *
+     * Counterpart to Solidity's `int104` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 104 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt104(int256 value) internal pure returns (int104 downcasted) {
+        downcasted = int104(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int96 from int256, reverting on
+     * overflow (when the input is less than smallest int96 or
+     * greater than largest int96).
+     *
+     * Counterpart to Solidity's `int96` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 96 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt96(int256 value) internal pure returns (int96 downcasted) {
+        downcasted = int96(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int88 from int256, reverting on
+     * overflow (when the input is less than smallest int88 or
+     * greater than largest int88).
+     *
+     * Counterpart to Solidity's `int88` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 88 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt88(int256 value) internal pure returns (int88 downcasted) {
+        downcasted = int88(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int80 from int256, reverting on
+     * overflow (when the input is less than smallest int80 or
+     * greater than largest int80).
+     *
+     * Counterpart to Solidity's `int80` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 80 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt80(int256 value) internal pure returns (int80 downcasted) {
+        downcasted = int80(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int72 from int256, reverting on
+     * overflow (when the input is less than smallest int72 or
+     * greater than largest int72).
+     *
+     * Counterpart to Solidity's `int72` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 72 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt72(int256 value) internal pure returns (int72 downcasted) {
+        downcasted = int72(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int64 from int256, reverting on
+     * overflow (when the input is less than smallest int64 or
+     * greater than largest int64).
+     *
+     * Counterpart to Solidity's `int64` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 64 bits
+     *
+     * _Available since v3.1._
+     */
+    function toInt64(int256 value) internal pure returns (int64 downcasted) {
+        downcasted = int64(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int56 from int256, reverting on
+     * overflow (when the input is less than smallest int56 or
+     * greater than largest int56).
+     *
+     * Counterpart to Solidity's `int56` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 56 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt56(int256 value) internal pure returns (int56 downcasted) {
+        downcasted = int56(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int48 from int256, reverting on
+     * overflow (when the input is less than smallest int48 or
+     * greater than largest int48).
+     *
+     * Counterpart to Solidity's `int48` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 48 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt48(int256 value) internal pure returns (int48 downcasted) {
+        downcasted = int48(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int40 from int256, reverting on
+     * overflow (when the input is less than smallest int40 or
+     * greater than largest int40).
+     *
+     * Counterpart to Solidity's `int40` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 40 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt40(int256 value) internal pure returns (int40 downcasted) {
+        downcasted = int40(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int32 from int256, reverting on
+     * overflow (when the input is less than smallest int32 or
+     * greater than largest int32).
+     *
+     * Counterpart to Solidity's `int32` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 32 bits
+     *
+     * _Available since v3.1._
+     */
+    function toInt32(int256 value) internal pure returns (int32 downcasted) {
+        downcasted = int32(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int24 from int256, reverting on
+     * overflow (when the input is less than smallest int24 or
+     * greater than largest int24).
+     *
+     * Counterpart to Solidity's `int24` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 24 bits
+     *
+     * _Available since v4.7._
+     */
+    function toInt24(int256 value) internal pure returns (int24 downcasted) {
+        downcasted = int24(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int16 from int256, reverting on
+     * overflow (when the input is less than smallest int16 or
+     * greater than largest int16).
+     *
+     * Counterpart to Solidity's `int16` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 16 bits
+     *
+     * _Available since v3.1._
+     */
+    function toInt16(int256 value) internal pure returns (int16 downcasted) {
+        downcasted = int16(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
+    }
+
+    /**
+     * @dev Returns the downcasted int8 from int256, reverting on
+     * overflow (when the input is less than smallest int8 or
+     * greater than largest int8).
+     *
+     * Counterpart to Solidity's `int8` operator.
+     *
+     * Requirements:
+     *
+     * - input must fit into 8 bits
+     *
+     * _Available since v3.1._
+     */
+    function toInt8(int256 value) internal pure returns (int8 downcasted) {
+        downcasted = int8(value);
+        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
+    }
+
+    /**
+     * @dev Converts an unsigned uint256 into a signed int256.
+     *
+     * Requirements:
+     *
+     * - input must be less than or equal to maxInt256.
+     *
+     * _Available since v3.0._
+     */
+    function toInt256(uint256 value) internal pure returns (int256) {
+        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
+        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
+        return int256(value);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/ZeppelinContract.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/ZeppelinContract.sol
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.9;
+
+import "./@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import "./@openzeppelin/contracts/security/Pausable.sol";
+import "./@openzeppelin/contracts/access/Ownable.sol";
+import "./@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
+import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
+import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol";
+
+contract ZeppelinContract is ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint {
+    constructor()
+        ERC20("ZeppelinContract", "UNQ")
+        ERC20Permit("ZeppelinContract")
+    {}
+
+    function pause() public onlyOwner {
+        _pause();
+    }
+
+    function unpause() public onlyOwner {
+        _unpause();
+    }
+
+    function mint(address to, uint256 amount) public onlyOwner {
+        _mint(to, amount);
+    }
+
+    function _beforeTokenTransfer(address from, address to, uint256 amount)
+        internal
+        whenNotPaused
+        override
+    {
+        super._beforeTokenTransfer(from, to, amount);
+    }
+
+    // The following functions are overrides required by Solidity.
+
+    function _afterTokenTransfer(address from, address to, uint256 amount)
+        internal
+        override(ERC20, ERC20Votes)
+    {
+        super._afterTokenTransfer(from, to, amount);
+    }
+
+    function _mint(address to, uint256 amount)
+        internal
+        override(ERC20, ERC20Votes)
+    {
+        super._mint(to, amount);
+    }
+
+    function _burn(address account, uint256 amount)
+        internal
+        override(ERC20, ERC20Votes)
+    {
+        super._burn(account, amount);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.abidiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.abi
@@ -0,0 +1 @@
+[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"pos","type":"uint32"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint224","name":"votes","type":"uint224"}],"internalType":"struct ERC20Votes.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.bindiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.bin
@@ -0,0 +1 @@
+6101406040523480156200001257600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525080604051806040016040528060018152602001603160f81b8152506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b8152508160039081620000ad919062000269565b506004620000bc828262000269565b50506005805460ff1916905550620000d4336200016a565b815160209283012081519183019190912060e08290526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818801819052818301969096526060810194909452608080850193909352308483018190528151808603909301835260c094850190915281519190950120905291909152610120525062000335565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620001ef57607f821691505b6020821081036200021057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200026457600081815260208120601f850160051c810160208610156200023f5750805b601f850160051c820191505b8181101562000260578281556001016200024b565b5050505b505050565b81516001600160401b03811115620002855762000285620001c4565b6200029d81620002968454620001da565b8462000216565b602080601f831160018114620002d55760008415620002bc5750858301515b600019600386901b1c1916600185901b17855562000260565b600085815260208120601f198616915b828110156200030657888601518255948401946001909101908401620002e5565b5085821015620003255787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e05161010051610120516125f06200038560003960006112ad015260006112fc015260006112d7015260006112300152600061125a0152600061128401526125f06000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806370a082311161011a5780639ab24eb0116100ad578063d505accf1161007c578063d505accf1461046a578063d9d98ce41461047d578063dd62ed3e14610490578063f1127ed8146104a3578063f2fde38b146104e057600080fd5b80639ab24eb01461041e578063a457c2d714610431578063a9059cbb14610444578063c3cda5201461045757600080fd5b80638456cb59116100e95780638456cb59146103e55780638da5cb5b146103ed5780638e539e8c1461040357806395d89b411461041657600080fd5b806370a082311461038e578063715018a6146103b757806379cc6790146103bf5780637ecebe00146103d257600080fd5b80633f4ba83a1161019d5780635c19a95c1161016c5780635c19a95c146103225780635c975abb146103355780635cffe9de14610340578063613255ab146103535780636fcfff451461036657600080fd5b80633f4ba83a146102ae57806340c10f19146102b857806342966c68146102cb578063587cde1e146102de57600080fd5b8063313ce567116101d9578063313ce567146102715780633644e5151461028057806339509351146102885780633a46b1a81461029b57600080fd5b806306fdde031461020b578063095ea7b31461022957806318160ddd1461024c57806323b872dd1461025e575b600080fd5b6102136104f3565b604051610220919061217e565b60405180910390f35b61023c6102373660046121e1565b610585565b6040519015158152602001610220565b6002545b604051908152602001610220565b61023c61026c36600461220d565b61059f565b60405160128152602001610220565b6102506105c3565b61023c6102963660046121e1565b6105d2565b6102506102a93660046121e1565b6105f4565b6102b6610673565b005b6102b66102c63660046121e1565b610685565b6102b66102d936600461224e565b61069b565b61030a6102ec366004612267565b6001600160a01b039081166000908152600860205260409020541690565b6040516001600160a01b039091168152602001610220565b6102b6610330366004612267565b6106a8565b60055460ff1661023c565b61023c61034e366004612284565b6106b2565b610250610361366004612267565b610896565b610379610374366004612267565b6108be565b60405163ffffffff9091168152602001610220565b61025061039c366004612267565b6001600160a01b031660009081526020819052604090205490565b6102b66108e0565b6102b66103cd3660046121e1565b6108f2565b6102506103e0366004612267565b610907565b6102b6610925565b60055461010090046001600160a01b031661030a565b61025061041136600461224e565b610935565b610213610991565b61025061042c366004612267565b6109a0565b61023c61043f3660046121e1565b610a27565b61023c6104523660046121e1565b610aa2565b6102b6610465366004612339565b610ab0565b6102b6610478366004612393565b610be6565b61025061048b3660046121e1565b610d4a565b61025061049e366004612401565b610dab565b6104b66104b136600461243a565b610dd6565b60408051825163ffffffff1681526020928301516001600160e01b03169281019290925201610220565b6102b66104ee366004612267565b610e5a565b60606003805461050290612471565b80601f016020809104026020016040519081016040528092919081815260200182805461052e90612471565b801561057b5780601f106105505761010080835404028352916020019161057b565b820191906000526020600020905b81548152906001019060200180831161055e57829003601f168201915b5050505050905090565b600033610593818585610ed0565b60019150505b92915050565b6000336105ad858285610ff4565b6105b885858561106e565b506001949350505050565b60006105cd611223565b905090565b6000336105938185856105e58383610dab565b6105ef91906124bb565b610ed0565b600043821061064a5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e65640060448201526064015b60405180910390fd5b6001600160a01b038316600090815260096020526040902061066c908361134a565b9392505050565b61067b611441565b6106836114a1565b565b61068d611441565b61069782826114f3565b5050565b6106a533826114fd565b50565b6106a53382611507565b60006106bd85610896565b8411156107205760405162461bcd60e51b815260206004820152602b60248201527f4552433230466c6173684d696e743a20616d6f756e742065786365656473206d60448201526a30bc233630b9b42637b0b760a91b6064820152608401610641565b600061072c8686610d4a565b905061073887866114f3565b6040516323e30c8b60e01b81527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9906001600160a01b038916906323e30c8b906107909033908b908b9088908c908c906004016124ce565b6020604051808303816000875af11580156107af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d3919061252a565b1461082c5760405162461bcd60e51b8152602060048201526024808201527f4552433230466c6173684d696e743a20696e76616c69642072657475726e2076604482015263616c756560e01b6064820152608401610641565b6000610842883061083d858a6124bb565b610ff4565b81158061085657506001600160a01b038116155b156108735761086e8861086984896124bb565b6114fd565b610888565b61087d88876114fd565b61088888828461106e565b506001979650505050505050565b60006001600160a01b03821630146108af576000610599565b60025461059990600019612543565b6001600160a01b03811660009081526009602052604081205461059990611580565b6108e8611441565b61068360006115e9565b6108fd823383610ff4565b61069782826114fd565b6001600160a01b038116600090815260066020526040812054610599565b61092d611441565b610683611643565b60004382106109865760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610641565b610599600a8361134a565b60606004805461050290612471565b6001600160a01b0381166000908152600960205260408120548015610a14576001600160a01b03831660009081526009602052604090206109e2600183612543565b815481106109f2576109f2612556565b60009182526020909120015464010000000090046001600160e01b0316610a17565b60005b6001600160e01b03169392505050565b60003381610a358286610dab565b905083811015610a955760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610641565b6105b88286868403610ed0565b60003361059381858561106e565b83421115610b005760405162461bcd60e51b815260206004820152601d60248201527f4552433230566f7465733a207369676e617475726520657870697265640000006044820152606401610641565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038816918101919091526060810186905260808101859052600090610b7a90610b729060a00160405160208183030381529060405280519060200120611680565b8585856116ce565b9050610b85816116f6565b8614610bd35760405162461bcd60e51b815260206004820152601960248201527f4552433230566f7465733a20696e76616c6964206e6f6e6365000000000000006044820152606401610641565b610bdd8188611507565b50505050505050565b83421115610c365760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610641565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610c658c6116f6565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610cc082611680565b90506000610cd0828787876116ce565b9050896001600160a01b0316816001600160a01b031614610d335760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610641565b610d3e8a8a8a610ed0565b50505050505050505050565b60006001600160a01b0383163014610da45760405162461bcd60e51b815260206004820152601b60248201527f4552433230466c6173684d696e743a2077726f6e6720746f6b656e00000000006044820152606401610641565b600061066c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60408051808201909152600080825260208201526001600160a01b0383166000908152600960205260409020805463ffffffff8416908110610e1a57610e1a612556565b60009182526020918290206040805180820190915291015463ffffffff8116825264010000000090046001600160e01b0316918101919091529392505050565b610e62611441565b6001600160a01b038116610ec75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610641565b6106a5816115e9565b6001600160a01b038316610f325760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610641565b6001600160a01b038216610f935760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610641565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006110008484610dab565b90506000198114611068578181101561105b5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610641565b6110688484848403610ed0565b50505050565b6001600160a01b0383166110d25760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610641565b6001600160a01b0382166111345760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610641565b61113f83838361171e565b6001600160a01b038316600090815260208190526040902054818110156111b75760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610641565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a361106884848461172b565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561127c57507f000000000000000000000000000000000000000000000000000000000000000046145b156112a657507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b8154600090818160058111156113a457600061136584611736565b61136f9085612543565b600088815260209020909150869082015463ffffffff161115611394578091506113a2565b61139f8160016124bb565b92505b505b808210156113f15760006113b8838361181e565b600088815260209020909150869082015463ffffffff1611156113dd578091506113eb565b6113e88160016124bb565b92505b506113a4565b801561142b5761141486611406600184612543565b600091825260209091200190565b5464010000000090046001600160e01b031661142e565b60005b6001600160e01b03169695505050505050565b6005546001600160a01b036101009091041633146106835760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610641565b6114a9611839565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6106978282611882565b610697828261190c565b6001600160a01b038281166000818152600860208181526040808420805485845282862054949093528787166001600160a01b03198416811790915590519190951694919391928592917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4611068828483611924565b600063ffffffff8211156115e55760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610641565b5090565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b61164b611a61565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586114d63390565b600061059961168d611223565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b60008060006116df87878787611aa7565b915091506116ec81611b6b565b5095945050505050565b6001600160a01b03811660009081526006602052604090208054600181018255905b50919050565b611726611a61565b505050565b611726838383611cb5565b60008160000361174857506000919050565b6000600161175584611ce7565b901c6001901b9050600181848161176e5761176e61256c565b048201901c905060018184816117865761178661256c565b048201901c9050600181848161179e5761179e61256c565b048201901c905060018184816117b6576117b661256c565b048201901c905060018184816117ce576117ce61256c565b048201901c905060018184816117e6576117e661256c565b048201901c905060018184816117fe576117fe61256c565b048201901c905061066c818285816118185761181861256c565b04611d7b565b600061182d6002848418612582565b61066c908484166124bb565b60055460ff166106835760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610641565b61188c8282611d91565b6002546001600160e01b0310156118fe5760405162461bcd60e51b815260206004820152603060248201527f4552433230566f7465733a20746f74616c20737570706c79207269736b73206f60448201526f766572666c6f77696e6720766f74657360801b6064820152608401610641565b611068600a611e6483611e70565b6119168282611fc4565b611068600a61210983611e70565b816001600160a01b0316836001600160a01b0316141580156119465750600081115b15611726576001600160a01b038316156119d4576001600160a01b038316600090815260096020526040812081906119819061210985611e70565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516119c9929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615611726576001600160a01b03821660009081526009602052604081208190611a0a90611e6485611e70565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611a52929190918252602082015260400190565b60405180910390a25050505050565b60055460ff16156106835760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610641565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611ade5750600090506003611b62565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611b32573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611b5b57600060019250925050611b62565b9150600090505b94509492505050565b6000816004811115611b7f57611b7f6125a4565b03611b875750565b6001816004811115611b9b57611b9b6125a4565b03611be85760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610641565b6002816004811115611bfc57611bfc6125a4565b03611c495760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610641565b6003816004811115611c5d57611c5d6125a4565b036106a55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610641565b6001600160a01b0383811660009081526008602052604080822054858416835291205461172692918216911683611924565b600080608083901c15611cfc57608092831c92015b604083901c15611d0e57604092831c92015b602083901c15611d2057602092831c92015b601083901c15611d3257601092831c92015b600883901c15611d4457600892831c92015b600483901c15611d5657600492831c92015b600283901c15611d6857600292831c92015b600183901c156105995760010192915050565b6000818310611d8a578161066c565b5090919050565b6001600160a01b038216611de75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610641565b611df36000838361171e565b8060026000828254611e0591906124bb565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36106976000838361172b565b600061066c82846124bb565b82546000908190818115611ebd57611e8d87611406600185612543565b60408051808201909152905463ffffffff8116825264010000000090046001600160e01b03166020820152611ed2565b60408051808201909152600080825260208201525b905080602001516001600160e01b03169350611ef284868863ffffffff16565b9250600082118015611f0a5750805163ffffffff1643145b15611f4f57611f1883612115565b611f2788611406600186612543565b80546001600160e01b03929092166401000000000263ffffffff909216919091179055611fba565b866040518060400160405280611f6443611580565b63ffffffff168152602001611f7886612115565b6001600160e01b0390811690915282546001810184556000938452602093849020835194909301519091166401000000000263ffffffff909316929092179101555b5050935093915050565b6001600160a01b0382166120245760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610641565b6120308260008361171e565b6001600160a01b038216600090815260208190526040902054818110156120a45760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610641565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36117268360008461172b565b600061066c8284612543565b60006001600160e01b038211156115e55760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610641565b600060208083528351808285015260005b818110156121ab5785810183015185820160400152820161218f565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b03811681146106a557600080fd5b600080604083850312156121f457600080fd5b82356121ff816121cc565b946020939093013593505050565b60008060006060848603121561222257600080fd5b833561222d816121cc565b9250602084013561223d816121cc565b929592945050506040919091013590565b60006020828403121561226057600080fd5b5035919050565b60006020828403121561227957600080fd5b813561066c816121cc565b60008060008060006080868803121561229c57600080fd5b85356122a7816121cc565b945060208601356122b7816121cc565b935060408601359250606086013567ffffffffffffffff808211156122db57600080fd5b818801915088601f8301126122ef57600080fd5b8135818111156122fe57600080fd5b89602082850101111561231057600080fd5b9699959850939650602001949392505050565b803560ff8116811461233457600080fd5b919050565b60008060008060008060c0878903121561235257600080fd5b863561235d816121cc565b9550602087013594506040870135935061237960608801612323565b92506080870135915060a087013590509295509295509295565b600080600080600080600060e0888a0312156123ae57600080fd5b87356123b9816121cc565b965060208801356123c9816121cc565b955060408801359450606088013593506123e560808901612323565b925060a0880135915060c0880135905092959891949750929550565b6000806040838503121561241457600080fd5b823561241f816121cc565b9150602083013561242f816121cc565b809150509250929050565b6000806040838503121561244d57600080fd5b8235612458816121cc565b9150602083013563ffffffff8116811461242f57600080fd5b600181811c9082168061248557607f821691505b60208210810361171857634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610599576105996124a5565b6001600160a01b03878116825286166020820152604081018590526060810184905260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b60006020828403121561253c57600080fd5b5051919050565b81810381811115610599576105996124a5565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008261259f57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052602160045260246000fdfea264697066735822122057e08ed799a9773c2de4d8c40122801fc9994042a2e3e3ac01e5aa1c384b1c4b64736f6c63430008110033
\ No newline at end of file
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/README.mddiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/README.md
@@ -0,0 +1,12 @@
+# OpenZeppelin Contracts
+
+The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1.
+
+They are not meant to be edited.
+
+The originals can be found on [GitHub] and [npm].
+
+[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1
+[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1
+
+Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard).
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/access/Ownable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/access/Ownable.sol
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
+
+pragma solidity ^0.8.0;
+
+import "../utils/Context.sol";
+
+/**
+ * @dev Contract module which provides a basic access control mechanism, where
+ * there is an account (an owner) that can be granted exclusive access to
+ * specific functions.
+ *
+ * By default, the owner account will be the one that deploys the contract. This
+ * can later be changed with {transferOwnership}.
+ *
+ * This module is used through inheritance. It will make available the modifier
+ * `onlyOwner`, which can be applied to your functions to restrict their use to
+ * the owner.
+ */
+abstract contract Ownable is Context {
+    address private _owner;
+
+    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+
+    /**
+     * @dev Initializes the contract setting the deployer as the initial owner.
+     */
+    constructor() {
+        _transferOwnership(_msgSender());
+    }
+
+    /**
+     * @dev Throws if called by any account other than the owner.
+     */
+    modifier onlyOwner() {
+        _checkOwner();
+        _;
+    }
+
+    /**
+     * @dev Returns the address of the current owner.
+     */
+    function owner() public view virtual returns (address) {
+        return _owner;
+    }
+
+    /**
+     * @dev Throws if the sender is not the owner.
+     */
+    function _checkOwner() internal view virtual {
+        require(owner() == _msgSender(), "Ownable: caller is not the owner");
+    }
+
+    /**
+     * @dev Leaves the contract without owner. It will not be possible to call
+     * `onlyOwner` functions anymore. Can only be called by the current owner.
+     *
+     * NOTE: Renouncing ownership will leave the contract without an owner,
+     * thereby removing any functionality that is only available to the owner.
+     */
+    function renounceOwnership() public virtual onlyOwner {
+        _transferOwnership(address(0));
+    }
+
+    /**
+     * @dev Transfers ownership of the contract to a new account (`newOwner`).
+     * Can only be called by the current owner.
+     */
+    function transferOwnership(address newOwner) public virtual onlyOwner {
+        require(newOwner != address(0), "Ownable: new owner is the zero address");
+        _transferOwnership(newOwner);
+    }
+
+    /**
+     * @dev Transfers ownership of the contract to a new account (`newOwner`).
+     * Internal function without access restriction.
+     */
+    function _transferOwnership(address newOwner) internal virtual {
+        address oldOwner = _owner;
+        _owner = newOwner;
+        emit OwnershipTransferred(oldOwner, newOwner);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/ERC721.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/ERC721.sol
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol)
+
+pragma solidity ^0.8.0;
+
+import "./IERC721.sol";
+import "./IERC721Receiver.sol";
+import "./extensions/IERC721Metadata.sol";
+import "../../utils/Address.sol";
+import "../../utils/Context.sol";
+import "../../utils/Strings.sol";
+import "../../utils/introspection/ERC165.sol";
+
+/**
+ * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
+ * the Metadata extension, but not including the Enumerable extension, which is available separately as
+ * {ERC721Enumerable}.
+ */
+contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
+    using Address for address;
+    using Strings for uint256;
+
+    // Token name
+    string private _name;
+
+    // Token symbol
+    string private _symbol;
+
+    // Mapping from token ID to owner address
+    mapping(uint256 => address) private _owners;
+
+    // Mapping owner address to token count
+    mapping(address => uint256) private _balances;
+
+    // Mapping from token ID to approved address
+    mapping(uint256 => address) private _tokenApprovals;
+
+    // Mapping from owner to operator approvals
+    mapping(address => mapping(address => bool)) private _operatorApprovals;
+
+    /**
+     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
+     */
+    constructor(string memory name_, string memory symbol_) {
+        _name = name_;
+        _symbol = symbol_;
+    }
+
+    /**
+     * @dev See {IERC165-supportsInterface}.
+     */
+    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
+        return
+            interfaceId == type(IERC721).interfaceId ||
+            interfaceId == type(IERC721Metadata).interfaceId ||
+            super.supportsInterface(interfaceId);
+    }
+
+    /**
+     * @dev See {IERC721-balanceOf}.
+     */
+    function balanceOf(address owner) public view virtual override returns (uint256) {
+        require(owner != address(0), "ERC721: address zero is not a valid owner");
+        return _balances[owner];
+    }
+
+    /**
+     * @dev See {IERC721-ownerOf}.
+     */
+    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
+        address owner = _ownerOf(tokenId);
+        require(owner != address(0), "ERC721: invalid token ID");
+        return owner;
+    }
+
+    /**
+     * @dev See {IERC721Metadata-name}.
+     */
+    function name() public view virtual override returns (string memory) {
+        return _name;
+    }
+
+    /**
+     * @dev See {IERC721Metadata-symbol}.
+     */
+    function symbol() public view virtual override returns (string memory) {
+        return _symbol;
+    }
+
+    /**
+     * @dev See {IERC721Metadata-tokenURI}.
+     */
+    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
+        _requireMinted(tokenId);
+
+        string memory baseURI = _baseURI();
+        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
+    }
+
+    /**
+     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
+     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
+     * by default, can be overridden in child contracts.
+     */
+    function _baseURI() internal view virtual returns (string memory) {
+        return "";
+    }
+
+    /**
+     * @dev See {IERC721-approve}.
+     */
+    function approve(address to, uint256 tokenId) public virtual override {
+        address owner = ERC721.ownerOf(tokenId);
+        require(to != owner, "ERC721: approval to current owner");
+
+        require(
+            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
+            "ERC721: approve caller is not token owner or approved for all"
+        );
+
+        _approve(to, tokenId);
+    }
+
+    /**
+     * @dev See {IERC721-getApproved}.
+     */
+    function getApproved(uint256 tokenId) public view virtual override returns (address) {
+        _requireMinted(tokenId);
+
+        return _tokenApprovals[tokenId];
+    }
+
+    /**
+     * @dev See {IERC721-setApprovalForAll}.
+     */
+    function setApprovalForAll(address operator, bool approved) public virtual override {
+        _setApprovalForAll(_msgSender(), operator, approved);
+    }
+
+    /**
+     * @dev See {IERC721-isApprovedForAll}.
+     */
+    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
+        return _operatorApprovals[owner][operator];
+    }
+
+    /**
+     * @dev See {IERC721-transferFrom}.
+     */
+    function transferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) public virtual override {
+        //solhint-disable-next-line max-line-length
+        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
+
+        _transfer(from, to, tokenId);
+    }
+
+    /**
+     * @dev See {IERC721-safeTransferFrom}.
+     */
+    function safeTransferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) public virtual override {
+        safeTransferFrom(from, to, tokenId, "");
+    }
+
+    /**
+     * @dev See {IERC721-safeTransferFrom}.
+     */
+    function safeTransferFrom(
+        address from,
+        address to,
+        uint256 tokenId,
+        bytes memory data
+    ) public virtual override {
+        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
+        _safeTransfer(from, to, tokenId, data);
+    }
+
+    /**
+     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
+     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
+     *
+     * `data` is additional data, it has no specified format and it is sent in call to `to`.
+     *
+     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
+     * implement alternative mechanisms to perform token transfer, such as signature-based.
+     *
+     * Requirements:
+     *
+     * - `from` cannot be the zero address.
+     * - `to` cannot be the zero address.
+     * - `tokenId` token must exist and be owned by `from`.
+     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
+     *
+     * Emits a {Transfer} event.
+     */
+    function _safeTransfer(
+        address from,
+        address to,
+        uint256 tokenId,
+        bytes memory data
+    ) internal virtual {
+        _transfer(from, to, tokenId);
+        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
+    }
+
+    /**
+     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
+     */
+    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
+        return _owners[tokenId];
+    }
+
+    /**
+     * @dev Returns whether `tokenId` exists.
+     *
+     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
+     *
+     * Tokens start existing when they are minted (`_mint`),
+     * and stop existing when they are burned (`_burn`).
+     */
+    function _exists(uint256 tokenId) internal view virtual returns (bool) {
+        return _ownerOf(tokenId) != address(0);
+    }
+
+    /**
+     * @dev Returns whether `spender` is allowed to manage `tokenId`.
+     *
+     * Requirements:
+     *
+     * - `tokenId` must exist.
+     */
+    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
+        address owner = ERC721.ownerOf(tokenId);
+        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
+    }
+
+    /**
+     * @dev Safely mints `tokenId` and transfers it to `to`.
+     *
+     * Requirements:
+     *
+     * - `tokenId` must not exist.
+     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
+     *
+     * Emits a {Transfer} event.
+     */
+    function _safeMint(address to, uint256 tokenId) internal virtual {
+        _safeMint(to, tokenId, "");
+    }
+
+    /**
+     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
+     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
+     */
+    function _safeMint(
+        address to,
+        uint256 tokenId,
+        bytes memory data
+    ) internal virtual {
+        _mint(to, tokenId);
+        require(
+            _checkOnERC721Received(address(0), to, tokenId, data),
+            "ERC721: transfer to non ERC721Receiver implementer"
+        );
+    }
+
+    /**
+     * @dev Mints `tokenId` and transfers it to `to`.
+     *
+     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
+     *
+     * Requirements:
+     *
+     * - `tokenId` must not exist.
+     * - `to` cannot be the zero address.
+     *
+     * Emits a {Transfer} event.
+     */
+    function _mint(address to, uint256 tokenId) internal virtual {
+        require(to != address(0), "ERC721: mint to the zero address");
+        require(!_exists(tokenId), "ERC721: token already minted");
+
+        _beforeTokenTransfer(address(0), to, tokenId, 1);
+
+        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
+        require(!_exists(tokenId), "ERC721: token already minted");
+
+        unchecked {
+            // Will not overflow unless all 2**256 token ids are minted to the same owner.
+            // Given that tokens are minted one by one, it is impossible in practice that
+            // this ever happens. Might change if we allow batch minting.
+            // The ERC fails to describe this case.
+            _balances[to] += 1;
+        }
+
+        _owners[tokenId] = to;
+
+        emit Transfer(address(0), to, tokenId);
+
+        _afterTokenTransfer(address(0), to, tokenId, 1);
+    }
+
+    /**
+     * @dev Destroys `tokenId`.
+     * The approval is cleared when the token is burned.
+     * This is an internal function that does not check if the sender is authorized to operate on the token.
+     *
+     * Requirements:
+     *
+     * - `tokenId` must exist.
+     *
+     * Emits a {Transfer} event.
+     */
+    function _burn(uint256 tokenId) internal virtual {
+        address owner = ERC721.ownerOf(tokenId);
+
+        _beforeTokenTransfer(owner, address(0), tokenId, 1);
+
+        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
+        owner = ERC721.ownerOf(tokenId);
+
+        // Clear approvals
+        delete _tokenApprovals[tokenId];
+
+        unchecked {
+            // Cannot overflow, as that would require more tokens to be burned/transferred
+            // out than the owner initially received through minting and transferring in.
+            _balances[owner] -= 1;
+        }
+        delete _owners[tokenId];
+
+        emit Transfer(owner, address(0), tokenId);
+
+        _afterTokenTransfer(owner, address(0), tokenId, 1);
+    }
+
+    /**
+     * @dev Transfers `tokenId` from `from` to `to`.
+     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
+     *
+     * Requirements:
+     *
+     * - `to` cannot be the zero address.
+     * - `tokenId` token must be owned by `from`.
+     *
+     * Emits a {Transfer} event.
+     */
+    function _transfer(
+        address from,
+        address to,
+        uint256 tokenId
+    ) internal virtual {
+        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
+        require(to != address(0), "ERC721: transfer to the zero address");
+
+        _beforeTokenTransfer(from, to, tokenId, 1);
+
+        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
+        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
+
+        // Clear approvals from the previous owner
+        delete _tokenApprovals[tokenId];
+
+        unchecked {
+            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
+            // `from`'s balance is the number of token held, which is at least one before the current
+            // transfer.
+            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
+            // all 2**256 token ids to be minted, which in practice is impossible.
+            _balances[from] -= 1;
+            _balances[to] += 1;
+        }
+        _owners[tokenId] = to;
+
+        emit Transfer(from, to, tokenId);
+
+        _afterTokenTransfer(from, to, tokenId, 1);
+    }
+
+    /**
+     * @dev Approve `to` to operate on `tokenId`
+     *
+     * Emits an {Approval} event.
+     */
+    function _approve(address to, uint256 tokenId) internal virtual {
+        _tokenApprovals[tokenId] = to;
+        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
+    }
+
+    /**
+     * @dev Approve `operator` to operate on all of `owner` tokens
+     *
+     * Emits an {ApprovalForAll} event.
+     */
+    function _setApprovalForAll(
+        address owner,
+        address operator,
+        bool approved
+    ) internal virtual {
+        require(owner != operator, "ERC721: approve to caller");
+        _operatorApprovals[owner][operator] = approved;
+        emit ApprovalForAll(owner, operator, approved);
+    }
+
+    /**
+     * @dev Reverts if the `tokenId` has not been minted yet.
+     */
+    function _requireMinted(uint256 tokenId) internal view virtual {
+        require(_exists(tokenId), "ERC721: invalid token ID");
+    }
+
+    /**
+     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
+     * The call is not executed if the target address is not a contract.
+     *
+     * @param from address representing the previous owner of the given token ID
+     * @param to target address that will receive the tokens
+     * @param tokenId uint256 ID of the token to be transferred
+     * @param data bytes optional data to send along with the call
+     * @return bool whether the call correctly returned the expected magic value
+     */
+    function _checkOnERC721Received(
+        address from,
+        address to,
+        uint256 tokenId,
+        bytes memory data
+    ) private returns (bool) {
+        if (to.isContract()) {
+            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
+                return retval == IERC721Receiver.onERC721Received.selector;
+            } catch (bytes memory reason) {
+                if (reason.length == 0) {
+                    revert("ERC721: transfer to non ERC721Receiver implementer");
+                } else {
+                    /// @solidity memory-safe-assembly
+                    assembly {
+                        revert(add(32, reason), mload(reason))
+                    }
+                }
+            }
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
+     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
+     *
+     * Calling conditions:
+     *
+     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
+     * - When `from` is zero, the tokens will be minted for `to`.
+     * - When `to` is zero, ``from``'s tokens will be burned.
+     * - `from` and `to` are never both zero.
+     * - `batchSize` is non-zero.
+     *
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
+     */
+    function _beforeTokenTransfer(
+        address from,
+        address to,
+        uint256, /* firstTokenId */
+        uint256 batchSize
+    ) internal virtual {
+        if (batchSize > 1) {
+            if (from != address(0)) {
+                _balances[from] -= batchSize;
+            }
+            if (to != address(0)) {
+                _balances[to] += batchSize;
+            }
+        }
+    }
+
+    /**
+     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
+     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
+     *
+     * Calling conditions:
+     *
+     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
+     * - When `from` is zero, the tokens were minted for `to`.
+     * - When `to` is zero, ``from``'s tokens were burned.
+     * - `from` and `to` are never both zero.
+     * - `batchSize` is non-zero.
+     *
+     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
+     */
+    function _afterTokenTransfer(
+        address from,
+        address to,
+        uint256 firstTokenId,
+        uint256 batchSize
+    ) internal virtual {}
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721.sol
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
+
+pragma solidity ^0.8.0;
+
+import "../../utils/introspection/IERC165.sol";
+
+/**
+ * @dev Required interface of an ERC721 compliant contract.
+ */
+interface IERC721 is IERC165 {
+    /**
+     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
+     */
+    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
+
+    /**
+     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
+     */
+    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
+
+    /**
+     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
+     */
+    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
+
+    /**
+     * @dev Returns the number of tokens in ``owner``'s account.
+     */
+    function balanceOf(address owner) external view returns (uint256 balance);
+
+    /**
+     * @dev Returns the owner of the `tokenId` token.
+     *
+     * Requirements:
+     *
+     * - `tokenId` must exist.
+     */
+    function ownerOf(uint256 tokenId) external view returns (address owner);
+
+    /**
+     * @dev Safely transfers `tokenId` token from `from` to `to`.
+     *
+     * Requirements:
+     *
+     * - `from` cannot be the zero address.
+     * - `to` cannot be the zero address.
+     * - `tokenId` token must exist and be owned by `from`.
+     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
+     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
+     *
+     * Emits a {Transfer} event.
+     */
+    function safeTransferFrom(
+        address from,
+        address to,
+        uint256 tokenId,
+        bytes calldata data
+    ) external;
+
+    /**
+     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
+     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
+     *
+     * Requirements:
+     *
+     * - `from` cannot be the zero address.
+     * - `to` cannot be the zero address.
+     * - `tokenId` token must exist and be owned by `from`.
+     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
+     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
+     *
+     * Emits a {Transfer} event.
+     */
+    function safeTransferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external;
+
+    /**
+     * @dev Transfers `tokenId` token from `from` to `to`.
+     *
+     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
+     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
+     * understand this adds an external call which potentially creates a reentrancy vulnerability.
+     *
+     * Requirements:
+     *
+     * - `from` cannot be the zero address.
+     * - `to` cannot be the zero address.
+     * - `tokenId` token must be owned by `from`.
+     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
+     *
+     * Emits a {Transfer} event.
+     */
+    function transferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external;
+
+    /**
+     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
+     * The approval is cleared when the token is transferred.
+     *
+     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
+     *
+     * Requirements:
+     *
+     * - The caller must own the token or be an approved operator.
+     * - `tokenId` must exist.
+     *
+     * Emits an {Approval} event.
+     */
+    function approve(address to, uint256 tokenId) external;
+
+    /**
+     * @dev Approve or remove `operator` as an operator for the caller.
+     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
+     *
+     * Requirements:
+     *
+     * - The `operator` cannot be the caller.
+     *
+     * Emits an {ApprovalForAll} event.
+     */
+    function setApprovalForAll(address operator, bool _approved) external;
+
+    /**
+     * @dev Returns the account approved for `tokenId` token.
+     *
+     * Requirements:
+     *
+     * - `tokenId` must exist.
+     */
+    function getApproved(uint256 tokenId) external view returns (address operator);
+
+    /**
+     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
+     *
+     * See {setApprovalForAll}
+     */
+    function isApprovedForAll(address owner, address operator) external view returns (bool);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721Receiver.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @title ERC721 token receiver interface
+ * @dev Interface for any contract that wants to support safeTransfers
+ * from ERC721 asset contracts.
+ */
+interface IERC721Receiver {
+    /**
+     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
+     * by `operator` from `from`, this function is called.
+     *
+     * It must return its Solidity selector to confirm the token transfer.
+     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
+     *
+     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
+     */
+    function onERC721Received(
+        address operator,
+        address from,
+        uint256 tokenId,
+        bytes calldata data
+    ) external returns (bytes4);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Burnable.sol)
+
+pragma solidity ^0.8.0;
+
+import "../ERC721.sol";
+import "../../../utils/Context.sol";
+
+/**
+ * @title ERC721 Burnable Token
+ * @dev ERC721 Token that can be burned (destroyed).
+ */
+abstract contract ERC721Burnable is Context, ERC721 {
+    /**
+     * @dev Burns `tokenId`. See {ERC721-_burn}.
+     *
+     * Requirements:
+     *
+     * - The caller must own `tokenId` or be an approved operator.
+     */
+    function burn(uint256 tokenId) public virtual {
+        //solhint-disable-next-line max-line-length
+        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
+        _burn(tokenId);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)
+
+pragma solidity ^0.8.0;
+
+import "../ERC721.sol";
+import "./IERC721Enumerable.sol";
+
+/**
+ * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
+ * enumerability of all the token ids in the contract as well as all token ids owned by each
+ * account.
+ */
+abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
+    // Mapping from owner to list of owned token IDs
+    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
+
+    // Mapping from token ID to index of the owner tokens list
+    mapping(uint256 => uint256) private _ownedTokensIndex;
+
+    // Array with all token ids, used for enumeration
+    uint256[] private _allTokens;
+
+    // Mapping from token id to position in the allTokens array
+    mapping(uint256 => uint256) private _allTokensIndex;
+
+    /**
+     * @dev See {IERC165-supportsInterface}.
+     */
+    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
+        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
+    }
+
+    /**
+     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
+     */
+    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
+        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
+        return _ownedTokens[owner][index];
+    }
+
+    /**
+     * @dev See {IERC721Enumerable-totalSupply}.
+     */
+    function totalSupply() public view virtual override returns (uint256) {
+        return _allTokens.length;
+    }
+
+    /**
+     * @dev See {IERC721Enumerable-tokenByIndex}.
+     */
+    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
+        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
+        return _allTokens[index];
+    }
+
+    /**
+     * @dev See {ERC721-_beforeTokenTransfer}.
+     */
+    function _beforeTokenTransfer(
+        address from,
+        address to,
+        uint256 firstTokenId,
+        uint256 batchSize
+    ) internal virtual override {
+        super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
+
+        if (batchSize > 1) {
+            // Will only trigger during construction. Batch transferring (minting) is not available afterwards.
+            revert("ERC721Enumerable: consecutive transfers not supported");
+        }
+
+        uint256 tokenId = firstTokenId;
+
+        if (from == address(0)) {
+            _addTokenToAllTokensEnumeration(tokenId);
+        } else if (from != to) {
+            _removeTokenFromOwnerEnumeration(from, tokenId);
+        }
+        if (to == address(0)) {
+            _removeTokenFromAllTokensEnumeration(tokenId);
+        } else if (to != from) {
+            _addTokenToOwnerEnumeration(to, tokenId);
+        }
+    }
+
+    /**
+     * @dev Private function to add a token to this extension's ownership-tracking data structures.
+     * @param to address representing the new owner of the given token ID
+     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
+     */
+    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
+        uint256 length = ERC721.balanceOf(to);
+        _ownedTokens[to][length] = tokenId;
+        _ownedTokensIndex[tokenId] = length;
+    }
+
+    /**
+     * @dev Private function to add a token to this extension's token tracking data structures.
+     * @param tokenId uint256 ID of the token to be added to the tokens list
+     */
+    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
+        _allTokensIndex[tokenId] = _allTokens.length;
+        _allTokens.push(tokenId);
+    }
+
+    /**
+     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
+     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
+     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
+     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
+     * @param from address representing the previous owner of the given token ID
+     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
+     */
+    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
+        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
+        // then delete the last slot (swap and pop).
+
+        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
+        uint256 tokenIndex = _ownedTokensIndex[tokenId];
+
+        // When the token to delete is the last token, the swap operation is unnecessary
+        if (tokenIndex != lastTokenIndex) {
+            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
+
+            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
+            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
+        }
+
+        // This also deletes the contents at the last position of the array
+        delete _ownedTokensIndex[tokenId];
+        delete _ownedTokens[from][lastTokenIndex];
+    }
+
+    /**
+     * @dev Private function to remove a token from this extension's token tracking data structures.
+     * This has O(1) time complexity, but alters the order of the _allTokens array.
+     * @param tokenId uint256 ID of the token to be removed from the tokens list
+     */
+    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
+        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
+        // then delete the last slot (swap and pop).
+
+        uint256 lastTokenIndex = _allTokens.length - 1;
+        uint256 tokenIndex = _allTokensIndex[tokenId];
+
+        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
+        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
+        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
+        uint256 lastTokenId = _allTokens[lastTokenIndex];
+
+        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
+        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
+
+        // This also deletes the contents at the last position of the array
+        delete _allTokensIndex[tokenId];
+        _allTokens.pop();
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721URIStorage.sol)
+
+pragma solidity ^0.8.0;
+
+import "../ERC721.sol";
+
+/**
+ * @dev ERC721 token with storage based token URI management.
+ */
+abstract contract ERC721URIStorage is ERC721 {
+    using Strings for uint256;
+
+    // Optional mapping for token URIs
+    mapping(uint256 => string) private _tokenURIs;
+
+    /**
+     * @dev See {IERC721Metadata-tokenURI}.
+     */
+    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
+        _requireMinted(tokenId);
+
+        string memory _tokenURI = _tokenURIs[tokenId];
+        string memory base = _baseURI();
+
+        // If there is no base URI, return the token URI.
+        if (bytes(base).length == 0) {
+            return _tokenURI;
+        }
+        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
+        if (bytes(_tokenURI).length > 0) {
+            return string(abi.encodePacked(base, _tokenURI));
+        }
+
+        return super.tokenURI(tokenId);
+    }
+
+    /**
+     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
+     *
+     * Requirements:
+     *
+     * - `tokenId` must exist.
+     */
+    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
+        require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
+        _tokenURIs[tokenId] = _tokenURI;
+    }
+
+    /**
+     * @dev See {ERC721-_burn}. This override additionally checks to see if a
+     * token-specific URI was set for the token, and if so, it deletes the token URI from
+     * the storage mapping.
+     */
+    function _burn(uint256 tokenId) internal virtual override {
+        super._burn(tokenId);
+
+        if (bytes(_tokenURIs[tokenId]).length != 0) {
+            delete _tokenURIs[tokenId];
+        }
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
+
+pragma solidity ^0.8.0;
+
+import "../IERC721.sol";
+
+/**
+ * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
+ * @dev See https://eips.ethereum.org/EIPS/eip-721
+ */
+interface IERC721Enumerable is IERC721 {
+    /**
+     * @dev Returns the total amount of tokens stored by the contract.
+     */
+    function totalSupply() external view returns (uint256);
+
+    /**
+     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
+     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
+     */
+    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
+
+    /**
+     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
+     * Use along with {totalSupply} to enumerate all tokens.
+     */
+    function tokenByIndex(uint256 index) external view returns (uint256);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
+
+pragma solidity ^0.8.0;
+
+import "../IERC721.sol";
+
+/**
+ * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+ * @dev See https://eips.ethereum.org/EIPS/eip-721
+ */
+interface IERC721Metadata is IERC721 {
+    /**
+     * @dev Returns the token collection name.
+     */
+    function name() external view returns (string memory);
+
+    /**
+     * @dev Returns the token collection symbol.
+     */
+    function symbol() external view returns (string memory);
+
+    /**
+     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
+     */
+    function tokenURI(uint256 tokenId) external view returns (string memory);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Address.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Address.sol
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
+
+pragma solidity ^0.8.1;
+
+/**
+ * @dev Collection of functions related to the address type
+ */
+library Address {
+    /**
+     * @dev Returns true if `account` is a contract.
+     *
+     * [IMPORTANT]
+     * ====
+     * It is unsafe to assume that an address for which this function returns
+     * false is an externally-owned account (EOA) and not a contract.
+     *
+     * Among others, `isContract` will return false for the following
+     * types of addresses:
+     *
+     *  - an externally-owned account
+     *  - a contract in construction
+     *  - an address where a contract will be created
+     *  - an address where a contract lived, but was destroyed
+     * ====
+     *
+     * [IMPORTANT]
+     * ====
+     * You shouldn't rely on `isContract` to protect against flash loan attacks!
+     *
+     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
+     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
+     * constructor.
+     * ====
+     */
+    function isContract(address account) internal view returns (bool) {
+        // This method relies on extcodesize/address.code.length, which returns 0
+        // for contracts in construction, since the code is only stored at the end
+        // of the constructor execution.
+
+        return account.code.length > 0;
+    }
+
+    /**
+     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
+     * `recipient`, forwarding all available gas and reverting on errors.
+     *
+     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
+     * of certain opcodes, possibly making contracts go over the 2300 gas limit
+     * imposed by `transfer`, making them unable to receive funds via
+     * `transfer`. {sendValue} removes this limitation.
+     *
+     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
+     *
+     * IMPORTANT: because control is transferred to `recipient`, care must be
+     * taken to not create reentrancy vulnerabilities. Consider using
+     * {ReentrancyGuard} or the
+     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
+     */
+    function sendValue(address payable recipient, uint256 amount) internal {
+        require(address(this).balance >= amount, "Address: insufficient balance");
+
+        (bool success, ) = recipient.call{value: amount}("");
+        require(success, "Address: unable to send value, recipient may have reverted");
+    }
+
+    /**
+     * @dev Performs a Solidity function call using a low level `call`. A
+     * plain `call` is an unsafe replacement for a function call: use this
+     * function instead.
+     *
+     * If `target` reverts with a revert reason, it is bubbled up by this
+     * function (like regular Solidity function calls).
+     *
+     * Returns the raw returned data. To convert to the expected return value,
+     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
+     *
+     * Requirements:
+     *
+     * - `target` must be a contract.
+     * - calling `target` with `data` must not revert.
+     *
+     * _Available since v3.1._
+     */
+    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
+        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
+    }
+
+    /**
+     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
+     * `errorMessage` as a fallback revert reason when `target` reverts.
+     *
+     * _Available since v3.1._
+     */
+    function functionCall(
+        address target,
+        bytes memory data,
+        string memory errorMessage
+    ) internal returns (bytes memory) {
+        return functionCallWithValue(target, data, 0, errorMessage);
+    }
+
+    /**
+     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+     * but also transferring `value` wei to `target`.
+     *
+     * Requirements:
+     *
+     * - the calling contract must have an ETH balance of at least `value`.
+     * - the called Solidity function must be `payable`.
+     *
+     * _Available since v3.1._
+     */
+    function functionCallWithValue(
+        address target,
+        bytes memory data,
+        uint256 value
+    ) internal returns (bytes memory) {
+        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
+    }
+
+    /**
+     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
+     * with `errorMessage` as a fallback revert reason when `target` reverts.
+     *
+     * _Available since v3.1._
+     */
+    function functionCallWithValue(
+        address target,
+        bytes memory data,
+        uint256 value,
+        string memory errorMessage
+    ) internal returns (bytes memory) {
+        require(address(this).balance >= value, "Address: insufficient balance for call");
+        (bool success, bytes memory returndata) = target.call{value: value}(data);
+        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
+    }
+
+    /**
+     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+     * but performing a static call.
+     *
+     * _Available since v3.3._
+     */
+    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
+        return functionStaticCall(target, data, "Address: low-level static call failed");
+    }
+
+    /**
+     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
+     * but performing a static call.
+     *
+     * _Available since v3.3._
+     */
+    function functionStaticCall(
+        address target,
+        bytes memory data,
+        string memory errorMessage
+    ) internal view returns (bytes memory) {
+        (bool success, bytes memory returndata) = target.staticcall(data);
+        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
+    }
+
+    /**
+     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
+     * but performing a delegate call.
+     *
+     * _Available since v3.4._
+     */
+    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
+        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
+    }
+
+    /**
+     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
+     * but performing a delegate call.
+     *
+     * _Available since v3.4._
+     */
+    function functionDelegateCall(
+        address target,
+        bytes memory data,
+        string memory errorMessage
+    ) internal returns (bytes memory) {
+        (bool success, bytes memory returndata) = target.delegatecall(data);
+        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
+    }
+
+    /**
+     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
+     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
+     *
+     * _Available since v4.8._
+     */
+    function verifyCallResultFromTarget(
+        address target,
+        bool success,
+        bytes memory returndata,
+        string memory errorMessage
+    ) internal view returns (bytes memory) {
+        if (success) {
+            if (returndata.length == 0) {
+                // only check isContract if the call was successful and the return data is empty
+                // otherwise we already know that it was a contract
+                require(isContract(target), "Address: call to non-contract");
+            }
+            return returndata;
+        } else {
+            _revert(returndata, errorMessage);
+        }
+    }
+
+    /**
+     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
+     * revert reason or using the provided one.
+     *
+     * _Available since v4.3._
+     */
+    function verifyCallResult(
+        bool success,
+        bytes memory returndata,
+        string memory errorMessage
+    ) internal pure returns (bytes memory) {
+        if (success) {
+            return returndata;
+        } else {
+            _revert(returndata, errorMessage);
+        }
+    }
+
+    function _revert(bytes memory returndata, string memory errorMessage) private pure {
+        // Look for revert reason and bubble it up if present
+        if (returndata.length > 0) {
+            // The easiest way to bubble the revert reason is using memory via assembly
+            /// @solidity memory-safe-assembly
+            assembly {
+                let returndata_size := mload(returndata)
+                revert(add(32, returndata), returndata_size)
+            }
+        } else {
+            revert(errorMessage);
+        }
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Context.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Context.sol
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Provides information about the current execution context, including the
+ * sender of the transaction and its data. While these are generally available
+ * via msg.sender and msg.data, they should not be accessed in such a direct
+ * manner, since when dealing with meta-transactions the account sending and
+ * paying for execution may not be the actual sender (as far as an application
+ * is concerned).
+ *
+ * This contract is only required for intermediate, library-like contracts.
+ */
+abstract contract Context {
+    function _msgSender() internal view virtual returns (address) {
+        return msg.sender;
+    }
+
+    function _msgData() internal view virtual returns (bytes calldata) {
+        return msg.data;
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Counters.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Counters.sol
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @title Counters
+ * @author Matt Condon (@shrugs)
+ * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
+ * of elements in a mapping, issuing ERC721 ids, or counting request ids.
+ *
+ * Include with `using Counters for Counters.Counter;`
+ */
+library Counters {
+    struct Counter {
+        // This variable should never be directly accessed by users of the library: interactions must be restricted to
+        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
+        // this feature: see https://github.com/ethereum/solidity/issues/4637
+        uint256 _value; // default: 0
+    }
+
+    function current(Counter storage counter) internal view returns (uint256) {
+        return counter._value;
+    }
+
+    function increment(Counter storage counter) internal {
+        unchecked {
+            counter._value += 1;
+        }
+    }
+
+    function decrement(Counter storage counter) internal {
+        uint256 value = counter._value;
+        require(value > 0, "Counter: decrement overflow");
+        unchecked {
+            counter._value = value - 1;
+        }
+    }
+
+    function reset(Counter storage counter) internal {
+        counter._value = 0;
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Strings.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Strings.sol
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
+
+pragma solidity ^0.8.0;
+
+import "./math/Math.sol";
+
+/**
+ * @dev String operations.
+ */
+library Strings {
+    bytes16 private constant _SYMBOLS = "0123456789abcdef";
+    uint8 private constant _ADDRESS_LENGTH = 20;
+
+    /**
+     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
+     */
+    function toString(uint256 value) internal pure returns (string memory) {
+        unchecked {
+            uint256 length = Math.log10(value) + 1;
+            string memory buffer = new string(length);
+            uint256 ptr;
+            /// @solidity memory-safe-assembly
+            assembly {
+                ptr := add(buffer, add(32, length))
+            }
+            while (true) {
+                ptr--;
+                /// @solidity memory-safe-assembly
+                assembly {
+                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
+                }
+                value /= 10;
+                if (value == 0) break;
+            }
+            return buffer;
+        }
+    }
+
+    /**
+     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
+     */
+    function toHexString(uint256 value) internal pure returns (string memory) {
+        unchecked {
+            return toHexString(value, Math.log256(value) + 1);
+        }
+    }
+
+    /**
+     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
+     */
+    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
+        bytes memory buffer = new bytes(2 * length + 2);
+        buffer[0] = "0";
+        buffer[1] = "x";
+        for (uint256 i = 2 * length + 1; i > 1; --i) {
+            buffer[i] = _SYMBOLS[value & 0xf];
+            value >>= 4;
+        }
+        require(value == 0, "Strings: hex length insufficient");
+        return string(buffer);
+    }
+
+    /**
+     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
+     */
+    function toHexString(address addr) internal pure returns (string memory) {
+        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/ERC165.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/ERC165.sol
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
+
+pragma solidity ^0.8.0;
+
+import "./IERC165.sol";
+
+/**
+ * @dev Implementation of the {IERC165} interface.
+ *
+ * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
+ * for the additional interface id that will be supported. For example:
+ *
+ * ```solidity
+ * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
+ *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
+ * }
+ * ```
+ *
+ * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
+ */
+abstract contract ERC165 is IERC165 {
+    /**
+     * @dev See {IERC165-supportsInterface}.
+     */
+    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
+        return interfaceId == type(IERC165).interfaceId;
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/IERC165.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/IERC165.sol
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Interface of the ERC165 standard, as defined in the
+ * https://eips.ethereum.org/EIPS/eip-165[EIP].
+ *
+ * Implementers can declare support of contract interfaces, which can then be
+ * queried by others ({ERC165Checker}).
+ *
+ * For an implementation, see {ERC165}.
+ */
+interface IERC165 {
+    /**
+     * @dev Returns true if this contract implements the interface defined by
+     * `interfaceId`. See the corresponding
+     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
+     * to learn more about how these ids are created.
+     *
+     * This function call must use less than 30 000 gas.
+     */
+    function supportsInterface(bytes4 interfaceId) external view returns (bool);
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/math/Math.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/math/Math.sol
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
+
+pragma solidity ^0.8.0;
+
+/**
+ * @dev Standard math utilities missing in the Solidity language.
+ */
+library Math {
+    enum Rounding {
+        Down, // Toward negative infinity
+        Up, // Toward infinity
+        Zero // Toward zero
+    }
+
+    /**
+     * @dev Returns the largest of two numbers.
+     */
+    function max(uint256 a, uint256 b) internal pure returns (uint256) {
+        return a > b ? a : b;
+    }
+
+    /**
+     * @dev Returns the smallest of two numbers.
+     */
+    function min(uint256 a, uint256 b) internal pure returns (uint256) {
+        return a < b ? a : b;
+    }
+
+    /**
+     * @dev Returns the average of two numbers. The result is rounded towards
+     * zero.
+     */
+    function average(uint256 a, uint256 b) internal pure returns (uint256) {
+        // (a + b) / 2 can overflow.
+        return (a & b) + (a ^ b) / 2;
+    }
+
+    /**
+     * @dev Returns the ceiling of the division of two numbers.
+     *
+     * This differs from standard division with `/` in that it rounds up instead
+     * of rounding down.
+     */
+    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
+        // (a + b - 1) / b can overflow on addition, so we distribute.
+        return a == 0 ? 0 : (a - 1) / b + 1;
+    }
+
+    /**
+     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
+     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
+     * with further edits by Uniswap Labs also under MIT license.
+     */
+    function mulDiv(
+        uint256 x,
+        uint256 y,
+        uint256 denominator
+    ) internal pure returns (uint256 result) {
+        unchecked {
+            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
+            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
+            // variables such that product = prod1 * 2^256 + prod0.
+            uint256 prod0; // Least significant 256 bits of the product
+            uint256 prod1; // Most significant 256 bits of the product
+            assembly {
+                let mm := mulmod(x, y, not(0))
+                prod0 := mul(x, y)
+                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
+            }
+
+            // Handle non-overflow cases, 256 by 256 division.
+            if (prod1 == 0) {
+                return prod0 / denominator;
+            }
+
+            // Make sure the result is less than 2^256. Also prevents denominator == 0.
+            require(denominator > prod1);
+
+            ///////////////////////////////////////////////
+            // 512 by 256 division.
+            ///////////////////////////////////////////////
+
+            // Make division exact by subtracting the remainder from [prod1 prod0].
+            uint256 remainder;
+            assembly {
+                // Compute remainder using mulmod.
+                remainder := mulmod(x, y, denominator)
+
+                // Subtract 256 bit number from 512 bit number.
+                prod1 := sub(prod1, gt(remainder, prod0))
+                prod0 := sub(prod0, remainder)
+            }
+
+            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
+            // See https://cs.stackexchange.com/q/138556/92363.
+
+            // Does not overflow because the denominator cannot be zero at this stage in the function.
+            uint256 twos = denominator & (~denominator + 1);
+            assembly {
+                // Divide denominator by twos.
+                denominator := div(denominator, twos)
+
+                // Divide [prod1 prod0] by twos.
+                prod0 := div(prod0, twos)
+
+                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
+                twos := add(div(sub(0, twos), twos), 1)
+            }
+
+            // Shift in bits from prod1 into prod0.
+            prod0 |= prod1 * twos;
+
+            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
+            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
+            // four bits. That is, denominator * inv = 1 mod 2^4.
+            uint256 inverse = (3 * denominator) ^ 2;
+
+            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
+            // in modular arithmetic, doubling the correct bits in each step.
+            inverse *= 2 - denominator * inverse; // inverse mod 2^8
+            inverse *= 2 - denominator * inverse; // inverse mod 2^16
+            inverse *= 2 - denominator * inverse; // inverse mod 2^32
+            inverse *= 2 - denominator * inverse; // inverse mod 2^64
+            inverse *= 2 - denominator * inverse; // inverse mod 2^128
+            inverse *= 2 - denominator * inverse; // inverse mod 2^256
+
+            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
+            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
+            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
+            // is no longer required.
+            result = prod0 * inverse;
+            return result;
+        }
+    }
+
+    /**
+     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
+     */
+    function mulDiv(
+        uint256 x,
+        uint256 y,
+        uint256 denominator,
+        Rounding rounding
+    ) internal pure returns (uint256) {
+        uint256 result = mulDiv(x, y, denominator);
+        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
+            result += 1;
+        }
+        return result;
+    }
+
+    /**
+     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
+     *
+     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
+     */
+    function sqrt(uint256 a) internal pure returns (uint256) {
+        if (a == 0) {
+            return 0;
+        }
+
+        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
+        //
+        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
+        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
+        //
+        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
+        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
+        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
+        //
+        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
+        uint256 result = 1 << (log2(a) >> 1);
+
+        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
+        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
+        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
+        // into the expected uint128 result.
+        unchecked {
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            result = (result + a / result) >> 1;
+            return min(result, a / result);
+        }
+    }
+
+    /**
+     * @notice Calculates sqrt(a), following the selected rounding direction.
+     */
+    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = sqrt(a);
+            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
+        }
+    }
+
+    /**
+     * @dev Return the log in base 2, rounded down, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log2(uint256 value) internal pure returns (uint256) {
+        uint256 result = 0;
+        unchecked {
+            if (value >> 128 > 0) {
+                value >>= 128;
+                result += 128;
+            }
+            if (value >> 64 > 0) {
+                value >>= 64;
+                result += 64;
+            }
+            if (value >> 32 > 0) {
+                value >>= 32;
+                result += 32;
+            }
+            if (value >> 16 > 0) {
+                value >>= 16;
+                result += 16;
+            }
+            if (value >> 8 > 0) {
+                value >>= 8;
+                result += 8;
+            }
+            if (value >> 4 > 0) {
+                value >>= 4;
+                result += 4;
+            }
+            if (value >> 2 > 0) {
+                value >>= 2;
+                result += 2;
+            }
+            if (value >> 1 > 0) {
+                result += 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = log2(value);
+            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
+        }
+    }
+
+    /**
+     * @dev Return the log in base 10, rounded down, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log10(uint256 value) internal pure returns (uint256) {
+        uint256 result = 0;
+        unchecked {
+            if (value >= 10**64) {
+                value /= 10**64;
+                result += 64;
+            }
+            if (value >= 10**32) {
+                value /= 10**32;
+                result += 32;
+            }
+            if (value >= 10**16) {
+                value /= 10**16;
+                result += 16;
+            }
+            if (value >= 10**8) {
+                value /= 10**8;
+                result += 8;
+            }
+            if (value >= 10**4) {
+                value /= 10**4;
+                result += 4;
+            }
+            if (value >= 10**2) {
+                value /= 10**2;
+                result += 2;
+            }
+            if (value >= 10**1) {
+                result += 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = log10(value);
+            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
+        }
+    }
+
+    /**
+     * @dev Return the log in base 256, rounded down, of a positive value.
+     * Returns 0 if given 0.
+     *
+     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
+     */
+    function log256(uint256 value) internal pure returns (uint256) {
+        uint256 result = 0;
+        unchecked {
+            if (value >> 128 > 0) {
+                value >>= 128;
+                result += 16;
+            }
+            if (value >> 64 > 0) {
+                value >>= 64;
+                result += 8;
+            }
+            if (value >> 32 > 0) {
+                value >>= 32;
+                result += 4;
+            }
+            if (value >> 16 > 0) {
+                value >>= 16;
+                result += 2;
+            }
+            if (value >> 8 > 0) {
+                result += 1;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
+     * Returns 0 if given 0.
+     */
+    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
+        unchecked {
+            uint256 result = log256(value);
+            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
+        }
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/ZeppelinContract.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/ZeppelinContract.sol
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.9;
+
+import "./@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
+import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
+import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
+import "./@openzeppelin/contracts/access/Ownable.sol";
+import "./@openzeppelin/contracts/utils/Counters.sol";
+
+contract ZeppelinContract is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Burnable, Ownable {
+    using Counters for Counters.Counter;
+
+    Counters.Counter private _tokenIdCounter;
+
+    constructor() ERC721("ZeppelinContract", "UNQ") {}
+
+    function _baseURI() internal pure override returns (string memory) {
+        return "test";
+    }
+
+    function safeMint(address to, string memory uri) public onlyOwner {
+        uint256 tokenId = _tokenIdCounter.current();
+        _tokenIdCounter.increment();
+        _safeMint(to, tokenId);
+        _setTokenURI(tokenId, uri);
+    }
+
+    // The following functions are overrides required by Solidity.
+
+    function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
+        internal
+        override(ERC721, ERC721Enumerable)
+    {
+        super._beforeTokenTransfer(from, to, tokenId, batchSize);
+    }
+
+    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
+        super._burn(tokenId);
+    }
+
+    function tokenURI(uint256 tokenId)
+        public
+        view
+        override(ERC721, ERC721URIStorage)
+        returns (string memory)
+    {
+        return super.tokenURI(tokenId);
+    }
+
+    function supportsInterface(bytes4 interfaceId)
+        public
+        view
+        override(ERC721, ERC721Enumerable)
+        returns (bool)
+    {
+        return super.supportsInterface(interfaceId);
+    }
+}
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.abidiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.abi
@@ -0,0 +1 @@
+[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"uri","type":"string"}],"name":"safeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedjs-packages/scripts/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.bindiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.bin
@@ -0,0 +1 @@
+60806040523480156200001157600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b815250816000908162000068919062000195565b50600162000077828262000195565b505050620000946200008e6200009a60201b60201c565b6200009e565b62000261565b3390565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200011b57607f821691505b6020821081036200013c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200019057600081815260208120601f850160051c810160208610156200016b5750805b601f850160051c820191505b818110156200018c5782815560010162000177565b5050505b505050565b81516001600160401b03811115620001b157620001b1620000f0565b620001c981620001c2845462000106565b8462000142565b602080601f831160018114620002015760008415620001e85750858301515b600019600386901b1c1916600185901b1785556200018c565b600085815260208120601f198616915b82811015620002325788860151825594840194600190910190840162000211565b5085821015620002515787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b611f0d80620002716000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80636352211e116100b8578063a22cb4651161007c578063a22cb46514610271578063b88d4fde14610284578063c87b56dd14610297578063d204c45e146102aa578063e985e9c5146102bd578063f2fde38b146102f957600080fd5b80636352211e1461022a57806370a082311461023d578063715018a6146102505780638da5cb5b1461025857806395d89b411461026957600080fd5b806323b872dd116100ff57806323b872dd146101cb5780632f745c59146101de57806342842e0e146101f157806342966c68146102045780634f6ccce71461021757600080fd5b806301ffc9a71461013c57806306fdde0314610164578063081812fc14610179578063095ea7b3146101a457806318160ddd146101b9575b600080fd5b61014f61014a3660046118ab565b61030c565b60405190151581526020015b60405180910390f35b61016c61031d565b60405161015b9190611918565b61018c61018736600461192b565b6103af565b6040516001600160a01b03909116815260200161015b565b6101b76101b2366004611960565b6103d6565b005b6008545b60405190815260200161015b565b6101b76101d936600461198a565b6104f0565b6101bd6101ec366004611960565b610522565b6101b76101ff36600461198a565b6105b8565b6101b761021236600461192b565b6105d3565b6101bd61022536600461192b565b610604565b61018c61023836600461192b565b610697565b6101bd61024b3660046119c6565b6106f7565b6101b761077d565b600b546001600160a01b031661018c565b61016c610791565b6101b761027f3660046119e1565b6107a0565b6101b7610292366004611aa9565b6107af565b61016c6102a536600461192b565b6107e7565b6101b76102b8366004611b25565b6107f2565b61014f6102cb366004611b87565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6101b76103073660046119c6565b610829565b60006103178261089f565b92915050565b60606000805461032c90611bba565b80601f016020809104026020016040519081016040528092919081815260200182805461035890611bba565b80156103a55780601f1061037a576101008083540402835291602001916103a5565b820191906000526020600020905b81548152906001019060200180831161038857829003601f168201915b5050505050905090565b60006103ba826108c4565b506000908152600460205260409020546001600160a01b031690565b60006103e182610697565b9050806001600160a01b0316836001600160a01b0316036104535760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b038216148061046f575061046f81336102cb565b6104e15760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000606482015260840161044a565b6104eb8383610923565b505050565b6104fb335b82610991565b6105175760405162461bcd60e51b815260040161044a90611bf4565b6104eb838383610a10565b600061052d836106f7565b821061058f5760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b606482015260840161044a565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b6104eb838383604051806020016040528060008152506107af565b6105dc336104f5565b6105f85760405162461bcd60e51b815260040161044a90611bf4565b61060181610b81565b50565b600061060f60085490565b82106106725760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b606482015260840161044a565b6008828154811061068557610685611c41565b90600052602060002001549050919050565b6000818152600260205260408120546001600160a01b0316806103175760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b60006001600160a01b0382166107615760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b606482015260840161044a565b506001600160a01b031660009081526003602052604090205490565b610785610b8a565b61078f6000610be4565b565b60606001805461032c90611bba565b6107ab338383610c36565b5050565b6107b93383610991565b6107d55760405162461bcd60e51b815260040161044a90611bf4565b6107e184848484610d04565b50505050565b606061031782610d37565b6107fa610b8a565b6000610805600c5490565b9050610815600c80546001019055565b61081f8382610e4b565b6104eb8183610e65565b610831610b8a565b6001600160a01b0381166108965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161044a565b61060181610be4565b60006001600160e01b0319821663780e9d6360e01b1480610317575061031782610ef8565b6000818152600260205260409020546001600160a01b03166106015760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061095882610697565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60008061099d83610697565b9050806001600160a01b0316846001600160a01b031614806109e457506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b80610a085750836001600160a01b03166109fd846103af565b6001600160a01b0316145b949350505050565b826001600160a01b0316610a2382610697565b6001600160a01b031614610a495760405162461bcd60e51b815260040161044a90611c57565b6001600160a01b038216610aab5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b606482015260840161044a565b610ab88383836001610f48565b826001600160a01b0316610acb82610697565b6001600160a01b031614610af15760405162461bcd60e51b815260040161044a90611c57565b600081815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260038552838620805460001901905590871680865283862080546001019055868652600290945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61060181610f54565b600b546001600160a01b0316331461078f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161044a565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b031603610c975760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015260640161044a565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b610d0f848484610a10565b610d1b84848484610f94565b6107e15760405162461bcd60e51b815260040161044a90611c9c565b6060610d42826108c4565b6000828152600a602052604081208054610d5b90611bba565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8790611bba565b8015610dd45780601f10610da957610100808354040283529160200191610dd4565b820191906000526020600020905b815481529060010190602001808311610db757829003601f168201915b505050505090506000610dfe6040805180820190915260048152631d195cdd60e21b602082015290565b90508051600003610e10575092915050565b815115610e42578082604051602001610e2a929190611cee565b60405160208183030381529060405292505050919050565b610a0884611095565b6107ab828260405180602001604052806000815250611115565b6000828152600260205260409020546001600160a01b0316610ee05760405162461bcd60e51b815260206004820152602e60248201527f45524337323155524953746f726167653a2055524920736574206f66206e6f6e60448201526d32bc34b9ba32b73a103a37b5b2b760911b606482015260840161044a565b6000828152600a602052604090206104eb8282611d6b565b60006001600160e01b031982166380ac58cd60e01b1480610f2957506001600160e01b03198216635b5e139f60e01b145b8061031757506301ffc9a760e01b6001600160e01b0319831614610317565b6107e184848484611148565b610f5d81611288565b6000818152600a602052604090208054610f7690611bba565b159050610601576000818152600a6020526040812061060191611847565b60006001600160a01b0384163b1561108a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290610fd8903390899088908890600401611e2b565b6020604051808303816000875af1925050508015611013575060408051601f3d908101601f1916820190925261101091810190611e68565b60015b611070573d808015611041576040519150601f19603f3d011682016040523d82523d6000602084013e611046565b606091505b5080516000036110685760405162461bcd60e51b815260040161044a90611c9c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610a08565b506001949350505050565b60606110a0826108c4565b60006110c36040805180820190915260048152631d195cdd60e21b602082015290565b905060008151116110e3576040518060200160405280600081525061110e565b806110ed8461132b565b6040516020016110fe929190611cee565b6040516020818303038152906040525b9392505050565b61111f83836113be565b61112c6000848484610f94565b6104eb5760405162461bcd60e51b815260040161044a90611c9c565b61115484848484611557565b60018111156111c35760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e7365637574697665207472604482015274185b9cd9995c9cc81b9bdd081cdd5c1c1bdc9d1959605a1b606482015260840161044a565b816001600160a01b03851661121f5761121a81600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b611242565b836001600160a01b0316856001600160a01b0316146112425761124285826115df565b6001600160a01b03841661125e576112598161167c565b611281565b846001600160a01b0316846001600160a01b03161461128157611281848261172b565b5050505050565b600061129382610697565b90506112a3816000846001610f48565b6112ac82610697565b600083815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526003845282852080546000190190558785526002909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b606060006113388361176f565b600101905060008167ffffffffffffffff81111561135857611358611a1d565b6040519080825280601f01601f191660200182016040528015611382576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461138c57509392505050565b6001600160a01b0382166114145760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161044a565b6000818152600260205260409020546001600160a01b0316156114795760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b611487600083836001610f48565b6000818152600260205260409020546001600160a01b0316156114ec5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60018111156107e1576001600160a01b0384161561159d576001600160a01b03841660009081526003602052604081208054839290611597908490611e9b565b90915550505b6001600160a01b038316156107e1576001600160a01b038316600090815260036020526040812080548392906115d4908490611eae565b909155505050505050565b600060016115ec846106f7565b6115f69190611e9b565b600083815260076020526040902054909150808214611649576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061168e90600190611e9b565b600083815260096020526040812054600880549394509092849081106116b6576116b6611c41565b9060005260206000200154905080600883815481106116d7576116d7611c41565b600091825260208083209091019290925582815260099091526040808220849055858252812055600880548061170f5761170f611ec1565b6001900381819060005260206000200160009055905550505050565b6000611736836106f7565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106117ae5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106117da576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106117f857662386f26fc10000830492506010015b6305f5e1008310611810576305f5e100830492506008015b612710831061182457612710830492506004015b60648310611836576064830492506002015b600a83106103175760010192915050565b50805461185390611bba565b6000825580601f10611863575050565b601f01602090049060005260206000209081019061060191905b80821115611891576000815560010161187d565b5090565b6001600160e01b03198116811461060157600080fd5b6000602082840312156118bd57600080fd5b813561110e81611895565b60005b838110156118e35781810151838201526020016118cb565b50506000910152565b600081518084526119048160208601602086016118c8565b601f01601f19169290920160200192915050565b60208152600061110e60208301846118ec565b60006020828403121561193d57600080fd5b5035919050565b80356001600160a01b038116811461195b57600080fd5b919050565b6000806040838503121561197357600080fd5b61197c83611944565b946020939093013593505050565b60008060006060848603121561199f57600080fd5b6119a884611944565b92506119b660208501611944565b9150604084013590509250925092565b6000602082840312156119d857600080fd5b61110e82611944565b600080604083850312156119f457600080fd5b6119fd83611944565b915060208301358015158114611a1257600080fd5b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115611a4e57611a4e611a1d565b604051601f8501601f19908116603f01168101908282118183101715611a7657611a76611a1d565b81604052809350858152868686011115611a8f57600080fd5b858560208301376000602087830101525050509392505050565b60008060008060808587031215611abf57600080fd5b611ac885611944565b9350611ad660208601611944565b925060408501359150606085013567ffffffffffffffff811115611af957600080fd5b8501601f81018713611b0a57600080fd5b611b1987823560208401611a33565b91505092959194509250565b60008060408385031215611b3857600080fd5b611b4183611944565b9150602083013567ffffffffffffffff811115611b5d57600080fd5b8301601f81018513611b6e57600080fd5b611b7d85823560208401611a33565b9150509250929050565b60008060408385031215611b9a57600080fd5b611ba383611944565b9150611bb160208401611944565b90509250929050565b600181811c90821680611bce57607f821691505b602082108103611bee57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60008351611d008184602088016118c8565b835190830190611d148183602088016118c8565b01949350505050565b601f8211156104eb57600081815260208120601f850160051c81016020861015611d445750805b601f850160051c820191505b81811015611d6357828155600101611d50565b505050505050565b815167ffffffffffffffff811115611d8557611d85611a1d565b611d9981611d938454611bba565b84611d1d565b602080601f831160018114611dce5760008415611db65750858301515b600019600386901b1c1916600185901b178555611d63565b600085815260208120601f198616915b82811015611dfd57888601518255948401946001909101908401611dde565b5085821015611e1b5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611e5e908301846118ec565b9695505050505050565b600060208284031215611e7a57600080fd5b815161110e81611895565b634e487b7160e01b600052601160045260246000fd5b8181038181111561031757610317611e85565b8082018082111561031757610317611e85565b634e487b7160e01b600052603160045260246000fdfea26469706673582212207960bc65d7484ab1502ccdc352c91f0fb91535a525cf99e1b6392e242548279a64736f6c63430008110033
\ No newline at end of file
addedjs-packages/scripts/benchmarks/utils/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/benchmarks/utils/types.ts
@@ -0,0 +1,62 @@
+export interface IFeeGas {
+  fee?: number | bigint,
+  gas?: number | bigint,
+  substrate?: number,
+  zeppelin?: {
+    fee: number | bigint,
+    gas: number | bigint,
+  }
+
+}
+export interface IFeeGasCsv extends IFeeGasVm {
+  function: string,
+}
+export interface IFeeGasVm{
+    ethFee?: number | bigint,
+    ethGas?: number | bigint,
+    substrate?: number,
+    zeppelinFee?: number | bigint,
+    zeppelinGas?: number | bigint
+}
+export interface IFunctionFee {
+  [name: string]: IFeeGas
+}
+
+
+export abstract class FunctionFeeVM {
+  [name: string]: IFeeGasVm
+
+  static toCsv(model: FunctionFeeVM): IFeeGasCsv[]{
+    const res: IFeeGasCsv[] = [];
+    Object.keys(model).forEach(key => {
+      res.push({
+        function: key,
+        ethFee: model[key].ethFee,
+        ethGas: model[key].ethGas,
+        substrate: model[key].substrate,
+        zeppelinFee: model[key].zeppelinFee,
+        zeppelinGas: model[key].zeppelinGas,
+      });
+    });
+    return res;
+  }
+  static fromModel(model: IFunctionFee): FunctionFeeVM {
+    const res: FunctionFeeVM = {};
+
+    Object.keys(model).forEach(key => {
+      res[key] = {};
+      if(model[key].fee && model[key].gas) {
+        res[key].ethFee = model[key].fee;
+        res[key].ethGas = model[key].gas;
+      }
+      if(model[key].substrate)
+        res[key].substrate = model[key].substrate;
+      if(model[key].zeppelin) {
+        res[key].zeppelinFee = model[key].zeppelin?.fee;
+        res[key].zeppelinGas = model[key].zeppelin?.gas;
+      }
+    });
+
+    return res;
+  }
+}
\ No newline at end of file
addedjs-packages/scripts/calibrate.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/calibrate.ts
@@ -0,0 +1,318 @@
+import type {IKeyringPair} from '@polkadot/types/types';
+import {usingEthPlaygrounds} from '@unique/tests/eth/util/index.js';
+import {EthUniqueHelper} from '@unique/tests//eth/util/playgrounds/unique.dev.js';
+
+class Fract {
+  static ZERO = new Fract(0n);
+  constructor(public readonly a: bigint, public readonly b: bigint = 1n) {
+    if(b === 0n) throw new Error('division by zero');
+    if(b < 0n) throw new Error('missing normalization');
+  }
+
+  mul(other: Fract) {
+    return new Fract(this.a * other.a, this.b * other.b).optimize();
+  }
+
+  div(other: Fract) {
+    return this.mul(other.inv());
+  }
+
+  plus(other: Fract) {
+    if(this.b === other.b) {
+      return new Fract(this.a + other.a, this.b);
+    }
+    return new Fract(this.a * other.b + other.a * this.b, this.b * other.b).optimize();
+  }
+
+  minus(other: Fract) {
+    return this.plus(other.neg());
+  }
+
+  neg() {
+    return new Fract(-this.a, this.b);
+  }
+  inv() {
+    if(this.a < 0) {
+      return new Fract(-this.b, -this.a);
+    } else {
+      return new Fract(this.b, this.a);
+    }
+  }
+
+  optimize() {
+    function gcd(x: bigint, y: bigint) {
+      if(x < 0n)
+        x = -x;
+      if(y < 0n)
+        y = -y;
+      while(y) {
+        const t = y;
+        y = x % y;
+        x = t;
+      }
+      return x;
+    }
+    const v = gcd(this.a, this.b);
+    return new Fract(this.a / v, this.b / v);
+  }
+
+  toBigInt() {
+    return this.a / this.b;
+  }
+  toNumber() {
+    const v = this.optimize();
+    return Number(v.a) / Number(v.b);
+  }
+  toString() {
+    const v = this.optimize();
+    return `${v.a} / ${v.b}`;
+  }
+
+  lt(other: Fract) {
+    return this.a * other.b < other.a * this.b;
+  }
+  eq(other: Fract) {
+    return this.a * other.b === other.a * this.b;
+  }
+
+  sqrt() {
+    if(this.a < 0n) {
+      throw new Error('square root of negative numbers is not supported');
+    }
+
+    if(this.lt(new Fract(2n))) {
+      return this;
+    }
+
+    function newtonIteration(n: Fract, x0: Fract): Fract {
+      const x1 = rpn(n, x0, '/', x0, '+', new Fract(2n), '/');
+      if(x0.eq(x1) || x0.eq(x1.minus(new Fract(1n)))) {
+        return x0;
+      }
+      return newtonIteration(n, x1);
+    }
+
+    return newtonIteration(this, new Fract(1n));
+  }
+}
+
+type Op = Fract | '+' | '-' | '*' | '/' | 'dup' | Op[];
+function rpn(...ops: (Op)[]) {
+  const stack: Fract[] = [];
+  for(const op of ops) {
+    if(op instanceof Fract) {
+      stack.push(op);
+    } else if(op === '+') {
+      if(stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.plus(b));
+    } else if(op === '*') {
+      if(stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.mul(b));
+    } else if(op === '-') {
+      if(stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.minus(b));
+    } else if(op === '/') {
+      if(stack.length < 2)
+        throw new Error('stack underflow');
+      const b = stack.pop()!;
+      const a = stack.pop()!;
+      stack.push(a.div(b));
+    } else if(op === 'dup') {
+      if(stack.length < 1)
+        throw new Error('stack underflow');
+      const a = stack.pop()!;
+      stack.push(a);
+      stack.push(a);
+    } else if(Array.isArray(op)) {
+      stack.push(rpn(...op));
+    } else {
+      throw new Error(`unknown operand: ${op}`);
+    }
+  }
+  if(stack.length != 1)
+    throw new Error('one element should be left on stack');
+  return stack[0]!;
+}
+
+function linearRegression(points: { x: Fract, y: Fract }[]) {
+  let sumxy = Fract.ZERO;
+  let sumx = Fract.ZERO;
+  let sumy = Fract.ZERO;
+  let sumx2 = Fract.ZERO;
+  const n = points.length;
+  for(let i = 0; i < n; i++) {
+    const p = points[i];
+    sumxy = rpn(p.x, p.y, '*', sumxy, '+');
+    sumx = sumx.plus(p.x);
+    sumy = sumy.plus(p.y);
+    sumx2 = rpn(p.x, p.x, '*', sumx2, '+');
+  }
+
+  const nb = new Fract(BigInt(n));
+
+  const a = rpn(
+    [nb, sumxy, '*', sumx, sumy, '*', '-'],
+    [nb, sumx2, '*', sumx, sumx, '*', '-'],
+    '/',
+  );
+  const b = rpn(
+    [sumy, a, sumx, '*', '-'],
+    nb,
+    '/',
+  );
+
+  return {a, b};
+}
+
+const hypothesisLinear = (a: Fract, b: Fract) => (x: Fract) => rpn(x, a, '*', b, '+');
+
+// function error(points: { x: Fract, y: Fract }[], hypothesis: (a: Fract) => Fract) {
+//   return points.map(p => {
+//     const v = hypothesis(p.x);
+//     const vv = p.y;
+
+//     return rpn(v, vv, '-', 'dup', '*');
+//   }).reduce((a, b) => a.plus(b), Fract.ZERO).sqrt().div(new Fract(BigInt(points.length)));
+// }
+
+async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {
+  const alice = await privateKey('//Alice');
+  const bob = await privateKey('//Bob');
+  const dataPoints = [];
+
+  {
+    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+    await token.transfer(alice, {Substrate: bob.address});
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+
+    console.log(`\t[NFT transfer] Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+  }
+
+  const api = helper.getApi();
+  const base = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();
+  for(let i = -5; i < 5; i++) {
+    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(base + base / 1000n * BigInt(i))));
+
+    const coefficient = new Fract((await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt());
+    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+    await token.transfer(alice, {Substrate: bob.address});
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+
+    const transferPrice = new Fract(aliceBalanceBefore - aliceBalanceAfter);
+
+    dataPoints.push({x: transferPrice, y: coefficient});
+  }
+  const {a, b} = linearRegression(dataPoints);
+
+  const hyp = hypothesisLinear(a, b);
+  // console.log(`\t[NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`);
+
+  // 0.1 UNQ
+  const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(1n, 10n), '*'));
+  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toBigInt())));
+
+  {
+    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+    await token.transfer(alice, {Substrate: bob.address});
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+
+    console.log(`\t[NFT transfer] Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+  }
+}
+
+async function calibrateMinGasPrice(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {
+  const alice = await privateKey('//Alice');
+  const caller = await helper.eth.createAccountWithBalance(alice);
+  const receiver = helper.eth.createAccount();
+  const dataPoints = [];
+
+  {
+    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
+    const token = await collection.mintToken(alice, {Ethereum: caller});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
+
+    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));
+
+    console.log(`\t[ETH NFT transfer] Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+  }
+
+  const api = helper.getApi();
+  // const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt();
+  const base = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();
+  for(let i = -8; i < 8; i++) {
+    const gasPrice = base + base / 100000n * BigInt(i);
+    const gasPriceStr = '0x' + gasPrice.toString(16);
+    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice)));
+
+    const coefficient = new Fract((await api.query.configuration.minGasPriceOverride() as any).toBigInt());
+    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
+    const token = await collection.mintToken(alice, {Ethereum: caller});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
+
+    const transferPrice = new Fract(await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS})));
+
+    dataPoints.push({x: transferPrice, y: coefficient});
+  }
+
+  const {a, b} = linearRegression(dataPoints);
+
+  const hyp = hypothesisLinear(a, b);
+  // console.log(`\t[ETH NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`);
+
+  // 0.15 UNQ
+  const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(15n, 100n), '*'));
+  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toBigInt())));
+
+  {
+    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
+    const token = await collection.mintToken(alice, {Ethereum: caller});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
+
+    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));
+
+    console.log(`\t[ETH NFT transfer] Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
+  }
+}
+
+// eslint-disable-next-line @typescript-eslint/no-floating-promises
+(async () => {
+  await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {
+    // Subsequent runs reduce error, as price line is not actually straight, this is a curve
+
+    const iterations = 3;
+
+    console.log('[Calibrate WeightToFee]');
+    for(let i = 0; i < iterations; i++) {
+      await calibrateWeightToFee(helper, privateKey);
+    }
+
+    console.log();
+
+    console.log('[Calibrate MinGasPrice]');
+    for(let i = 0; i < iterations; i++) {
+      await calibrateMinGasPrice(helper, privateKey);
+    }
+  });
+})();
addedjs-packages/scripts/calibrateApply.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/calibrateApply.ts
@@ -0,0 +1,40 @@
+import {readFile, writeFile} from 'fs/promises';
+import path from 'path';
+import {makeNames, usingPlaygrounds} from '@unique/tests/util/index.js';
+
+const {dirname} = makeNames(import.meta.url);
+
+const formatNumber = (num: string): string => num.split('').reverse().join('').replace(/([0-9]{3})/g, '$1_').split('').reverse().join('').replace(/^_/, '');
+
+(async () => {
+  let weightToFeeCoefficientOverride: string;
+  let minGasPriceOverride: string;
+  await usingPlaygrounds(async (helpers, _privateKey) => {
+    weightToFeeCoefficientOverride = (await helpers.getApi().query.configuration.weightToFeeCoefficientOverride() as any).toBigInt().toString();
+    minGasPriceOverride = (await helpers.getApi().query.configuration.minGasPriceOverride() as any).toBigInt().toString();
+  });
+  const constantsFile = path.resolve(dirname, '../../primitives/common/src/constants.rs');
+  let constants = (await readFile(constantsFile)).toString();
+
+  let weight2feeFound = false;
+  constants = constants.replace(/(\/\*<weight2fee>\*\/)[0-9_]+(\/\*<\/weight2fee>\*\/)/, (_f, p, s) => {
+    weight2feeFound = true;
+    return p+formatNumber(weightToFeeCoefficientOverride)+s;
+  });
+  if(!weight2feeFound) {
+    throw new Error('failed to find weight2fee marker in source code');
+  }
+
+  let minGasPriceFound = false;
+  constants = constants.replace(/(\/\*<mingasprice>\*\/)[0-9_]+(\/\*<\/mingasprice>\*\/)/, (_f, p, s) => {
+    minGasPriceFound = true;
+    return p+formatNumber(minGasPriceOverride)+s;
+  });
+  if(!minGasPriceFound) {
+    throw new Error('failed to find mingasprice marker in source code');
+  }
+
+  await writeFile(constantsFile, constants);
+})().catch(e => {
+  console.log(e.stack);
+});
addedjs-packages/scripts/fetchMetadata.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/fetchMetadata.ts
@@ -0,0 +1,40 @@
+import {writeFile} from 'fs/promises';
+import {join} from 'path';
+import {exit} from 'process';
+import {fileURLToPath} from 'url';
+
+// TODO: Extract metadata statically with chainql.
+const url = process.env.RELAY_OPAL_HTTP_URL || process.env.RELAY_QUARTZ_HTTP_URL || process.env.RELAY_UNIQUE_HTTP_URL || process.env.RELAY_SAPPHIRE_HTTP_URL || 'http://127.0.0.1:9944';
+
+const srcDir = fileURLToPath(new URL('.', import.meta.url));
+
+for(let i = 0; i < 10; i++) {
+  try {
+    console.log(`Trying to fetch metadata, retry ${i + 1}/${10}`);
+    const response = await fetch(url, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      redirect: 'follow',
+      body: JSON.stringify({
+        jsonrpc: '2.0',
+        id: '1',
+        method: 'state_getMetadata',
+        params: [],
+      }),
+    });
+    const json = await response.json();
+    const output = join(srcDir, 'metadata.json');
+    console.log(`Received response, saving to ${output}`);
+    await writeFile(output, JSON.stringify(json));
+    exit(0);
+  } catch (e) {
+    console.error('Failed to request metadata:');
+    console.error(e);
+    console.error('Waiting 1 minute');
+    await new Promise(res => setTimeout(res, 60 * 1000));
+  }
+}
+console.error('Out of retries');
+exit(1);
addedjs-packages/scripts/generateEnv.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generateEnv.ts
@@ -0,0 +1,87 @@
+import {ApiPromise, WsProvider} from '@polkadot/api';
+import {readFile} from 'fs/promises';
+import {join} from 'path';
+import {makeNames} from '@unique/tests/util/index.js';
+
+const {dirname} = makeNames(import.meta.url);
+
+async function fetchVersion(chain: string): Promise<string> {
+  const api = await ApiPromise.create({provider: new WsProvider(chain)});
+  const last = (await api.query.system.lastRuntimeUpgrade()).toJSON();
+  await api.disconnect();
+  return (last as any).specVersion.toString();
+}
+
+function setVar(env: string, key: string, value: string): string {
+  let found = false;
+  const newEnv = env.replace(new RegExp(`${key}=.+?\n`), () => {
+    found = true;
+    return `${key}=${value}\n`;
+  });
+  if(!found) throw new Error(`env key "${key}" is not found`);
+  return newEnv;
+}
+
+// Fetch and format version string
+async function ff(url: string, regex: RegExp, rep: string | ((substring: string, ...params:any[]) => string)): Promise<string> {
+  const ver = await fetchVersion(url);
+  if(ver.match(regex) === null)
+    throw new Error(`bad regex for ${url}`);
+  return ver.replace(regex, rep as any);
+}
+function fixupUnique(version: string): string {
+  if(version === 'release-v930033')
+    return 'release-v930033-fix-gas-price';
+  if(version === 'release-v930034')
+    return 'release-v930034-fix-gas-price';
+  return version;
+}
+
+(async () => {
+  let env = (await readFile(join(dirname, '../../.env'))).toString();
+  await Promise.all([
+    ff('wss://rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'POLKADOT_MAINNET_BRANCH', v)),
+    ff('wss://statemint-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINT_BUILD_BRANCH', v)),
+    ff('wss://acala-rpc-0.aca-api.network/', /^(.)(..)(.)$/, '$1.$2.$3').then(v => env = setVar(env, 'ACALA_BUILD_BRANCH', v)),
+    ff('wss://wss.api.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONBEAM_BUILD_BRANCH', v)),
+    ff('wss://ws.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'UNIQUE_MAINNET_BRANCH', fixupUnique(v))),
+
+    ff('wss://kusama-rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'KUSAMA_MAINNET_BRANCH', v)),
+    ff('wss://statemine-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINE_BUILD_BRANCH', v)),
+    ff('wss://karura-rpc-0.aca-api.network/', /^(.)(..)(.)$/, 'release-karura-$1.$2.$3').then(v => env = setVar(env, 'KARURA_BUILD_BRANCH', v)),
+    ff('wss://wss.api.moonriver.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONRIVER_BUILD_BRANCH', v)),
+    ff('wss://ws-quartz.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'QUARTZ_MAINNET_BRANCH', fixupUnique(v))),
+
+    ff('wss://ws-westend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEWEST_MAINNET_BRANCH', v)),
+    ff('wss://westmint-rpc.polkadot.io/', /^(....)$/, 'parachains-v$1').then(v => env = setVar(env, 'WESTMINT_BUILD_BRANCH', v)),
+    ff('wss://ws-opal.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'OPAL_MAINNET_BRANCH', fixupUnique(v))),
+
+    ff('wss://ws-eastend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEEAST_MAINNET_BRANCH', v)),
+    ff('wss://ws-sapphire.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'SAPPHIRE_MAINNET_BRANCH', fixupUnique(v))),
+
+    ff('wss://rpc.astar.network/', /^(.+)$/, (_, r) => {
+      switch (r) {
+        case '55': return 'v5.3.0';
+        case '57': return 'v5.4.0';
+        case '61': return 'v5.11.0';
+        case '66': return 'v5.18.0';
+        default: throw new Error('unknown astar branch for runtime ' + r);
+      }
+    }).then(v => env = setVar(env, 'ASTAR_BUILD_BRANCH', v)),
+    ff('wss://shiden.api.onfinality.io/public-ws', /^(.+)$/, (_, r) => {
+      switch (r) {
+        case '90': return 'v4.49.0';
+        case '96': return 'v5.4.0';
+        case '100': return 'v5.10.0';
+        case '104': return 'v5.15.0';
+        case '106': return 'v5.18.0';
+        default: throw new Error('unknown shiden branch for runtime ' + r);
+      }
+    }).then(v => env = setVar(env, 'SHIDEN_BUILD_BRANCH', v)),
+  ]);
+  console.log(env);
+})().catch(e => {
+  console.error('Fatal');
+  console.error(e.stack);
+  process.exit(1);
+});
addedjs-packages/scripts/generate_types/functions.shdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/functions.sh
@@ -0,0 +1,4 @@
+
+function do_rpc {
+	curl -s --header "Content-Type: application/json" -XPOST --data "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"$1\",\"params\":[$2]}" "$RPC_URL"
+}
addedjs-packages/scripts/generate_types/generate_types_package.shdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/generate_types_package.sh
@@ -0,0 +1,208 @@
+#!/usr/bin/env bash
+
+# 2001 - cat file | cmd is fancier than cmd < file in most cases
+# 2002 - bash replacement patterns are awful
+# shellcheck disable=SC2001,SC2002
+set -eux
+
+DIR=$(realpath "$(dirname "$0")")
+TEMPLATE=$DIR/types_template
+GIT_REPO=git@github.com:UniqueNetwork/unique-types-js.git
+
+. "$DIR/functions.sh"
+
+usage() {
+	echo "Usage: [RPC_URL=http://localhost:9944] $0 <--rc|--release> [--force] [--push] [--rpc-url=http://localhost:9944]" 1>&2
+	exit 1
+}
+
+rc=
+release=
+force=
+push=
+
+for i in "$@"; do
+case $i in
+	--rc)
+		rc=1
+		if test "$release"; then usage; fi
+		;;
+	--release)
+		release=1
+		if test "$rc"; then usage; fi
+		;;
+	--force)
+		force=1
+		;;
+	--push)
+		push=1
+		;;
+	--rpc-url=*)
+		RPC_URL=${i#*=}
+		;;
+	*)
+		usage
+		;;
+esac
+done
+
+if test \( ! \( "$rc" -o "$release" \) \) -o \( "${RPC_URL=}" = "" \); then
+	usage
+elif test "$rc"; then
+	echo "Rc build"
+else
+	echo "Release build"
+fi
+
+cd "$DIR/../../"
+yarn polkadot-types
+
+version=$(do_rpc state_getRuntimeVersion "")
+spec_version=$(echo "$version" | jq -r .result.specVersion)
+spec_name=$(echo "$version" | jq -r .result.specName)
+echo "Spec version: $spec_version, name: $spec_name"
+
+case $spec_name in
+	opal)
+		package_name=@unique-nft/opal-testnet-types
+		repo_branch=opal-testnet
+		repo_tag=$repo_branch
+		;;
+	sapphire)
+		package_name=@unique-nft/sapphire-mainnet-types
+		repo_branch=sapphire-mainnet
+		repo_tag=$repo_branch
+		;;
+	quartz)
+		package_name=@unique-nft/quartz-mainnet-types
+		repo_branch=quartz-mainnet
+		repo_tag=$repo_branch
+		;;
+	unique)
+		package_name=@unique-nft/unique-mainnet-types
+		repo_branch=master
+		repo_tag=unique-mainnet
+		;;
+	*)
+		echo "unknown spec name: $spec_name"
+		exit 1
+		;;
+esac
+
+if test "$rc" = 1; then
+	if test "$spec_name" != "opal"; then
+		echo "rc types can only be based on opal spec"
+		exit 1
+	fi
+	package_name=@unique-nft/rc-types
+	repo_branch=rc
+	repo_tag=$repo_branch
+fi
+
+package_version=${spec_version:0:4}.$(echo "${spec_version:4:4}" | sed 's/^0*//').
+last_patch=NEVER
+for tag in $(git ls-remote -t --refs $GIT_REPO | cut -f 2 | sort -r); do
+	tag_prefix=refs/tags/$repo_tag-v$package_version
+	if [[ $tag == $tag_prefix* ]]; then
+		last_patch=${tag#"$tag_prefix"}
+		break;
+	fi
+done
+echo "Package version: ${package_version}X, name: $package_name"
+echo "Last published: $package_version$last_patch"
+
+if test "$last_patch" = "NEVER"; then
+	new_package_version=${package_version}0
+else
+	new_package_version=${package_version}$((last_patch+1))
+fi
+package_version=${package_version}$last_patch
+echo "New package version: $new_package_version"
+
+pjsapi_ver=^$(cat "$DIR/../package.json" | jq -r '.dependencies."@polkadot/api"' | sed -e "s/^\^//")
+tsnode_ver=^$(cat "$DIR/../package.json" | jq -r '.devDependencies."ts-node"' | sed -e "s/^\^//")
+ts_ver=^$(cat "$DIR/../package.json" | jq -r '.devDependencies."typescript"' | sed -e "s/^\^//")
+
+gen=$(mktemp -d)
+pushd "$gen"
+git clone $GIT_REPO -b $repo_branch --depth 1 .
+if test "$last_patch" != "NEVER"; then
+	git reset --hard "$repo_tag-v$package_version"
+fi
+git rm -r "*"
+popd
+
+# Using old package_version here, becaue we first check if
+# there is any difference between generated and already uplaoded types
+cat "$TEMPLATE/package.json" \
+| jq '.private = false' - \
+| jq ".name = \"$package_name\"" - \
+| jq ".version = \"$package_version\"" - \
+| jq ".peerDependencies.\"@polkadot/api\" = \"$pjsapi_ver\"" - \
+| jq ".peerDependencies.\"@polkadot/types\" = \"$pjsapi_ver\"" - \
+| jq ".devDependencies.\"@polkadot/api\" = \"$pjsapi_ver\"" - \
+| jq ".devDependencies.\"@polkadot/types\" = \"$pjsapi_ver\"" - \
+| jq ".devDependencies.\"ts-node\" = \"$tsnode_ver\"" - \
+| jq ".devDependencies.\"typescript\" = \"$ts_ver\"" - \
+> "$gen/package.json"
+for file in .gitignore .npmignore README.md tsconfig.json; do
+	cp "$TEMPLATE/$file" "$gen/"
+done
+package_name_replacement=$(printf '%s\n' "$package_name" | sed -e 's/[\/&]/\\&/g')
+sed -i "s/PKGNAME/$package_name_replacement/" "$gen/README.md"
+
+rsync -ar --exclude .gitignore types/ "$gen"
+for file in "$gen"/augment-* "$gen"/**/types.ts "$gen/registry.ts"; do
+	sed -i '1s;^;//@ts-nocheck\n;' "$file"
+done
+
+pushd "$gen"
+git add .
+popd
+
+pushd "$gen"
+if git diff --quiet HEAD && test ! "$force"; then
+	echo "no changes detected"
+	exit 0
+fi
+popd
+
+mv "$gen/package.json" "$gen/package.old.json"
+cat "$gen/package.old.json" \
+| jq ".version = \"$new_package_version\"" - \
+> "$gen/package.json"
+rm "$gen/package.old.json"
+pushd "$gen"
+git add package.json
+popd
+
+echo "package.json contents:"
+cat "$gen/package.json"
+echo "overall diff:"
+pushd "$gen"
+git status
+git diff HEAD || true
+popd
+
+# This check is only active if running in interactive terminal
+if [ -t 0 ]; then
+	read -p "Is everything ok at $gen [y/n]? " -n 1 -r
+	echo
+	if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+		echo "Aborting!"
+		exit 1
+	fi
+fi
+
+pushd "$gen"
+yarn
+yarn prepublish
+git commit -m "chore: upgrade types to v$new_package_version"
+git tag --force "$repo_tag-v$new_package_version"
+if test "$push" = 1; then
+	git push --tags --force -u origin HEAD
+else
+	echo "--push not given, origin repo left intact"
+	echo "To publish manually, go to $gen, and run \"git push --tags --force -u origin HEAD\""
+fi
+popd
addedjs-packages/scripts/generate_types/types_template/.gitignorediffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/types_template/.gitignore
@@ -0,0 +1,5 @@
+*.js
+*.map
+*.d.ts
+/node_modules
+metadata.json
addedjs-packages/scripts/generate_types/types_template/.npmignorediffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/types_template/.npmignore
@@ -0,0 +1 @@
+/src
addedjs-packages/scripts/generate_types/types_template/README.mddiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/types_template/README.md
@@ -0,0 +1,31 @@
+# PKGNAME
+
+Unique network api types
+
+Do not edit by hand, those types are generated automatically, and definitions are located in chain repo
+
+## Using types
+
+Install library:
+
+```bash
+yarn add --dev PKGNAME
+```
+
+Replace polkadot.js types with our chain types adding corresponding path override to the tsconfig `compilerOptions.paths` section:
+
+```json
+// in tsconfig.json
+{
+  "compilerOptions": {
+    "paths": {
+      "@polkadot/types/lookup": ["node_modules/PKGNAME/types-lookup"]
+    }
+  }
+}
+```
+
+Since polkadot v7 api augmentations not loaded by default, in every file, where you need to access `api.tx`, `api.query`, `api.rpc`, etc; you should explicitly import corresponding augmentation before any other `polkadot.js` related import:
+```
+import 'PKGNAME/augment-api';
+```
addedjs-packages/scripts/generate_types/types_template/package.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/types_template/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "TODO",
+  "private": true,
+  "version": "TODO",
+  "main": "index.js",
+  "repository": "git@github.com:UniqueNetwork/unique-types-js.git",
+  "homepage": "https://unique.network/",
+  "license": "MIT",
+  "scripts": {
+    "prepublish": "tsc -d"
+  },
+  "peerDependencies": {
+    "@polkadot/api": "TODO",
+    "@polkadot/types": "TODO"
+  },
+  "devDependencies": {
+    "@polkadot/api": "TODO",
+    "@polkadot/types": "TODO",
+    "ts-node": "TODO",
+    "typescript": "TODO"
+  }
+}
addedjs-packages/scripts/generate_types/types_template/tsconfig.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/types_template/tsconfig.json
@@ -0,0 +1,28 @@
+{
+	"exclude": [
+		"node_modules",
+		"node_modules/**/*",
+		"../node_modules/**/*",
+		"**/node_modules/**/*"
+	],
+	"compilerOptions": {
+		"target": "ES2020",
+		"moduleResolution": "node",
+		"esModuleInterop": true,
+		"resolveJsonModule": true,
+		"module": "commonjs",
+		"sourceMap": true,
+		"outDir": ".",
+		"rootDir": ".",
+		"strict": true,
+		"paths": {
+		},
+		"skipLibCheck": true,
+	},
+	"include": [
+		"**/*",
+	],
+	"lib": [
+		"es2017"
+	],
+}
addedjs-packages/scripts/generate_types/wait_for_first_block.shdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/generate_types/wait_for_first_block.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+DIR=$(dirname "$0")
+
+. "$DIR/functions.sh"
+
+last_block_id=0
+block_id=0
+function get_block {
+	block_id_hex=$(do_rpc chain_getHeader | jq -r .result.number)
+	block_id=$((block_id_hex))
+	echo Id = $block_id
+}
+
+function had_new_block {
+	last_block_id=$block_id
+	get_block
+	if (( last_block_id != 0 && block_id > last_block_id )); then
+		return 0
+	fi
+	return 1
+}
+
+function reset_check {
+	last_block_id=0
+	block_id=0
+}
+
+while ! had_new_block; do
+	echo "Waiting for next block..."
+	sleep 12
+done
+reset_check
+
+echo "Chain is running, but lets wait for another block after a minute, to avoid startup flakiness."
+sleep 60
+
+while ! had_new_block; do
+	echo "Waiting for another block..."
+	sleep 12
+done
+
+echo "Chain is running!"
addedjs-packages/scripts/proposefasttrack.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/proposefasttrack.ts
@@ -0,0 +1,42 @@
+import {ApiPromise, WsProvider} from '@polkadot/api';
+import {blake2AsHex} from '@polkadot/util-crypto';
+
+async function main() {
+  const networkUrl = process.argv[2];
+
+  const wsProvider = new WsProvider(networkUrl);
+  const api = await ApiPromise.create({provider: wsProvider});
+
+  const externalDemocracyProposal = (await api.query.democracy.nextExternal() as any).unwrap()[0];
+
+  let proposalHash;
+  if(externalDemocracyProposal.isInline) {
+    proposalHash = blake2AsHex(externalDemocracyProposal.asInline, 256);
+  } else if(externalDemocracyProposal.isLegacy) {
+    proposalHash = externalDemocracyProposal.asLegacy.toJSON().hash;
+  } else {
+    proposalHash = externalDemocracyProposal.asLookup.toJSON().hash;
+  }
+
+  const voringPeriod = 7200;
+  const delay = 10;
+
+  const democracyFastTrack = api.tx.democracy.fastTrack(proposalHash, voringPeriod, delay);
+
+  const techCommThreshold = ((await api.query.technicalCommittee.members()).toJSON() as any[]).length;
+  const techCommProposal = api.tx.technicalCommittee.propose(
+    techCommThreshold,
+    democracyFastTrack.method.toHex(),
+    democracyFastTrack.encodedLength,
+  );
+
+  const encodedCall = techCommProposal.method.toHex();
+
+  console.log('-----------------');
+  console.log('Fast Track Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`);
+  console.log('-----------------');
+
+  await api.disconnect();
+}
+
+await main();
addedjs-packages/scripts/proposeupgrade.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/proposeupgrade.ts
@@ -0,0 +1,53 @@
+import {ApiPromise, WsProvider} from '@polkadot/api';
+import {blake2AsHex} from '@polkadot/util-crypto';
+import {readFileSync} from 'fs';
+
+async function main() {
+  const networkUrl = process.argv[2];
+  const wasmFile = process.argv[3];
+
+  const wsProvider = new WsProvider(networkUrl);
+  const api = await ApiPromise.create({provider: wsProvider});
+
+  const wasmFileBytes = readFileSync(wasmFile);
+  const wasmFileHash = blake2AsHex(wasmFileBytes, 256);
+
+  const authorizeUpgrade = api.tx.parachainSystem.authorizeUpgrade(wasmFileHash, true);
+  const enableMaintenance = api.tx.maintenance.enable();
+
+  const councilMembers = (await api.query.council.members()).toJSON() as any[];
+  const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1;
+
+  const democracyProposalContent = api.tx.utility.batchAll([
+    authorizeUpgrade.method.toHex(),
+    enableMaintenance.method.toHex(),
+  ]).method.toHex();
+
+  const democracyProposalHash = blake2AsHex(democracyProposalContent, 256);
+  const democracyProposalPreimage = api.tx.preimage.notePreimage(democracyProposalContent).method.toHex();
+
+  const democracyProposal = api.tx.democracy.externalProposeDefault({
+    Legacy: democracyProposalHash,
+  });
+
+  const councilProposal = api.tx.council.propose(
+    councilProposalThreshold,
+    democracyProposal,
+    democracyProposal.method.encodedLength,
+  ).method.toHex();
+
+  const proposeUpgradeBatch = api.tx.utility.batchAll([
+    democracyProposalPreimage,
+    councilProposal,
+  ]);
+
+  const encodedCall = proposeUpgradeBatch.method.toHex();
+
+  console.log('-----------------');
+  console.log('Upgrade Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`);
+  console.log('-----------------');
+
+  await api.disconnect();
+}
+
+await main();
deletedjs-packages/scripts/src/benchmarks/mintFee/index.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/mintFee/index.ts
+++ /dev/null
@@ -1,399 +0,0 @@
-import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js';
-import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js';
-import {readFile} from 'fs/promises';
-import type {ICrossAccountId} from '@unique/playgrounds/src/types.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {UniqueNFTCollection} from '@unique/playgrounds/src/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/src/util/index.js';
-import type {ContractImports} from '@unique/tests/src/eth/util/playgrounds/types.js';
-
-const {dirname} = makeNames(import.meta.url);
-
-export const CONTRACT_IMPORT: ContractImports[] = [
-  {
-    fsPath: `${dirname}/../../../../tests/src/eth/api/CollectionHelpers.sol`,
-    solPath: 'eth/api/CollectionHelpers.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../tests/src/eth/api/ContractHelpers.sol`,
-    solPath: 'eth/api/ContractHelpers.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../tests/src/eth/api/UniqueRefungibleToken.sol`,
-    solPath: 'eth/api/UniqueRefungibleToken.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../tests/src/eth/api/UniqueRefungible.sol`,
-    solPath: 'eth/api/UniqueRefungible.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../tests/src/eth/api/UniqueNFT.sol`,
-    solPath: 'eth/api/UniqueNFT.sol',
-  },
-];
-
-interface IBenchmarkResultForProp {
-	propertiesNumber: number;
-	substrateFee: number;
-	ethFee: number;
-	ethBulkFee: number;
-  ethMintCrossFee: number;
-	evmProxyContractFee: number;
-	evmProxyContractBulkFee: number;
-}
-
-const main = async () => {
-  const benchmarks = [
-    'substrateFee',
-    'ethFee',
-    'ethBulkFee',
-    'ethMintCrossFee',
-    'evmProxyContractFee',
-    'evmProxyContractBulkFee',
-  ];
-  const headers = [
-    'propertiesNumber',
-    ...benchmarks,
-  ];
-
-
-  const csvWriter = createObjectCsvWriter({
-    path: 'properties.csv',
-    header: headers,
-  });
-
-  await usingEthPlaygrounds(async (helper, privateKey) => {
-    const CONTRACT_SOURCE = (
-      await readFile(`${dirname}/proxyContract.sol`)
-    ).toString();
-
-    const donor = await privateKey('//Alice'); // Seed from account with balance on this network
-    const ethSigner = await helper.eth.createAccountWithBalance(donor);
-
-    const contract = await helper.ethContract.deployByCode(
-      ethSigner,
-      'ProxyMint',
-      CONTRACT_SOURCE,
-      CONTRACT_IMPORT,
-    );
-
-    const fees = await benchMintFee(helper, privateKey, contract);
-    console.log('Minting without properties');
-    console.table(fees);
-
-    const result: IBenchmarkResultForProp[] = [];
-    const csvResult: IBenchmarkResultForProp[] = [];
-
-    for(let i = 1; i <= 20; i++) {
-      const benchResult = await benchMintWithProperties(helper, privateKey, contract, {
-        propertiesNumber: i,
-      }) as any;
-
-      csvResult.push(benchResult);
-
-      const minFee = Math.min(...(benchmarks.map(x => benchResult[x])));
-      for(const key of benchmarks) {
-        const keyPercent = Math.round((benchResult[key] / minFee) * 100);
-        benchResult[key] = `${benchResult[key]} (${keyPercent}%)`;
-      }
-
-      result.push(benchResult);
-    }
-
-    await csvWriter.writeRecords(csvResult);
-
-    console.log('Minting with properties');
-    console.table(result, headers);
-  });
-};
-
-main()
-  .then(() => process.exit(0))
-  .catch((e) => {
-    console.log(e);
-    process.exit(1);
-  });
-
-
-
-async function benchMintFee(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  proxyContract: Contract,
-): Promise<{
-	substrateFee: number;
-	ethFee: number;
-	evmProxyContractFee: number;
-}> {
-  const donor = await privateKey('//Alice');
-  const substrateReceiver = await privateKey('//Bob');
-  const ethSigner = await helper.eth.createAccountWithBalance(donor);
-
-  const nominal = helper.balance.getOneTokenNominal();
-
-  await helper.eth.transferBalanceFromSubstrate(
-    donor,
-    proxyContract.options.address,
-    100n,
-  );
-
-  const collection = (await createCollectionForBenchmarks(
-    'nft',
-    helper,
-    privateKey,
-    ethSigner,
-    proxyContract.options.address,
-    PERMISSIONS,
-  )) as UniqueNFTCollection;
-
-  const substrateFee = await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintToken(donor, {Substrate: substrateReceiver.address}),
-  );
-
-  const collectionEthAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-  const collectionContract = await helper.ethNativeContract.collection(
-    collectionEthAddress,
-    'nft',
-  );
-
-  const receiverEthAddress = helper.address.substrateToEth(substrateReceiver.address);
-
-  const encodedCall = collectionContract.methods
-    .mint(receiverEthAddress)
-    .encodeABI();
-
-  const ethFee = await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    async () => {
-      await helper.eth.sendEVM(
-        donor,
-        collectionContract.options.address,
-        encodedCall,
-        '0',
-      );
-    },
-  );
-
-  const evmProxyContractFee = await helper.arrange.calculcateFee(
-    {Ethereum: ethSigner},
-    async () => {
-      await proxyContract.methods
-        .mintToSubstrate(
-          helper.ethAddress.fromCollectionId(collection.collectionId),
-          substrateReceiver.addressRaw,
-        )
-        .send({from: ethSigner});
-    },
-  );
-
-  return {
-    substrateFee: convertToTokens(substrateFee, nominal),
-    ethFee: convertToTokens(ethFee, nominal),
-    evmProxyContractFee: convertToTokens(evmProxyContractFee, nominal),
-  };
-}
-
-async function benchMintWithProperties(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  proxyContract: Contract,
-  setup: { propertiesNumber: number },
-): Promise<IBenchmarkResultForProp> {
-  const donor = await privateKey('//Alice'); // Seed from account with balance on this network
-  const ethSigner = await helper.eth.createAccountWithBalance(donor);
-
-  const susbstrateReceiver = await privateKey('//Bob');
-  const receiverEthAddress = helper.address.substrateToEth(susbstrateReceiver.address);
-
-  const nominal = helper.balance.getOneTokenNominal();
-
-  const substrateFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Substrate: donor.address},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      await collection.mintToken(
-        donor,
-        {Substrate: susbstrateReceiver.address},
-        PROPERTIES.slice(0, setup.propertiesNumber).map((p) => ({key: p.key, value: Buffer.from(p.value).toString()})),
-      );
-    },
-  );
-
-  const ethFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Substrate: donor.address},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      const evmContract = await helper.ethNativeContract.collection(
-        helper.ethAddress.fromCollectionId(collection.collectionId),
-        'nft',
-        undefined,
-        true,
-      );
-
-      const subTokenId = await evmContract.methods.nextTokenId().call();
-
-      let encodedCall = evmContract.methods
-        .mint(receiverEthAddress)
-        .encodeABI();
-
-      await helper.eth.sendEVM(
-        donor,
-        evmContract.options.address,
-        encodedCall,
-        '0',
-      );
-
-      for(const val of PROPERTIES.slice(0, setup.propertiesNumber)) {
-        encodedCall = await evmContract.methods
-          .setProperty(subTokenId, val.key, Buffer.from(val.value))
-          .encodeABI();
-
-        await helper.eth.sendEVM(
-          donor,
-          evmContract.options.address,
-          encodedCall,
-          '0',
-        );
-      }
-    },
-  );
-
-  const ethBulkFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Substrate: donor.address},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      const evmContract = await helper.ethNativeContract.collection(
-        helper.ethAddress.fromCollectionId(collection.collectionId),
-        'nft',
-      );
-
-      const subTokenId = await evmContract.methods.nextTokenId().call();
-
-      let encodedCall = evmContract.methods
-        .mint(receiverEthAddress)
-        .encodeABI();
-
-      await helper.eth.sendEVM(
-        donor,
-        evmContract.options.address,
-        encodedCall,
-        '0',
-      );
-
-      encodedCall = await evmContract.methods
-        .setProperties(
-          subTokenId,
-          PROPERTIES.slice(0, setup.propertiesNumber),
-        )
-        .encodeABI();
-
-      await helper.eth.sendEVM(
-        donor,
-        evmContract.options.address,
-        encodedCall,
-        '0',
-      );
-    },
-  );
-
-  const ethMintCrossFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Ethereum: ethSigner},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      const evmContract = await helper.ethNativeContract.collection(
-        helper.ethAddress.fromCollectionId(collection.collectionId),
-        'nft',
-      );
-
-      await evmContract.methods.mintCross(
-        helper.ethCrossAccount.fromAddress(receiverEthAddress),
-        PROPERTIES.slice(0, setup.propertiesNumber),
-      )
-        .send({from: ethSigner});
-    },
-  );
-
-  const proxyContractFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Ethereum: ethSigner},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      await proxyContract.methods
-        .mintToSubstrateWithProperty(
-          helper.ethAddress.fromCollectionId(collection.collectionId),
-          susbstrateReceiver.addressRaw,
-          PROPERTIES.slice(0, setup.propertiesNumber),
-        )
-        .send({from: ethSigner});
-    },
-  );
-
-  const proxyContractBulkFee = await calculateFeeNftMintWithProperties(
-    helper,
-    privateKey,
-    {Ethereum: ethSigner},
-    ethSigner,
-    proxyContract.options.address,
-    async (collection) => {
-      await proxyContract.methods
-        .mintToSubstrateBulkProperty(
-          helper.ethAddress.fromCollectionId(collection.collectionId),
-          susbstrateReceiver.addressRaw,
-          PROPERTIES.slice(0, setup.propertiesNumber),
-        )
-        .send({from: ethSigner});
-    },
-  );
-
-  return {
-    propertiesNumber: setup.propertiesNumber,
-    substrateFee: convertToTokens(substrateFee, nominal),
-    ethFee: convertToTokens(ethFee, nominal),
-    ethBulkFee: convertToTokens(ethBulkFee, nominal),
-    ethMintCrossFee: convertToTokens(ethMintCrossFee, nominal),
-    evmProxyContractFee: convertToTokens(proxyContractFee, nominal),
-    evmProxyContractBulkFee: convertToTokens(proxyContractBulkFee, nominal),
-  };
-}
-
-async function calculateFeeNftMintWithProperties(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  payer: ICrossAccountId,
-  ethSigner: string,
-  proxyContractAddress: string,
-  calculatedCall: (collection: UniqueNFTCollection) => Promise<any>,
-): Promise<bigint> {
-  const collection = (await createCollectionForBenchmarks(
-    'nft',
-    helper,
-    privateKey,
-    ethSigner,
-    proxyContractAddress,
-    PERMISSIONS,
-  )) as UniqueNFTCollection;
-  return helper.arrange.calculcateFee(payer, async () => {
-    await calculatedCall(collection);
-  });
-}
-
-
-
deletedjs-packages/scripts/src/benchmarks/mintFee/proxyContract.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/mintFee/proxyContract.sol
+++ /dev/null
@@ -1,153 +0,0 @@
-// SPDX-License-Identifier:  Apache License
-pragma solidity >=0.8.0;
-import {CollectionHelpers} from "eth/api/CollectionHelpers.sol";
-import {ContractHelpers} from "eth/api/ContractHelpers.sol";
-import {UniqueRefungibleToken} from "eth/api/UniqueRefungibleToken.sol";
-import {UniqueRefungible, Collection, CrossAddress as RftCrossAccountId, Property as RftProperty} from "eth/api/UniqueRefungible.sol";
-import {UniqueNFT, CrossAddress as NftCrossAccountId, Property as NftProperty} from "eth/api/UniqueNFT.sol";
-
-struct Property {
-	string key;
-	bytes value;
-}
-
-interface SoftDeprecatedMethods {
-	/// @notice Set token property value.
-	/// @dev Throws error if `msg.sender` has no permission to edit the property.
-	/// @param tokenId ID of the token.
-	/// @param key Property key.
-	/// @param value Property value.
-	/// @dev EVM selector for this function is: 0x1752d67b,
-	///  or in textual repr: setProperty(uint256,string,bytes)
-	function setProperty(
-		uint256 tokenId,
-		string memory key,
-		bytes memory value
-	) external;
-}
-
-interface BenchUniqueRefungible is UniqueRefungible, SoftDeprecatedMethods {}
-interface BenchUniqueNFT is UniqueNFT, SoftDeprecatedMethods {}
-
-
-
-contract ProxyMint {
-	bytes32 constant REFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("ReFungible"));
-	bytes32 constant NONFUNGIBLE_COLLECTION_TYPE = keccak256(bytes("NFT"));
-
-	modifier checkRestrictions(address _collection) {
-		Collection commonContract = Collection(_collection);
-		require(
-			commonContract.isOwnerOrAdminCross(RftCrossAccountId(msg.sender, 0)),
-			"Only collection admin/owner can call this method"
-		);
-		_;
-	}
-
-	/// @dev This emits when a mint to a substrate address has been made.
-	event MintToSub(address _toEth, uint256 _toSub, address _collection, uint256 _tokenId);
-
-	function mintToSubstrate(address _collection, uint256 _substrateReceiver) external checkRestrictions(_collection) {
-		Collection commonContract = Collection(_collection);
-		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
-		uint256 tokenId;
-
-		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
-			UniqueRefungible rftCollection = UniqueRefungible(_collection);
-
-			tokenId = rftCollection.mint(address(this));
-
-			rftCollection.transferFromCross(
-				RftCrossAccountId(address(this), 0),
-				RftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
-			UniqueNFT nftCollection = UniqueNFT(_collection);
-			tokenId = nftCollection.mint(address(this));
-
-			nftCollection.transferFromCross(
-				NftCrossAccountId(address(this), 0),
-				NftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else {
-			revert("Wrong collection type. Works only with NFT or RFT");
-		}
-
-		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
-	}
-
-	function mintToSubstrateWithProperty(
-		address _collection,
-		uint256 _substrateReceiver,
-		Property[] calldata _properties
-	) external checkRestrictions(_collection) {
-		uint256 propertiesLength = _properties.length;
-		require(propertiesLength > 0, "Properies is empty");
-
-		Collection commonContract = Collection(_collection);
-		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
-		uint256 tokenId;
-
-		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {
-			BenchUniqueRefungible rftCollection = BenchUniqueRefungible(_collection);
-			tokenId = rftCollection.nextTokenId();
-			rftCollection.mint(address(this));
-
-			for (uint256 i = 0; i < propertiesLength; ++i) {
-				rftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value);
-			}
-			rftCollection.transferFromCross(
-				RftCrossAccountId(address(this), 0),
-				RftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
-			BenchUniqueNFT nftCollection = BenchUniqueNFT(_collection);
-			tokenId = nftCollection.mint(address(this));
-			for (uint256 i = 0; i < propertiesLength; ++i) {
-				nftCollection.setProperty(tokenId, _properties[i].key, _properties[i].value);
-			}
-			nftCollection.transferFromCross(
-				NftCrossAccountId(address(this), 0),
-				NftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else {
-			revert("Wrong collection type. Works only with NFT or RFT");
-		}
-
-		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
-	}
-
-	function mintToSubstrateBulkProperty(
-		address _collection,
-		uint256 _substrateReceiver,
-		NftProperty[] calldata _properties
-	) external checkRestrictions(_collection) {
-		uint256 propertiesLength = _properties.length;
-		require(propertiesLength > 0, "Properies is empty");
-
-		Collection commonContract = Collection(_collection);
-		bytes32 collectionType = keccak256(bytes(commonContract.uniqueCollectionType()));
-		uint256 tokenId;
-
-		if (collectionType == REFUNGIBLE_COLLECTION_TYPE) {} else if (collectionType == NONFUNGIBLE_COLLECTION_TYPE) {
-			UniqueNFT nftCollection = UniqueNFT(_collection);
-			tokenId = nftCollection.mint(address(this));
-
-			nftCollection.setProperties(tokenId, _properties);
-
-			nftCollection.transferFromCross(
-				NftCrossAccountId(address(this), 0),
-				NftCrossAccountId(address(0), _substrateReceiver),
-				tokenId
-			);
-		} else {
-			revert("Wrong collection type. Works only with NFT or RFT");
-		}
-
-		emit MintToSub(address(0), _substrateReceiver, _collection, tokenId);
-	}
-}
deletedjs-packages/scripts/src/benchmarks/nesting/ABIGEN/RMRKNestableMintable.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/nesting/ABIGEN/RMRKNestableMintable.ts
+++ /dev/null
@@ -1,354 +0,0 @@
-/* Autogenerated file. Do not edit manually. */
-/* tslint:disable */
-/* eslint-disable */
-
-import type BN from "bn.js";
-import type { ContractOptions } from "web3-eth-contract";
-import type { EventLog } from "web3-core";
-import type { EventEmitter } from "events";
-import type {
-  Callback,
-  NonPayableTransactionObject,
-  BlockType,
-  ContractEventLog,
-  BaseContract,
-} from "./types.js";
-
-export interface EventOptions {
-  filter?: object;
-  fromBlock?: BlockType;
-  topics?: string[];
-}
-
-export type AllChildrenRejected = ContractEventLog<{
-  tokenId: string;
-  0: string;
-}>;
-export type Approval = ContractEventLog<{
-  owner: string;
-  approved: string;
-  tokenId: string;
-  0: string;
-  1: string;
-  2: string;
-}>;
-export type ApprovalForAll = ContractEventLog<{
-  owner: string;
-  operator: string;
-  approved: boolean;
-  0: string;
-  1: string;
-  2: boolean;
-}>;
-export type ChildAccepted = ContractEventLog<{
-  tokenId: string;
-  childIndex: string;
-  childAddress: string;
-  childId: string;
-  0: string;
-  1: string;
-  2: string;
-  3: string;
-}>;
-export type ChildProposed = ContractEventLog<{
-  tokenId: string;
-  childIndex: string;
-  childAddress: string;
-  childId: string;
-  0: string;
-  1: string;
-  2: string;
-  3: string;
-}>;
-export type ChildTransferred = ContractEventLog<{
-  tokenId: string;
-  childIndex: string;
-  childAddress: string;
-  childId: string;
-  fromPending: boolean;
-  toZero: boolean;
-  0: string;
-  1: string;
-  2: string;
-  3: string;
-  4: boolean;
-  5: boolean;
-}>;
-export type NestTransfer = ContractEventLog<{
-  from: string;
-  to: string;
-  fromTokenId: string;
-  toTokenId: string;
-  tokenId: string;
-  0: string;
-  1: string;
-  2: string;
-  3: string;
-  4: string;
-}>;
-export type Transfer = ContractEventLog<{
-  from: string;
-  to: string;
-  tokenId: string;
-  0: string;
-  1: string;
-  2: string;
-}>;
-
-export interface RMRKNestableMintable extends BaseContract {
-  constructor(
-    jsonInterface: any[],
-    address?: string,
-    options?: ContractOptions
-  ): RMRKNestableMintable;
-  clone(): RMRKNestableMintable;
-  methods: {
-    RMRK_INTERFACE(): NonPayableTransactionObject<string>;
-
-    VERSION(): NonPayableTransactionObject<string>;
-
-    acceptChild(
-      parentId: number | string | BN,
-      childIndex: number | string | BN,
-      childAddress: string,
-      childId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    addChild(
-      parentId: number | string | BN,
-      childId: number | string | BN,
-      data: string | number[]
-    ): NonPayableTransactionObject<void>;
-
-    approve(
-      to: string,
-      tokenId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    balanceOf(owner: string): NonPayableTransactionObject<string>;
-
-    "burn(uint256)"(
-      tokenId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    "burn(uint256,uint256)"(
-      tokenId: number | string | BN,
-      maxChildrenBurns: number | string | BN
-    ): NonPayableTransactionObject<string>;
-
-    childOf(
-      parentId: number | string | BN,
-      index: number | string | BN
-    ): NonPayableTransactionObject<[string, string]>;
-
-    childrenOf(
-      parentId: number | string | BN
-    ): NonPayableTransactionObject<[string, string][]>;
-
-    directOwnerOf(tokenId: number | string | BN): NonPayableTransactionObject<{
-      0: string;
-      1: string;
-      2: boolean;
-    }>;
-
-    getApproved(
-      tokenId: number | string | BN
-    ): NonPayableTransactionObject<string>;
-
-    isApprovedForAll(
-      owner: string,
-      operator: string
-    ): NonPayableTransactionObject<boolean>;
-
-    mint(
-      to: string,
-      tokenId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    name(): NonPayableTransactionObject<string>;
-
-    nestMint(
-      to: string,
-      tokenId: number | string | BN,
-      destinationId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    nestTransfer(
-      to: string,
-      tokenId: number | string | BN,
-      destinationId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    nestTransferFrom(
-      from: string,
-      to: string,
-      tokenId: number | string | BN,
-      destinationId: number | string | BN,
-      data: string | number[]
-    ): NonPayableTransactionObject<void>;
-
-    onERC721Received(
-      _operator: string,
-      _from: string,
-      _tokenId: number | string | BN,
-      _data: string | number[]
-    ): NonPayableTransactionObject<string>;
-
-    ownerOf(tokenId: number | string | BN): NonPayableTransactionObject<string>;
-
-    pendingChildOf(
-      parentId: number | string | BN,
-      index: number | string | BN
-    ): NonPayableTransactionObject<[string, string]>;
-
-    pendingChildrenOf(
-      parentId: number | string | BN
-    ): NonPayableTransactionObject<[string, string][]>;
-
-    rejectAllChildren(
-      tokenId: number | string | BN,
-      maxRejections: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    safeMint(to: string): NonPayableTransactionObject<void>;
-
-    "safeTransferFrom(address,address,uint256)"(
-      from: string,
-      to: string,
-      tokenId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    "safeTransferFrom(address,address,uint256,bytes)"(
-      from: string,
-      to: string,
-      tokenId: number | string | BN,
-      data: string | number[]
-    ): NonPayableTransactionObject<void>;
-
-    setApprovalForAll(
-      operator: string,
-      approved: boolean
-    ): NonPayableTransactionObject<void>;
-
-    supportsInterface(
-      interfaceId: string | number[]
-    ): NonPayableTransactionObject<boolean>;
-
-    symbol(): NonPayableTransactionObject<string>;
-
-    transfer(
-      to: string,
-      tokenId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-
-    transferChild(
-      tokenId: number | string | BN,
-      to: string,
-      destinationId: number | string | BN,
-      childIndex: number | string | BN,
-      childAddress: string,
-      childId: number | string | BN,
-      isPending: boolean,
-      data: string | number[]
-    ): NonPayableTransactionObject<void>;
-
-    transferFrom(
-      from: string,
-      to: string,
-      tokenId: number | string | BN
-    ): NonPayableTransactionObject<void>;
-  };
-  events: {
-    AllChildrenRejected(cb?: Callback<AllChildrenRejected>): EventEmitter;
-    AllChildrenRejected(
-      options?: EventOptions,
-      cb?: Callback<AllChildrenRejected>
-    ): EventEmitter;
-
-    Approval(cb?: Callback<Approval>): EventEmitter;
-    Approval(options?: EventOptions, cb?: Callback<Approval>): EventEmitter;
-
-    ApprovalForAll(cb?: Callback<ApprovalForAll>): EventEmitter;
-    ApprovalForAll(
-      options?: EventOptions,
-      cb?: Callback<ApprovalForAll>
-    ): EventEmitter;
-
-    ChildAccepted(cb?: Callback<ChildAccepted>): EventEmitter;
-    ChildAccepted(
-      options?: EventOptions,
-      cb?: Callback<ChildAccepted>
-    ): EventEmitter;
-
-    ChildProposed(cb?: Callback<ChildProposed>): EventEmitter;
-    ChildProposed(
-      options?: EventOptions,
-      cb?: Callback<ChildProposed>
-    ): EventEmitter;
-
-    ChildTransferred(cb?: Callback<ChildTransferred>): EventEmitter;
-    ChildTransferred(
-      options?: EventOptions,
-      cb?: Callback<ChildTransferred>
-    ): EventEmitter;
-
-    NestTransfer(cb?: Callback<NestTransfer>): EventEmitter;
-    NestTransfer(
-      options?: EventOptions,
-      cb?: Callback<NestTransfer>
-    ): EventEmitter;
-
-    Transfer(cb?: Callback<Transfer>): EventEmitter;
-    Transfer(options?: EventOptions, cb?: Callback<Transfer>): EventEmitter;
-
-    allEvents(options?: EventOptions, cb?: Callback<EventLog>): EventEmitter;
-  };
-
-  once(event: "AllChildrenRejected", cb: Callback<AllChildrenRejected>): void;
-  once(
-    event: "AllChildrenRejected",
-    options: EventOptions,
-    cb: Callback<AllChildrenRejected>
-  ): void;
-
-  once(event: "Approval", cb: Callback<Approval>): void;
-  once(event: "Approval", options: EventOptions, cb: Callback<Approval>): void;
-
-  once(event: "ApprovalForAll", cb: Callback<ApprovalForAll>): void;
-  once(
-    event: "ApprovalForAll",
-    options: EventOptions,
-    cb: Callback<ApprovalForAll>
-  ): void;
-
-  once(event: "ChildAccepted", cb: Callback<ChildAccepted>): void;
-  once(
-    event: "ChildAccepted",
-    options: EventOptions,
-    cb: Callback<ChildAccepted>
-  ): void;
-
-  once(event: "ChildProposed", cb: Callback<ChildProposed>): void;
-  once(
-    event: "ChildProposed",
-    options: EventOptions,
-    cb: Callback<ChildProposed>
-  ): void;
-
-  once(event: "ChildTransferred", cb: Callback<ChildTransferred>): void;
-  once(
-    event: "ChildTransferred",
-    options: EventOptions,
-    cb: Callback<ChildTransferred>
-  ): void;
-
-  once(event: "NestTransfer", cb: Callback<NestTransfer>): void;
-  once(
-    event: "NestTransfer",
-    options: EventOptions,
-    cb: Callback<NestTransfer>
-  ): void;
-
-  once(event: "Transfer", cb: Callback<Transfer>): void;
-  once(event: "Transfer", options: EventOptions, cb: Callback<Transfer>): void;
-}
deletedjs-packages/scripts/src/benchmarks/nesting/ABIGEN/index.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/nesting/ABIGEN/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-/* Autogenerated file. Do not edit manually. */
-/* tslint:disable */
-/* eslint-disable */
-export type { RMRKNestableMintable } from "./RMRKNestableMintable.js";
deletedjs-packages/scripts/src/benchmarks/nesting/ABIGEN/types.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/nesting/ABIGEN/types.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Autogenerated file. Do not edit manually. */
-/* tslint:disable */
-/* eslint-disable */
-import type BN from "bn.js";
-import type { EventEmitter } from "events";
-import type { EventLog, PromiEvent, TransactionReceipt } from "web3-core";
-import type { Contract } from "web3-eth-contract";
-
-export interface EstimateGasOptions {
-  from?: string;
-  gas?: number;
-  value?: number | string | BN;
-}
-
-export interface EventOptions {
-  filter?: object;
-  fromBlock?: BlockType;
-  topics?: string[];
-}
-
-export type Callback<T> = (error: Error, result: T) => void;
-export interface ContractEventLog<T> extends EventLog {
-  returnValues: T;
-}
-export interface ContractEventEmitter<T> extends EventEmitter {
-  on(event: "connected", listener: (subscriptionId: string) => void): this;
-  on(
-    event: "data" | "changed",
-    listener: (event: ContractEventLog<T>) => void
-  ): this;
-  on(event: "error", listener: (error: Error) => void): this;
-}
-
-export interface NonPayableTx {
-  nonce?: string | number | BN;
-  chainId?: string | number | BN;
-  from?: string;
-  to?: string;
-  data?: string;
-  gas?: string | number | BN;
-  maxPriorityFeePerGas?: string | number | BN;
-  maxFeePerGas?: string | number | BN;
-  gasPrice?: string | number | BN;
-}
-
-export interface PayableTx extends NonPayableTx {
-  value?: string | number | BN;
-}
-
-export interface NonPayableTransactionObject<T> {
-  arguments: any[];
-  call(tx?: NonPayableTx, block?: BlockType): Promise<T>;
-  send(tx?: NonPayableTx): PromiEvent<TransactionReceipt>;
-  estimateGas(tx?: NonPayableTx): Promise<number>;
-  encodeABI(): string;
-}
-
-export interface PayableTransactionObject<T> {
-  arguments: any[];
-  call(tx?: PayableTx, block?: BlockType): Promise<T>;
-  send(tx?: PayableTx): PromiEvent<TransactionReceipt>;
-  estimateGas(tx?: PayableTx): Promise<number>;
-  encodeABI(): string;
-}
-
-export type BlockType =
-  | "latest"
-  | "pending"
-  | "genesis"
-  | "earliest"
-  | number
-  | BN;
-export type BaseContract = Omit<Contract, "clone" | "once">;
deletedjs-packages/scripts/src/benchmarks/nesting/RMRKNestableMintable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/nesting/RMRKNestableMintable.sol
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.18;
-
-import "@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol";
-import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
-
-contract RMRKNestableMintable is RMRKNestable, IERC721Receiver {
-	uint256 private _counter;
-
-	constructor() RMRKNestable("RMRK", "nesting") {
-		_counter = 1;
-	}
-
-	function safeMint(address to) external {
-		_safeMint(to, _counter, "");
-		_counter++;
-	}
-
-	function mint(address to, uint256 tokenId) external {
-		_mint(to, tokenId, "");
-	}
-
-	function nestMint(address to, uint256 tokenId, uint256 destinationId) external {
-		_nestMint(to, tokenId, destinationId, "");
-	}
-
-	function nestTransfer(address to, uint256 tokenId, uint256 destinationId) public virtual {
-		nestTransferFrom(_msgSender(), to, tokenId, destinationId, "");
-	}
-
-	function transfer(address to, uint256 tokenId) public virtual {
-		transferFrom(_msgSender(), to, tokenId);
-	}
-
-	function onERC721Received(
-		address _operator,
-		address _from,
-		uint256 _tokenId,
-		bytes calldata _data
-	) external returns (bytes4) {
-		return IERC721Receiver.onERC721Received.selector;
-	}
-}
deletedjs-packages/scripts/src/benchmarks/nesting/abigen.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/nesting/abigen.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import {runTypeChain, glob, DEFAULT_FLAGS} from 'typechain';
-
-
-async function main() {
-  const cwd = process.cwd();
-
-  // find all files matching the glob
-  const allFiles = glob(cwd, ['./ABI/*.abi']);
-
-  await runTypeChain({
-    cwd,
-    filesToProcess: allFiles,
-    allFiles,
-    outDir: 'ABIGEN',
-    target: 'web3-v1',
-    flags: {...DEFAULT_FLAGS, alwaysGenerateOverloads: true, tsNocheck: false},
-  });
-}
-
-main().catch(console.error);
\ No newline at end of file
deletedjs-packages/scripts/src/benchmarks/nesting/index.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/nesting/index.ts
+++ /dev/null
@@ -1,222 +0,0 @@
-import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js';
-import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js';
-import {readFile} from 'fs/promises';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {Contract} from 'web3-eth-contract';
-import {convertToTokens} from '../utils/common.js';
-import {makeNames} from '@unique/tests/src/util/index.js';
-import type {ContractImports} from '@unique/tests/src/eth/util/playgrounds/types.js';
-import type {RMRKNestableMintable} from './ABIGEN/index.js';
-
-const {dirname} = makeNames(import.meta.url);
-
-export const CONTRACT_IMPORT: ContractImports[] = [
-  {
-    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol`,
-    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/RMRKNestable.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol`,
-    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/nestable/IERC6059.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol`,
-    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/RMRKCore.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol`,
-    solPath: '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`,
-    solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol`,
-    solPath: '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Address.sol`,
-    solPath: '@openzeppelin/contracts/utils/Address.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`,
-    solPath: '@openzeppelin/contracts/utils/Context.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`,
-    solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol`,
-    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/library/RMRKErrors.sol',
-  },
-  {
-    fsPath: `${dirname}/../../../../node_modules/@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol`,
-    solPath: '@rmrk-team/evm-contracts/contracts/RMRK/core/IRMRKCore.sol',
-  },
-  {
-    fsPath: `${dirname}/RMRKNestableMintable.sol`,
-    solPath: 'RMRKNestableMintable.sol',
-  },
-];
-
-
-const main = async () => {
-
-  await usingEthPlaygrounds(async (helper, privateKey) => {
-
-    const donor = await privateKey('//Alice'); // Seed from account with balance on this network
-
-    const eth = await measureEth(helper, donor);
-    const sub = await measureSub(helper, donor);
-    const rmrk = await measureRMRK(helper, donor);
-    console.table({susbtrate: sub, eth: eth, rmrk: rmrk});
-  });
-};
-
-async function measureRMRK(helper: EthUniqueHelper, donor: IKeyringPair) {
-  const CONTRACT_SOURCE = (
-    await readFile(`${dirname}/RMRKNestableMintable.sol`)
-  ).toString();
-  const RELAYER_SOURCE = (await readFile(`${dirname}/relayer.sol`)).toString();
-
-  const ethSigner = await helper.eth.createAccountWithBalance(donor);
-
-  const contract = await helper.ethContract.deployByCode(
-    ethSigner,
-    'RMRKNestableMintable',
-    CONTRACT_SOURCE,
-    CONTRACT_IMPORT,
-    5000000,
-  );
-
-  const relayer = await helper.ethContract.deployByCode(
-    ethSigner,
-    'Relayer',
-    RELAYER_SOURCE,
-    CONTRACT_IMPORT,
-    5000000,
-    [contract.options.address],
-  );
-
-  const relayerAddress = relayer.options.address;
-
-  const rmrk = contract as any as RMRKNestableMintable;
-  const createTokenFor = async (receiver: string) => {
-    const tokenReceipt = await rmrk.methods.safeMint(receiver).send({from: ethSigner});
-    return tokenReceipt.events!['Transfer'].returnValues.tokenId as number;
-  };
-
-
-  const nestId = await createTokenFor(ethSigner);
-  const outerCollectionNestedId = 10;
-  const contractOwnedNestId = await createTokenFor(relayerAddress);
-  const nextTokenId = contractOwnedNestId + 1;
-
-  const nestTransfer = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {
-    const addChildData = rmrk.methods.addChild(nestId, outerCollectionNestedId, []).encodeABI();
-    await relayer.methods.relay(addChildData).send({from: ethSigner});
-    await rmrk.methods.acceptChild(nestId, 0, relayerAddress, outerCollectionNestedId).send({from: ethSigner});
-  });
-
-
-  const nestMint = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {
-    const nestMintData = rmrk.methods.nestMint(rmrk.options.address, nextTokenId, contractOwnedNestId).encodeABI();
-    await relayer.methods.relay(nestMintData).send({from: ethSigner});
-    const acceptNestedToken = rmrk.methods.acceptChild(contractOwnedNestId, 0, rmrk.options.address, nextTokenId).encodeABI();
-    await relayer.methods.relay(acceptNestedToken).send({from: ethSigner});
-  });
-
-
-  const unnestToken = await helper.arrange.calculcateFee({Ethereum: ethSigner}, async () => {
-    const unnestData = rmrk.methods.transferChild(contractOwnedNestId, ethSigner, 0, 0, rmrk.options.address, nextTokenId, false, []).encodeABI();
-    await relayer.methods.relay(unnestData).send({from: ethSigner});
-  });
-
-  return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)};
-}
-
-async function measureEth(helper: EthUniqueHelper, donor: IKeyringPair) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const {collectionId, contract} = await createNestingCollection(helper, owner);
-
-  // Create a token to be nested to
-  const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-  const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;
-  const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);
-
-  // Create a nested token
-  const nestMint = await helper.arrange.calculcateFee({Ethereum: owner}, async () => {
-    await contract.methods.mint(targetNftTokenAddress).send({from: owner});
-  });
-
-  // Create a token to be nested and nest
-  const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-  const nestedTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;
-
-  const nestTransfer = await helper.arrange.calculcateFee({Ethereum: owner}, async () => {
-    await contract.methods.transfer(targetNftTokenAddress, nestedTokenId).send({from: owner});
-  });
-
-  const unnestToken = await helper.arrange.calculcateFee({Ethereum: owner}, async () => {
-    await contract.methods.transferFrom(targetNftTokenAddress, owner, nestedTokenId).send({from: owner});
-  });
-  return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)};
-}
-
-async function measureSub(helper: EthUniqueHelper, donor: IKeyringPair) {
-
-  const [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-
-  const targetNFTCollection = await helper.nft.mintCollection(alice);
-  const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
-
-
-  const collectionForNesting = await helper.nft.mintCollection(bob);
-  // permissions should be set:
-  await targetNFTCollection.setPermissions(alice, {
-    nesting: {tokenOwner: true},
-  });
-
-  const nestMint = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => {
-    await (collectionForNesting.mintToken(bob, targetTokenBob.nestingAccount()));
-  });
-
-  const nestedToken2 = await collectionForNesting.mintToken(bob);
-  const nestTransfer = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => {
-    await nestedToken2.nest(bob, targetTokenBob);
-  });
-
-  const unnestToken = await helper.arrange.calculcateFee({Substrate: bob.address}, async () => {
-    await nestedToken2.transferFrom(bob, targetTokenBob.nestingAccount(), {Substrate: bob.address});
-  });
-
-  return {mint: convertToTokens(nestMint), transfer: convertToTokens(nestTransfer), unnest: convertToTokens(unnestToken)};
-}
-
-
-const createNestingCollection = async (
-  helper: EthUniqueHelper,
-  owner: string,
-): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {
-  const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-  const contract =  helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-  await contract.methods.setCollectionNesting(true).send({from: owner});
-
-  return {collectionId, collectionAddress, contract};
-};
-
-main()
-  .then(() => process.exit(0))
-  .catch((e) => {
-    console.log(e);
-    process.exit(1);
-  });
-
-
-
-
-
-
deletedjs-packages/scripts/src/benchmarks/nesting/relayer.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/nesting/relayer.sol
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.18;
-
-import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
-import "./RMRKNestableMintable.sol";
-
-contract Relayer is RMRKNestableMintable {
-	address _receiver;
-
-	constructor(address recevier) {
-		_receiver = recevier;
-	}
-
-	function relay(bytes calldata payload) external {
-		(bool succes, bytes memory _returnData) = address(_receiver).call(payload);
-		require(succes);
-	}
-}
deletedjs-packages/scripts/src/benchmarks/opsFee/index.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/opsFee/index.ts
+++ /dev/null
@@ -1,892 +0,0 @@
-import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js';
-import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js';
-import {readFile} from 'fs/promises';
-import {CollectionLimitField,  CreateCollectionData,  TokenPermissionField} from '@unique/tests/src/eth/util/playgrounds/types.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {UniqueFTCollection, UniqueNFTCollection} from '@unique/playgrounds/src/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/src/util/index.js';
-
-
-const {dirname} = makeNames(import.meta.url);
-
-const main = async () => {
-
-  const headers = [
-    'function',
-    'ethFee',
-    'ethGas',
-    'substrate',
-    'zeppelinFee',
-    'zeppelinGas',
-  ];
-
-  const csvWriter20 = createObjectCsvWriter({
-    path: 'erc20.csv',
-    header: headers,
-  });
-
-  const csvWriter721 = createObjectCsvWriter({
-    path: 'erc721.csv',
-    header: headers,
-  });
-
-  await usingEthPlaygrounds(async (helper, privateKey) => {
-    console.log('\n ERC20 ops fees');
-    const erc20 = FunctionFeeVM.fromModel(await erc20CalculateFeeGas(helper, privateKey));
-    console.table(erc20);
-    await csvWriter20.writeRecords(FunctionFeeVM.toCsv(erc20));
-
-    console.log('\n ERC721 ops fees');
-    const erc721 = FunctionFeeVM.fromModel(await erc721CalculateFeeGas(helper, privateKey));
-    console.table(erc721);
-
-    await csvWriter721.writeRecords(FunctionFeeVM.toCsv(erc721));
-  });
-};
-
-main()
-  .then(() => process.exit(0))
-  .catch((e) => {
-    console.log(e);
-    process.exit(1);
-  });
-
-async function erc721CalculateFeeGas(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-): Promise<IFunctionFee> {
-  const res: IFunctionFee = {};
-  const donor = await privateKey('//Alice');
-  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);
-  const ethSigner = await helper.eth.createAccountWithBalance(donor);
-  const ethReceiver = await helper.eth.createAccountWithBalance(donor);
-  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);
-  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);
-  const collection = (await createCollectionForBenchmarks(
-    'nft',
-    helper,
-    privateKey,
-    ethSigner,
-    null,
-    PERMISSIONS,
-  )) as UniqueNFTCollection;
-
-  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);
-  let zeppelelinContract: Contract | null = null;
-  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.bin`)).toString();
-  const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC721/bin/ZeppelinContract.abi`)).toString());
-
-  const evmContract = await helper.ethNativeContract.collection(
-    helper.ethAddress.fromCollectionId(collection.collectionId),
-    'nft',
-    ethSigner,
-    true,
-  );
-
-  res['createCollection'] = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    () => helperContract.methods.createNFTCollection('test','test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),
-  );
-
-  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.nft.mintCollection(
-      donor,
-      {name: 'test', description: 'test', tokenPrefix: 'test'},
-    ),
-  )));
-
-  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    async () => {
-      zeppelelinContract = await helper.ethContract.deployByAbi(
-        ethSigner,
-        ZEPPELIN_ABI,
-        ZEPPELIN_OBJECT,
-      );
-    },
-  );
-
-  res['mint'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mint(ethSigner).send(),
-    );
-
-  res['mint'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.safeMint(ethSigner, '').send({from: ethSigner}),
-    );
-
-  res['mintCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintCross(crossSigner, []).send(),
-    );
-
-  res['mint'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintToken(
-      donor,
-      {Substrate: donor.address},
-    ),
-  )));
-
-  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintMultipleTokens(donor, [{
-      owner: {Substrate: donor.address},
-    }]),
-  )));
-
-  res['mintWithTokenURI'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintWithTokenURI(ethSigner, 'Test URI').send(),
-    );
-
-  res['mintWithTokenURI'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mintToken(
-      donor,
-      {Ethereum: ethSigner},
-      [{key: 'URI', value: 'Test URI'}],
-    ),
-  )));
-
-  res['mintWithTokenURI'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.safeMint(ethSigner, 'Test URI').send({from: ethSigner}),
-    );
-
-  res['setProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setProperties(1, PROPERTIES.slice(0,1)).send(),
-    );
-
-  res['deleteProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.deleteProperties(1, [PROPERTIES[0].key]).send(),
-    );
-
-  res['setProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setTokenProperties(
-      donor,
-      1,
-      SUBS_PROPERTIES.slice(0, 1),
-    ),
-  )));
-
-  res['deleteProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.deleteTokenProperties(
-      donor,
-      1,
-      SUBS_PROPERTIES.slice(0, 1)
-        .map(p => p.key),
-    ),
-  )));
-
-  res['transfer'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transfer(ethReceiver, 1).send(),
-    );
-
-  res['safeTransferFrom*'] = {
-    zeppelin:
-      await helper.arrange.calculcateFeeGas(
-        {Ethereum: ethSigner},
-        () => zeppelelinContract!.methods.safeTransferFrom(ethSigner, ethReceiver, 0).send({from: ethSigner}),
-      ),
-  };
-
-  res['transferCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),
-    );
-
-  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transferToken(
-      donor,
-      3,
-      {Substrate: subReceiver.address},
-    ),
-  )));
-  await collection.approveToken(subReceiver, 3, {Substrate: donor.address});
-
-  res['transferFrom*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),
-    );
-
-  res['transferFrom*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 0).send({from: ethReceiver}),
-    );
-
-
-  res['transferFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),
-    );
-
-  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transferTokenFrom(donor, 3, {Substrate: subReceiver.address}, {Substrate: donor.address}),
-  )));
-
-  res['burn'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.burn(1).send(),
-    );
-
-  res['burn'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.burnToken(donor, 3),
-  )));
-
-  res['approve*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approve(ethReceiver, 2).send(),
-    );
-
-  res['approve*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.approve(ethReceiver, 0).send({from: ethSigner}),
-    );
-
-  res['approveCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approveCross(crossReceiver, 2).send(),
-    );
-
-  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.approveToken(donor, 4, {Substrate: subReceiver.address}),
-  )));
-
-  res['setApprovalForAll*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setApprovalForAll(ethReceiver, true).send(),
-    );
-
-  res['setApprovalForAll*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.setApprovalForAll(ethReceiver, true).send({from: ethSigner}),
-    );
-
-  res['setApprovalForAll*'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.nft.setAllowanceForAll(donor, collection.collectionId, {Substrate: subReceiver.address}, true),
-  )));
-
-  res['burnFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.burnFromCross(crossSigner, 2).send({from:ethReceiver}),
-    );
-
-  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.burnTokenFrom(subReceiver, 4, {Substrate: donor.address}),
-  )));
-
-  res['setTokenPropertyPermissions'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setTokenPropertyPermissions([
-        ['url', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-      ]).send(),
-    );
-
-  res['setTokenPropertyPermissions'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setTokenPropertyPermissions(donor, [{key: 'url', permission: {
-      tokenOwner: true,
-      collectionAdmin: true,
-      mutable: true,
-    }}]),
-  )));
-
-  res['setCollectionSponsorCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),
-    );
-
-  res['confirmCollectionSponsorship'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),
-    );
-
-  res['removeCollectionSponsor'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeCollectionSponsor().send(),
-    );
-
-  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setSponsor(donor, subReceiver.address),
-  )));
-
-  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.confirmSponsorship(subReceiver),
-  )));
-
-  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeSponsor(donor),
-  )));
-
-  res['setCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),
-    );
-
-  res['deleteCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),
-    );
-  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => ({key: p.key, value: p.value.toString()}))),
-  )));
-
-  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => p.key)),
-  )));
-
-  res['setCollectionLimit'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),
-    );
-
-  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),
-  )));
-
-  const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send();
-  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);
-
-
-  res['addCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['removeCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionNesting'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true).send(),
-    );
-
-  res['setCollectionNesting[]'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),
-    );
-
-  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.disableNesting(donor),
-  )));
-
-  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(
-      donor,
-      {
-        nesting: {
-          tokenOwner: true,
-          restricted: [collection.collectionId],
-        },
-      },
-    ),
-  )));
-
-  res['setCollectionAccess'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionAccess(1).send(),
-    );
-
-  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {access: 'AllowList'}),
-  )));
-
-  res['addToCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['removeFromCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionMintMode'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionMintMode(true).send(),
-    );
-
-  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {mintMode: false}),
-  )));
-
-  res['changeCollectionOwnerCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),
-    );
-
-  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.changeOwner(donor, subReceiver.address),
-  )));
-
-  return res;
-}
-
-async function erc20CalculateFeeGas(
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-
-): Promise<IFunctionFee> {
-  const res: IFunctionFee = {};
-  const donor = await privateKey('//Alice');
-  const [subReceiver] = await helper.arrange.createAccounts([10n], donor);
-  const ethSigner = await helper.eth.createAccountWithBalance(donor);
-  const ethReceiver = await helper.eth.createAccountWithBalance(donor);
-  const crossSigner = helper.ethCrossAccount.fromAddress(ethSigner);
-  const crossReceiver = helper.ethCrossAccount.fromAddress(ethReceiver);
-  const collection = (await createCollectionForBenchmarks(
-    'ft',
-    helper,
-    privateKey,
-    ethSigner,
-    null,
-    PERMISSIONS,
-  )) as UniqueFTCollection;
-
-  const helperContract = await helper.ethNativeContract.collectionHelpers(ethSigner);
-  let zeppelelinContract: Contract | null = null;
-  const ZEPPELIN_OBJECT = '0x' + (await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.bin`)).toString();
-  const ZEPPELIN_ABI = JSON.parse((await readFile(`${dirname}/../utils/openZeppelin/ERC20/bin/ZeppelinContract.abi`)).toString());
-
-  const evmContract = await helper.ethNativeContract.collection(
-    helper.ethAddress.fromCollectionId(collection.collectionId),
-    'ft',
-    ethSigner,
-    true,
-  );
-
-  res['createCollection'] = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    () => helperContract.methods.createFTCollection('test', 18,'test','test').send({from: ethSigner, value: Number(2n * helper.balance.getOneTokenNominal())}),
-  );
-
-  res['createCollection'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.ft.mintCollection(
-      donor,
-      {name: 'test', description: 'test', tokenPrefix: 'test'},
-      18,
-    ),
-  )));
-
-  res['createCollection'].zeppelin = await helper.arrange.calculcateFeeGas(
-    {Ethereum: ethSigner},
-    async () => {
-      zeppelelinContract = await helper.ethContract.deployByAbi(
-        ethSigner,
-        ZEPPELIN_ABI,
-        ZEPPELIN_OBJECT,
-      );
-    },
-  );
-
-  res['mint'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mint(ethSigner, 1).send(),
-    );
-
-  res['mint'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.mint(ethSigner, 1).send(),
-    );
-
-  res['mintCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintCross(crossSigner, 1).send(),
-    );
-
-  res['mintCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.mint(
-      donor,
-      10n,
-      {Substrate: donor.address},
-    ),
-  )));
-
-  res['mintBulk'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.mintBulk([{to: ethSigner, amount: 1}]).send(),
-    );
-
-  res['mintBulk'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => helper.executeExtrinsic(donor, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, {
-      Fungible: new Map([
-        [JSON.stringify({Ethereum: ethSigner}), 1],
-      ]),
-    }], true),
-  )));
-
-  res['transfer*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transfer(ethReceiver, 1).send(),
-    );
-
-  res['transfer*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.transfer(ethReceiver, 1).send(),
-    );
-
-  res['transferCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferCross(crossSigner, 1).send({from: ethReceiver}),
-    );
-
-  res['transferCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transfer(
-      donor,
-      {Substrate: subReceiver.address},
-      1n,
-    ),
-  )));
-  await collection.approveTokens(subReceiver, {Substrate: donor.address}, 1n);
-
-  res['transferFrom*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.transferFrom(ethSigner, ethReceiver, 1).send(),
-    );
-
-  await zeppelelinContract!.methods.approve(ethSigner, 10).send({from: ethReceiver});
-
-  res['transferFrom*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.transferFrom(ethReceiver, ethSigner, 1).send({from: ethSigner}),
-    );
-
-  res['transferFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.transferFromCross(crossReceiver, crossSigner, 1).send({from:ethReceiver}),
-    );
-
-  res['transferFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.transferFrom(donor, {Substrate: subReceiver.address}, {Substrate: donor.address}, 1n),
-  )));
-
-
-  res['burnTokens'] = {fee: 0n, gas: 0n};
-  res['burnTokens'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.burnTokens(donor, 1n),
-  )));
-
-
-  res['approve*'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approve(ethReceiver, 2).send(),
-    );
-
-  res['approve*'].zeppelin =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => zeppelelinContract!.methods.approve(ethReceiver, 10).send(),
-    );
-
-  res['approveCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.approveCross(crossReceiver, 2).send(),
-    );
-
-  res['approveCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.approveTokens(donor, {Substrate: subReceiver.address}, 1n),
-  )));
-
-  res['burnFromCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () => evmContract.methods.burnFromCross(crossSigner, 1).send({from:ethReceiver}),
-    );
-
-  res['burnFromCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.burnTokensFrom(subReceiver, {Substrate: donor.address}, 1n),
-  )));
-
-  res['setCollectionSponsorCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionSponsorCross(crossReceiver).send(),
-    );
-
-  res['confirmCollectionSponsorship'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethReceiver},
-      () =>  evmContract.methods.confirmCollectionSponsorship().send({from: ethReceiver}),
-    );
-
-  res['removeCollectionSponsor'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeCollectionSponsor().send(),
-    );
-
-  res['setCollectionSponsorCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setSponsor(donor, subReceiver.address),
-  )));
-
-  res['confirmCollectionSponsorship'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: subReceiver.address},
-    () => collection.confirmSponsorship(subReceiver),
-  )));
-
-  res['removeCollectionSponsor'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeSponsor(donor),
-  )));
-
-  res['setCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () => evmContract.methods.setCollectionProperties(PROPERTIES.slice(0, 1)).send(),
-    );
-
-  res['deleteCollectionProperties'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.deleteCollectionProperties(PROPERTIES.slice(0,1).map(p => p.key)).send(),
-    );
-  res['setCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => ({key: p.key, value: p.value.toString()}))),
-  )));
-
-  res['deleteCollectionProperties'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.deleteProperties(donor, PROPERTIES.slice(0, 1)
-      .map(p => p.key)),
-  )));
-
-  res['setCollectionLimit'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}}).send(),
-    );
-
-  res['setCollectionLimit'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setLimits(donor, {accountTokenOwnershipLimit: 1000}),
-  )));
-
-  const {collectionAddress} = await helper.eth.createCollection(ethSigner, new CreateCollectionData('A', 'B', 'C', 'nft')).send();
-  const collectionWithEthOwner = await helper.ethNativeContract.collection(collectionAddress, 'nft', ethSigner, true);
-
-
-  res['addCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.addCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['removeCollectionAdminCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.removeCollectionAdminCross(crossReceiver).send(),
-    );
-
-  res['addCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeCollectionAdminCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeAdmin(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionNesting'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true).send(),
-    );
-
-  res['setCollectionNesting[]'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionNesting(true, [collectionAddress]).send(),
-    );
-
-  res['setCollectionNesting'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.disableNesting(donor),
-  )));
-
-  res['setCollectionNesting[]'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(
-      donor,
-      {
-        nesting: {
-          tokenOwner: true,
-          restricted: [collection.collectionId],
-        },
-      },
-    ),
-  )));
-
-  res['setCollectionAccess'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionAccess(1).send(),
-    );
-
-  res['setCollectionAccess'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {access: 'AllowList'}),
-  )));
-
-  res['addToCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.addToCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['removeFromCollectionAllowListCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.removeFromCollectionAllowListCross(crossReceiver).send(),
-    );
-
-  res['addToCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.addToAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['removeFromCollectionAllowListCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.removeFromAllowList(donor, {Ethereum: ethReceiver}),
-  )));
-
-  res['setCollectionMintMode'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  evmContract.methods.setCollectionMintMode(true).send(),
-    );
-
-  res['setCollectionMintMode'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.setPermissions(donor, {mintMode: false}),
-  )));
-
-  res['changeCollectionOwnerCross'] =
-    await helper.arrange.calculcateFeeGas(
-      {Ethereum: ethSigner},
-      () =>  collectionWithEthOwner.methods.changeCollectionOwnerCross(crossReceiver).send(),
-    );
-
-  res['changeCollectionOwnerCross'].substrate = convertToTokens((await helper.arrange.calculcateFee(
-    {Substrate: donor.address},
-    () => collection.changeOwner(donor, subReceiver.address),
-  )));
-
-  return res;
-}
deletedjs-packages/scripts/src/benchmarks/utils/common.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/common.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import {EthUniqueHelper} from '@unique/tests/src/eth/util/playgrounds/unique.dev.js';
-import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js';
-import type {ITokenPropertyPermission, TCollectionMode} from '@unique/playgrounds/src/types.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-export const PROPERTIES = Array(40)
-  .fill(0)
-  .map((_, i) => ({
-    key: `key_${i}`,
-    value: Uint8Array.from(Buffer.from(`value_${i}`)),
-  }));
-
-export const SUBS_PROPERTIES = Array(40)
-  .fill(0)
-  .map((_, i) => ({
-    key: `key_${i}`,
-    value: `value_${i}`,
-  }));
-
-export const PERMISSIONS: ITokenPropertyPermission[] = PROPERTIES.map((p) => ({
-  key: p.key,
-  permission: {
-    tokenOwner: true,
-    collectionAdmin: true,
-    mutable: true,
-  },
-}));
-
-export function convertToTokens(value: bigint, nominal = 1000_000_000_000_000_000n): number {
-  return Number((value * 1000n) / nominal) / 1000;
-}
-
-export async function createCollectionForBenchmarks(
-  mode : TCollectionMode,
-  helper: EthUniqueHelper,
-  privateKey: (seed: string) => Promise<IKeyringPair>,
-  ethSigner: string,
-  proxyContract: string | null,
-  permissions: ITokenPropertyPermission[],
-) {
-  const donor = await privateKey('//Alice');
-
-  const collection = await helper[mode].mintCollection(donor, {
-    name: 'test mintToSubstrate',
-    description: 'EVMHelpers',
-    tokenPrefix: mode,
-    ...(mode != 'ft' ? {
-      tokenPropertyPermissions: [
-        {
-          key: 'url',
-          permission: {
-            tokenOwner: true,
-            collectionAdmin: true,
-            mutable: true,
-          },
-        },
-        {
-          key: 'URI',
-          permission: {
-            tokenOwner: true,
-            collectionAdmin: true,
-            mutable: true,
-          },
-        },
-      ],
-    } : {}),
-    limits: {sponsorTransferTimeout: 0, sponsorApproveTimeout: 0},
-    permissions: {mintMode: true},
-  });
-
-  await collection.addToAllowList(donor, {
-    Ethereum: helper.address.substrateToEth(donor.address),
-  });
-  await collection.addToAllowList(donor, {Substrate: donor.address});
-  await collection.addAdmin(donor, {Ethereum: ethSigner});
-  await collection.addAdmin(donor, {
-    Ethereum: helper.address.substrateToEth(donor.address),
-  });
-
-  if(proxyContract) {
-    await collection.addToAllowList(donor, {Ethereum: proxyContract});
-    await collection.addAdmin(donor, {Ethereum: proxyContract});
-  }
-  if(collection instanceof UniqueNFTCollection || collection instanceof UniqueRFTCollection)
-    await collection.setTokenPropertyPermissions(donor, permissions);
-
-  return collection;
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/README.mddiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# OpenZeppelin Contracts
-
-The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1.
-
-They are not meant to be edited.
-
-The originals can be found on [GitHub] and [npm].
-
-[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1
-[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1
-
-Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard).
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/access/Ownable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/access/Ownable.sol
+++ /dev/null
@@ -1,83 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
-
-pragma solidity ^0.8.0;
-
-import "../utils/Context.sol";
-
-/**
- * @dev Contract module which provides a basic access control mechanism, where
- * there is an account (an owner) that can be granted exclusive access to
- * specific functions.
- *
- * By default, the owner account will be the one that deploys the contract. This
- * can later be changed with {transferOwnership}.
- *
- * This module is used through inheritance. It will make available the modifier
- * `onlyOwner`, which can be applied to your functions to restrict their use to
- * the owner.
- */
-abstract contract Ownable is Context {
-    address private _owner;
-
-    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
-
-    /**
-     * @dev Initializes the contract setting the deployer as the initial owner.
-     */
-    constructor() {
-        _transferOwnership(_msgSender());
-    }
-
-    /**
-     * @dev Throws if called by any account other than the owner.
-     */
-    modifier onlyOwner() {
-        _checkOwner();
-        _;
-    }
-
-    /**
-     * @dev Returns the address of the current owner.
-     */
-    function owner() public view virtual returns (address) {
-        return _owner;
-    }
-
-    /**
-     * @dev Throws if the sender is not the owner.
-     */
-    function _checkOwner() internal view virtual {
-        require(owner() == _msgSender(), "Ownable: caller is not the owner");
-    }
-
-    /**
-     * @dev Leaves the contract without owner. It will not be possible to call
-     * `onlyOwner` functions anymore. Can only be called by the current owner.
-     *
-     * NOTE: Renouncing ownership will leave the contract without an owner,
-     * thereby removing any functionality that is only available to the owner.
-     */
-    function renounceOwnership() public virtual onlyOwner {
-        _transferOwnership(address(0));
-    }
-
-    /**
-     * @dev Transfers ownership of the contract to a new account (`newOwner`).
-     * Can only be called by the current owner.
-     */
-    function transferOwnership(address newOwner) public virtual onlyOwner {
-        require(newOwner != address(0), "Ownable: new owner is the zero address");
-        _transferOwnership(newOwner);
-    }
-
-    /**
-     * @dev Transfers ownership of the contract to a new account (`newOwner`).
-     * Internal function without access restriction.
-     */
-    function _transferOwnership(address newOwner) internal virtual {
-        address oldOwner = _owner;
-        _owner = newOwner;
-        emit OwnershipTransferred(oldOwner, newOwner);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/governance/utils/IVotes.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/governance/utils/IVotes.sol
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.5.0) (governance/utils/IVotes.sol)
-pragma solidity ^0.8.0;
-
-/**
- * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts.
- *
- * _Available since v4.5._
- */
-interface IVotes {
-    /**
-     * @dev Emitted when an account changes their delegate.
-     */
-    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
-
-    /**
-     * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes.
-     */
-    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
-
-    /**
-     * @dev Returns the current amount of votes that `account` has.
-     */
-    function getVotes(address account) external view returns (uint256);
-
-    /**
-     * @dev Returns the amount of votes that `account` had at the end of a past block (`blockNumber`).
-     */
-    function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
-
-    /**
-     * @dev Returns the total supply of votes available at the end of a past block (`blockNumber`).
-     *
-     * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes.
-     * Votes that have not been delegated are still part of total supply, even though they would not participate in a
-     * vote.
-     */
-    function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
-
-    /**
-     * @dev Returns the delegate that `account` has chosen.
-     */
-    function delegates(address account) external view returns (address);
-
-    /**
-     * @dev Delegates votes from the sender to `delegatee`.
-     */
-    function delegate(address delegatee) external;
-
-    /**
-     * @dev Delegates votes from signer to `delegatee`.
-     */
-    function delegateBySig(
-        address delegatee,
-        uint256 nonce,
-        uint256 expiry,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) external;
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.7.0) (interfaces/IERC3156FlashBorrower.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Interface of the ERC3156 FlashBorrower, as defined in
- * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
- *
- * _Available since v4.1._
- */
-interface IERC3156FlashBorrower {
-    /**
-     * @dev Receive a flash loan.
-     * @param initiator The initiator of the loan.
-     * @param token The loan currency.
-     * @param amount The amount of tokens lent.
-     * @param fee The additional amount of tokens to repay.
-     * @param data Arbitrary data structure, intended to contain user-defined parameters.
-     * @return The keccak256 hash of "IERC3156FlashBorrower.onFlashLoan"
-     */
-    function onFlashLoan(
-        address initiator,
-        address token,
-        uint256 amount,
-        uint256 fee,
-        bytes calldata data
-    ) external returns (bytes32);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashLender.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (interfaces/IERC3156FlashLender.sol)
-
-pragma solidity ^0.8.0;
-
-import "./IERC3156FlashBorrower.sol";
-
-/**
- * @dev Interface of the ERC3156 FlashLender, as defined in
- * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
- *
- * _Available since v4.1._
- */
-interface IERC3156FlashLender {
-    /**
-     * @dev The amount of currency available to be lended.
-     * @param token The loan currency.
-     * @return The amount of `token` that can be borrowed.
-     */
-    function maxFlashLoan(address token) external view returns (uint256);
-
-    /**
-     * @dev The fee to be charged for a given loan.
-     * @param token The loan currency.
-     * @param amount The amount of tokens lent.
-     * @return The amount of `token` to be charged for the loan, on top of the returned principal.
-     */
-    function flashFee(address token, uint256 amount) external view returns (uint256);
-
-    /**
-     * @dev Initiate a flash loan.
-     * @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
-     * @param token The loan currency.
-     * @param amount The amount of tokens lent.
-     * @param data Arbitrary data structure, intended to contain user-defined parameters.
-     */
-    function flashLoan(
-        IERC3156FlashBorrower receiver,
-        address token,
-        uint256 amount,
-        bytes calldata data
-    ) external returns (bool);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/security/Pausable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/security/Pausable.sol
+++ /dev/null
@@ -1,105 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
-
-pragma solidity ^0.8.0;
-
-import "../utils/Context.sol";
-
-/**
- * @dev Contract module which allows children to implement an emergency stop
- * mechanism that can be triggered by an authorized account.
- *
- * This module is used through inheritance. It will make available the
- * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
- * the functions of your contract. Note that they will not be pausable by
- * simply including this module, only once the modifiers are put in place.
- */
-abstract contract Pausable is Context {
-    /**
-     * @dev Emitted when the pause is triggered by `account`.
-     */
-    event Paused(address account);
-
-    /**
-     * @dev Emitted when the pause is lifted by `account`.
-     */
-    event Unpaused(address account);
-
-    bool private _paused;
-
-    /**
-     * @dev Initializes the contract in unpaused state.
-     */
-    constructor() {
-        _paused = false;
-    }
-
-    /**
-     * @dev Modifier to make a function callable only when the contract is not paused.
-     *
-     * Requirements:
-     *
-     * - The contract must not be paused.
-     */
-    modifier whenNotPaused() {
-        _requireNotPaused();
-        _;
-    }
-
-    /**
-     * @dev Modifier to make a function callable only when the contract is paused.
-     *
-     * Requirements:
-     *
-     * - The contract must be paused.
-     */
-    modifier whenPaused() {
-        _requirePaused();
-        _;
-    }
-
-    /**
-     * @dev Returns true if the contract is paused, and false otherwise.
-     */
-    function paused() public view virtual returns (bool) {
-        return _paused;
-    }
-
-    /**
-     * @dev Throws if the contract is paused.
-     */
-    function _requireNotPaused() internal view virtual {
-        require(!paused(), "Pausable: paused");
-    }
-
-    /**
-     * @dev Throws if the contract is not paused.
-     */
-    function _requirePaused() internal view virtual {
-        require(paused(), "Pausable: not paused");
-    }
-
-    /**
-     * @dev Triggers stopped state.
-     *
-     * Requirements:
-     *
-     * - The contract must not be paused.
-     */
-    function _pause() internal virtual whenNotPaused {
-        _paused = true;
-        emit Paused(_msgSender());
-    }
-
-    /**
-     * @dev Returns to normal state.
-     *
-     * Requirements:
-     *
-     * - The contract must be paused.
-     */
-    function _unpause() internal virtual whenPaused {
-        _paused = false;
-        emit Unpaused(_msgSender());
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/ERC20.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/ERC20.sol
+++ /dev/null
@@ -1,389 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
-
-pragma solidity ^0.8.0;
-
-import "./IERC20.sol";
-import "./extensions/IERC20Metadata.sol";
-import "../../utils/Context.sol";
-
-/**
- * @dev Implementation of the {IERC20} interface.
- *
- * This implementation is agnostic to the way tokens are created. This means
- * that a supply mechanism has to be added in a derived contract using {_mint}.
- * For a generic mechanism see {ERC20PresetMinterPauser}.
- *
- * TIP: For a detailed writeup see our guide
- * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
- * to implement supply mechanisms].
- *
- * We have followed general OpenZeppelin Contracts guidelines: functions revert
- * instead returning `false` on failure. This behavior is nonetheless
- * conventional and does not conflict with the expectations of ERC20
- * applications.
- *
- * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
- * This allows applications to reconstruct the allowance for all accounts just
- * by listening to said events. Other implementations of the EIP may not emit
- * these events, as it isn't required by the specification.
- *
- * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
- * functions have been added to mitigate the well-known issues around setting
- * allowances. See {IERC20-approve}.
- */
-contract ERC20 is Context, IERC20, IERC20Metadata {
-    mapping(address => uint256) private _balances;
-
-    mapping(address => mapping(address => uint256)) private _allowances;
-
-    uint256 private _totalSupply;
-
-    string private _name;
-    string private _symbol;
-
-    /**
-     * @dev Sets the values for {name} and {symbol}.
-     *
-     * The default value of {decimals} is 18. To select a different value for
-     * {decimals} you should overload it.
-     *
-     * All two of these values are immutable: they can only be set once during
-     * construction.
-     */
-    constructor(string memory name_, string memory symbol_) {
-        _name = name_;
-        _symbol = symbol_;
-    }
-
-    /**
-     * @dev Returns the name of the token.
-     */
-    function name() public view virtual override returns (string memory) {
-        return _name;
-    }
-
-    /**
-     * @dev Returns the symbol of the token, usually a shorter version of the
-     * name.
-     */
-    function symbol() public view virtual override returns (string memory) {
-        return _symbol;
-    }
-
-    /**
-     * @dev Returns the number of decimals used to get its user representation.
-     * For example, if `decimals` equals `2`, a balance of `505` tokens should
-     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
-     *
-     * Tokens usually opt for a value of 18, imitating the relationship between
-     * Ether and Wei. This is the value {ERC20} uses, unless this function is
-     * overridden;
-     *
-     * NOTE: This information is only used for _display_ purposes: it in
-     * no way affects any of the arithmetic of the contract, including
-     * {IERC20-balanceOf} and {IERC20-transfer}.
-     */
-    function decimals() public view virtual override returns (uint8) {
-        return 18;
-    }
-
-    /**
-     * @dev See {IERC20-totalSupply}.
-     */
-    function totalSupply() public view virtual override returns (uint256) {
-        return _totalSupply;
-    }
-
-    /**
-     * @dev See {IERC20-balanceOf}.
-     */
-    function balanceOf(address account) public view virtual override returns (uint256) {
-        return _balances[account];
-    }
-
-    /**
-     * @dev See {IERC20-transfer}.
-     *
-     * Requirements:
-     *
-     * - `to` cannot be the zero address.
-     * - the caller must have a balance of at least `amount`.
-     */
-    function transfer(address to, uint256 amount) public virtual override returns (bool) {
-        address owner = _msgSender();
-        _transfer(owner, to, amount);
-        return true;
-    }
-
-    /**
-     * @dev See {IERC20-allowance}.
-     */
-    function allowance(address owner, address spender) public view virtual override returns (uint256) {
-        return _allowances[owner][spender];
-    }
-
-    /**
-     * @dev See {IERC20-approve}.
-     *
-     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
-     * `transferFrom`. This is semantically equivalent to an infinite approval.
-     *
-     * Requirements:
-     *
-     * - `spender` cannot be the zero address.
-     */
-    function approve(address spender, uint256 amount) public virtual override returns (bool) {
-        address owner = _msgSender();
-        _approve(owner, spender, amount);
-        return true;
-    }
-
-    /**
-     * @dev See {IERC20-transferFrom}.
-     *
-     * Emits an {Approval} event indicating the updated allowance. This is not
-     * required by the EIP. See the note at the beginning of {ERC20}.
-     *
-     * NOTE: Does not update the allowance if the current allowance
-     * is the maximum `uint256`.
-     *
-     * Requirements:
-     *
-     * - `from` and `to` cannot be the zero address.
-     * - `from` must have a balance of at least `amount`.
-     * - the caller must have allowance for ``from``'s tokens of at least
-     * `amount`.
-     */
-    function transferFrom(
-        address from,
-        address to,
-        uint256 amount
-    ) public virtual override returns (bool) {
-        address spender = _msgSender();
-        _spendAllowance(from, spender, amount);
-        _transfer(from, to, amount);
-        return true;
-    }
-
-    /**
-     * @dev Atomically increases the allowance granted to `spender` by the caller.
-     *
-     * This is an alternative to {approve} that can be used as a mitigation for
-     * problems described in {IERC20-approve}.
-     *
-     * Emits an {Approval} event indicating the updated allowance.
-     *
-     * Requirements:
-     *
-     * - `spender` cannot be the zero address.
-     */
-    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
-        address owner = _msgSender();
-        _approve(owner, spender, allowance(owner, spender) + addedValue);
-        return true;
-    }
-
-    /**
-     * @dev Atomically decreases the allowance granted to `spender` by the caller.
-     *
-     * This is an alternative to {approve} that can be used as a mitigation for
-     * problems described in {IERC20-approve}.
-     *
-     * Emits an {Approval} event indicating the updated allowance.
-     *
-     * Requirements:
-     *
-     * - `spender` cannot be the zero address.
-     * - `spender` must have allowance for the caller of at least
-     * `subtractedValue`.
-     */
-    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
-        address owner = _msgSender();
-        uint256 currentAllowance = allowance(owner, spender);
-        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
-        unchecked {
-            _approve(owner, spender, currentAllowance - subtractedValue);
-        }
-
-        return true;
-    }
-
-    /**
-     * @dev Moves `amount` of tokens from `from` to `to`.
-     *
-     * This internal function is equivalent to {transfer}, and can be used to
-     * e.g. implement automatic token fees, slashing mechanisms, etc.
-     *
-     * Emits a {Transfer} event.
-     *
-     * Requirements:
-     *
-     * - `from` cannot be the zero address.
-     * - `to` cannot be the zero address.
-     * - `from` must have a balance of at least `amount`.
-     */
-    function _transfer(
-        address from,
-        address to,
-        uint256 amount
-    ) internal virtual {
-        require(from != address(0), "ERC20: transfer from the zero address");
-        require(to != address(0), "ERC20: transfer to the zero address");
-
-        _beforeTokenTransfer(from, to, amount);
-
-        uint256 fromBalance = _balances[from];
-        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
-        unchecked {
-            _balances[from] = fromBalance - amount;
-            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
-            // decrementing then incrementing.
-            _balances[to] += amount;
-        }
-
-        emit Transfer(from, to, amount);
-
-        _afterTokenTransfer(from, to, amount);
-    }
-
-    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
-     * the total supply.
-     *
-     * Emits a {Transfer} event with `from` set to the zero address.
-     *
-     * Requirements:
-     *
-     * - `account` cannot be the zero address.
-     */
-    function _mint(address account, uint256 amount) internal virtual {
-        require(account != address(0), "ERC20: mint to the zero address");
-
-        _beforeTokenTransfer(address(0), account, amount);
-
-        _totalSupply += amount;
-        unchecked {
-            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
-            _balances[account] += amount;
-        }
-        emit Transfer(address(0), account, amount);
-
-        _afterTokenTransfer(address(0), account, amount);
-    }
-
-    /**
-     * @dev Destroys `amount` tokens from `account`, reducing the
-     * total supply.
-     *
-     * Emits a {Transfer} event with `to` set to the zero address.
-     *
-     * Requirements:
-     *
-     * - `account` cannot be the zero address.
-     * - `account` must have at least `amount` tokens.
-     */
-    function _burn(address account, uint256 amount) internal virtual {
-        require(account != address(0), "ERC20: burn from the zero address");
-
-        _beforeTokenTransfer(account, address(0), amount);
-
-        uint256 accountBalance = _balances[account];
-        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
-        unchecked {
-            _balances[account] = accountBalance - amount;
-            // Overflow not possible: amount <= accountBalance <= totalSupply.
-            _totalSupply -= amount;
-        }
-
-        emit Transfer(account, address(0), amount);
-
-        _afterTokenTransfer(account, address(0), amount);
-    }
-
-    /**
-     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
-     *
-     * This internal function is equivalent to `approve`, and can be used to
-     * e.g. set automatic allowances for certain subsystems, etc.
-     *
-     * Emits an {Approval} event.
-     *
-     * Requirements:
-     *
-     * - `owner` cannot be the zero address.
-     * - `spender` cannot be the zero address.
-     */
-    function _approve(
-        address owner,
-        address spender,
-        uint256 amount
-    ) internal virtual {
-        require(owner != address(0), "ERC20: approve from the zero address");
-        require(spender != address(0), "ERC20: approve to the zero address");
-
-        _allowances[owner][spender] = amount;
-        emit Approval(owner, spender, amount);
-    }
-
-    /**
-     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
-     *
-     * Does not update the allowance amount in case of infinite allowance.
-     * Revert if not enough allowance is available.
-     *
-     * Might emit an {Approval} event.
-     */
-    function _spendAllowance(
-        address owner,
-        address spender,
-        uint256 amount
-    ) internal virtual {
-        uint256 currentAllowance = allowance(owner, spender);
-        if (currentAllowance != type(uint256).max) {
-            require(currentAllowance >= amount, "ERC20: insufficient allowance");
-            unchecked {
-                _approve(owner, spender, currentAllowance - amount);
-            }
-        }
-    }
-
-    /**
-     * @dev Hook that is called before any transfer of tokens. This includes
-     * minting and burning.
-     *
-     * Calling conditions:
-     *
-     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
-     * will be transferred to `to`.
-     * - when `from` is zero, `amount` tokens will be minted for `to`.
-     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
-     * - `from` and `to` are never both zero.
-     *
-     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
-     */
-    function _beforeTokenTransfer(
-        address from,
-        address to,
-        uint256 amount
-    ) internal virtual {}
-
-    /**
-     * @dev Hook that is called after any transfer of tokens. This includes
-     * minting and burning.
-     *
-     * Calling conditions:
-     *
-     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
-     * has been transferred to `to`.
-     * - when `from` is zero, `amount` tokens have been minted for `to`.
-     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
-     * - `from` and `to` are never both zero.
-     *
-     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
-     */
-    function _afterTokenTransfer(
-        address from,
-        address to,
-        uint256 amount
-    ) internal virtual {}
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/IERC20.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/IERC20.sol
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Interface of the ERC20 standard as defined in the EIP.
- */
-interface IERC20 {
-    /**
-     * @dev Emitted when `value` tokens are moved from one account (`from`) to
-     * another (`to`).
-     *
-     * Note that `value` may be zero.
-     */
-    event Transfer(address indexed from, address indexed to, uint256 value);
-
-    /**
-     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
-     * a call to {approve}. `value` is the new allowance.
-     */
-    event Approval(address indexed owner, address indexed spender, uint256 value);
-
-    /**
-     * @dev Returns the amount of tokens in existence.
-     */
-    function totalSupply() external view returns (uint256);
-
-    /**
-     * @dev Returns the amount of tokens owned by `account`.
-     */
-    function balanceOf(address account) external view returns (uint256);
-
-    /**
-     * @dev Moves `amount` tokens from the caller's account to `to`.
-     *
-     * Returns a boolean value indicating whether the operation succeeded.
-     *
-     * Emits a {Transfer} event.
-     */
-    function transfer(address to, uint256 amount) external returns (bool);
-
-    /**
-     * @dev Returns the remaining number of tokens that `spender` will be
-     * allowed to spend on behalf of `owner` through {transferFrom}. This is
-     * zero by default.
-     *
-     * This value changes when {approve} or {transferFrom} are called.
-     */
-    function allowance(address owner, address spender) external view returns (uint256);
-
-    /**
-     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
-     *
-     * Returns a boolean value indicating whether the operation succeeded.
-     *
-     * IMPORTANT: Beware that changing an allowance with this method brings the risk
-     * that someone may use both the old and the new allowance by unfortunate
-     * transaction ordering. One possible solution to mitigate this race
-     * condition is to first reduce the spender's allowance to 0 and set the
-     * desired value afterwards:
-     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
-     *
-     * Emits an {Approval} event.
-     */
-    function approve(address spender, uint256 amount) external returns (bool);
-
-    /**
-     * @dev Moves `amount` tokens from `from` to `to` using the
-     * allowance mechanism. `amount` is then deducted from the caller's
-     * allowance.
-     *
-     * Returns a boolean value indicating whether the operation succeeded.
-     *
-     * Emits a {Transfer} event.
-     */
-    function transferFrom(
-        address from,
-        address to,
-        uint256 amount
-    ) external returns (bool);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
-
-pragma solidity ^0.8.0;
-
-import "../ERC20.sol";
-import "../../../utils/Context.sol";
-
-/**
- * @dev Extension of {ERC20} that allows token holders to destroy both their own
- * tokens and those that they have an allowance for, in a way that can be
- * recognized off-chain (via event analysis).
- */
-abstract contract ERC20Burnable is Context, ERC20 {
-    /**
-     * @dev Destroys `amount` tokens from the caller.
-     *
-     * See {ERC20-_burn}.
-     */
-    function burn(uint256 amount) public virtual {
-        _burn(_msgSender(), amount);
-    }
-
-    /**
-     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
-     * allowance.
-     *
-     * See {ERC20-_burn} and {ERC20-allowance}.
-     *
-     * Requirements:
-     *
-     * - the caller must have allowance for ``accounts``'s tokens of at least
-     * `amount`.
-     */
-    function burnFrom(address account, uint256 amount) public virtual {
-        _spendAllowance(account, _msgSender(), amount);
-        _burn(account, amount);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol
+++ /dev/null
@@ -1,109 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/ERC20FlashMint.sol)
-
-pragma solidity ^0.8.0;
-
-import "../../../interfaces/IERC3156FlashBorrower.sol";
-import "../../../interfaces/IERC3156FlashLender.sol";
-import "../ERC20.sol";
-
-/**
- * @dev Implementation of the ERC3156 Flash loans extension, as defined in
- * https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
- *
- * Adds the {flashLoan} method, which provides flash loan support at the token
- * level. By default there is no fee, but this can be changed by overriding {flashFee}.
- *
- * _Available since v4.1._
- */
-abstract contract ERC20FlashMint is ERC20, IERC3156FlashLender {
-    bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
-
-    /**
-     * @dev Returns the maximum amount of tokens available for loan.
-     * @param token The address of the token that is requested.
-     * @return The amount of token that can be loaned.
-     */
-    function maxFlashLoan(address token) public view virtual override returns (uint256) {
-        return token == address(this) ? type(uint256).max - ERC20.totalSupply() : 0;
-    }
-
-    /**
-     * @dev Returns the fee applied when doing flash loans. This function calls
-     * the {_flashFee} function which returns the fee applied when doing flash
-     * loans.
-     * @param token The token to be flash loaned.
-     * @param amount The amount of tokens to be loaned.
-     * @return The fees applied to the corresponding flash loan.
-     */
-    function flashFee(address token, uint256 amount) public view virtual override returns (uint256) {
-        require(token == address(this), "ERC20FlashMint: wrong token");
-        return _flashFee(token, amount);
-    }
-
-    /**
-     * @dev Returns the fee applied when doing flash loans. By default this
-     * implementation has 0 fees. This function can be overloaded to make
-     * the flash loan mechanism deflationary.
-     * @param token The token to be flash loaned.
-     * @param amount The amount of tokens to be loaned.
-     * @return The fees applied to the corresponding flash loan.
-     */
-    function _flashFee(address token, uint256 amount) internal view virtual returns (uint256) {
-        // silence warning about unused variable without the addition of bytecode.
-        token;
-        amount;
-        return 0;
-    }
-
-    /**
-     * @dev Returns the receiver address of the flash fee. By default this
-     * implementation returns the address(0) which means the fee amount will be burnt.
-     * This function can be overloaded to change the fee receiver.
-     * @return The address for which the flash fee will be sent to.
-     */
-    function _flashFeeReceiver() internal view virtual returns (address) {
-        return address(0);
-    }
-
-    /**
-     * @dev Performs a flash loan. New tokens are minted and sent to the
-     * `receiver`, who is required to implement the {IERC3156FlashBorrower}
-     * interface. By the end of the flash loan, the receiver is expected to own
-     * amount + fee tokens and have them approved back to the token contract itself so
-     * they can be burned.
-     * @param receiver The receiver of the flash loan. Should implement the
-     * {IERC3156FlashBorrower-onFlashLoan} interface.
-     * @param token The token to be flash loaned. Only `address(this)` is
-     * supported.
-     * @param amount The amount of tokens to be loaned.
-     * @param data An arbitrary datafield that is passed to the receiver.
-     * @return `true` if the flash loan was successful.
-     */
-    // This function can reenter, but it doesn't pose a risk because it always preserves the property that the amount
-    // minted at the beginning is always recovered and burned at the end, or else the entire function will revert.
-    // slither-disable-next-line reentrancy-no-eth
-    function flashLoan(
-        IERC3156FlashBorrower receiver,
-        address token,
-        uint256 amount,
-        bytes calldata data
-    ) public virtual override returns (bool) {
-        require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan");
-        uint256 fee = flashFee(token, amount);
-        _mint(address(receiver), amount);
-        require(
-            receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,
-            "ERC20FlashMint: invalid return value"
-        );
-        address flashFeeReceiver = _flashFeeReceiver();
-        _spendAllowance(address(receiver), address(this), amount + fee);
-        if (fee == 0 || flashFeeReceiver == address(0)) {
-            _burn(address(receiver), amount + fee);
-        } else {
-            _burn(address(receiver), amount);
-            _transfer(address(receiver), flashFeeReceiver, fee);
-        }
-        return true;
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol
+++ /dev/null
@@ -1,275 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC20Votes.sol)
-
-pragma solidity ^0.8.0;
-
-import "./draft-ERC20Permit.sol";
-import "../../../utils/math/Math.sol";
-import "../../../governance/utils/IVotes.sol";
-import "../../../utils/math/SafeCast.sol";
-import "../../../utils/cryptography/ECDSA.sol";
-
-/**
- * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's,
- * and supports token supply up to 2^224^ - 1, while COMP is limited to 2^96^ - 1.
- *
- * NOTE: If exact COMP compatibility is required, use the {ERC20VotesComp} variant of this module.
- *
- * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either
- * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting
- * power can be queried through the public accessors {getVotes} and {getPastVotes}.
- *
- * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it
- * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked.
- *
- * _Available since v4.2._
- */
-abstract contract ERC20Votes is IVotes, ERC20Permit {
-    struct Checkpoint {
-        uint32 fromBlock;
-        uint224 votes;
-    }
-
-    bytes32 private constant _DELEGATION_TYPEHASH =
-        keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
-
-    mapping(address => address) private _delegates;
-    mapping(address => Checkpoint[]) private _checkpoints;
-    Checkpoint[] private _totalSupplyCheckpoints;
-
-    /**
-     * @dev Get the `pos`-th checkpoint for `account`.
-     */
-    function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoint memory) {
-        return _checkpoints[account][pos];
-    }
-
-    /**
-     * @dev Get number of checkpoints for `account`.
-     */
-    function numCheckpoints(address account) public view virtual returns (uint32) {
-        return SafeCast.toUint32(_checkpoints[account].length);
-    }
-
-    /**
-     * @dev Get the address `account` is currently delegating to.
-     */
-    function delegates(address account) public view virtual override returns (address) {
-        return _delegates[account];
-    }
-
-    /**
-     * @dev Gets the current votes balance for `account`
-     */
-    function getVotes(address account) public view virtual override returns (uint256) {
-        uint256 pos = _checkpoints[account].length;
-        return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
-    }
-
-    /**
-     * @dev Retrieve the number of votes for `account` at the end of `blockNumber`.
-     *
-     * Requirements:
-     *
-     * - `blockNumber` must have been already mined
-     */
-    function getPastVotes(address account, uint256 blockNumber) public view virtual override returns (uint256) {
-        require(blockNumber < block.number, "ERC20Votes: block not yet mined");
-        return _checkpointsLookup(_checkpoints[account], blockNumber);
-    }
-
-    /**
-     * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
-     * It is but NOT the sum of all the delegated votes!
-     *
-     * Requirements:
-     *
-     * - `blockNumber` must have been already mined
-     */
-    function getPastTotalSupply(uint256 blockNumber) public view virtual override returns (uint256) {
-        require(blockNumber < block.number, "ERC20Votes: block not yet mined");
-        return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
-    }
-
-    /**
-     * @dev Lookup a value in a list of (sorted) checkpoints.
-     */
-    function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) {
-        // We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
-        //
-        // Initially we check if the block is recent to narrow the search range.
-        // During the loop, the index of the wanted checkpoint remains in the range [low-1, high).
-        // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
-        // - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
-        // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
-        // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
-        // out of bounds (in which case we're looking too far in the past and the result is 0).
-        // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
-        // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
-        // the same.
-        uint256 length = ckpts.length;
-
-        uint256 low = 0;
-        uint256 high = length;
-
-        if (length > 5) {
-            uint256 mid = length - Math.sqrt(length);
-            if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
-                high = mid;
-            } else {
-                low = mid + 1;
-            }
-        }
-
-        while (low < high) {
-            uint256 mid = Math.average(low, high);
-            if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) {
-                high = mid;
-            } else {
-                low = mid + 1;
-            }
-        }
-
-        return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes;
-    }
-
-    /**
-     * @dev Delegate votes from the sender to `delegatee`.
-     */
-    function delegate(address delegatee) public virtual override {
-        _delegate(_msgSender(), delegatee);
-    }
-
-    /**
-     * @dev Delegates votes from signer to `delegatee`
-     */
-    function delegateBySig(
-        address delegatee,
-        uint256 nonce,
-        uint256 expiry,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) public virtual override {
-        require(block.timestamp <= expiry, "ERC20Votes: signature expired");
-        address signer = ECDSA.recover(
-            _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))),
-            v,
-            r,
-            s
-        );
-        require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce");
-        _delegate(signer, delegatee);
-    }
-
-    /**
-     * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1).
-     */
-    function _maxSupply() internal view virtual returns (uint224) {
-        return type(uint224).max;
-    }
-
-    /**
-     * @dev Snapshots the totalSupply after it has been increased.
-     */
-    function _mint(address account, uint256 amount) internal virtual override {
-        super._mint(account, amount);
-        require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes");
-
-        _writeCheckpoint(_totalSupplyCheckpoints, _add, amount);
-    }
-
-    /**
-     * @dev Snapshots the totalSupply after it has been decreased.
-     */
-    function _burn(address account, uint256 amount) internal virtual override {
-        super._burn(account, amount);
-
-        _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount);
-    }
-
-    /**
-     * @dev Move voting power when tokens are transferred.
-     *
-     * Emits a {IVotes-DelegateVotesChanged} event.
-     */
-    function _afterTokenTransfer(
-        address from,
-        address to,
-        uint256 amount
-    ) internal virtual override {
-        super._afterTokenTransfer(from, to, amount);
-
-        _moveVotingPower(delegates(from), delegates(to), amount);
-    }
-
-    /**
-     * @dev Change delegation for `delegator` to `delegatee`.
-     *
-     * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}.
-     */
-    function _delegate(address delegator, address delegatee) internal virtual {
-        address currentDelegate = delegates(delegator);
-        uint256 delegatorBalance = balanceOf(delegator);
-        _delegates[delegator] = delegatee;
-
-        emit DelegateChanged(delegator, currentDelegate, delegatee);
-
-        _moveVotingPower(currentDelegate, delegatee, delegatorBalance);
-    }
-
-    function _moveVotingPower(
-        address src,
-        address dst,
-        uint256 amount
-    ) private {
-        if (src != dst && amount > 0) {
-            if (src != address(0)) {
-                (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount);
-                emit DelegateVotesChanged(src, oldWeight, newWeight);
-            }
-
-            if (dst != address(0)) {
-                (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount);
-                emit DelegateVotesChanged(dst, oldWeight, newWeight);
-            }
-        }
-    }
-
-    function _writeCheckpoint(
-        Checkpoint[] storage ckpts,
-        function(uint256, uint256) view returns (uint256) op,
-        uint256 delta
-    ) private returns (uint256 oldWeight, uint256 newWeight) {
-        uint256 pos = ckpts.length;
-
-        Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1);
-
-        oldWeight = oldCkpt.votes;
-        newWeight = op(oldWeight, delta);
-
-        if (pos > 0 && oldCkpt.fromBlock == block.number) {
-            _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight);
-        } else {
-            ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)}));
-        }
-    }
-
-    function _add(uint256 a, uint256 b) private pure returns (uint256) {
-        return a + b;
-    }
-
-    function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
-        return a - b;
-    }
-
-    /**
-     * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds.
-     */
-    function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) {
-        assembly {
-            mstore(0, ckpts.slot)
-            result.slot := add(keccak256(0, 0x20), pos)
-        }
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
-
-pragma solidity ^0.8.0;
-
-import "../IERC20.sol";
-
-/**
- * @dev Interface for the optional metadata functions from the ERC20 standard.
- *
- * _Available since v4.1._
- */
-interface IERC20Metadata is IERC20 {
-    /**
-     * @dev Returns the name of the token.
-     */
-    function name() external view returns (string memory);
-
-    /**
-     * @dev Returns the symbol of the token.
-     */
-    function symbol() external view returns (string memory);
-
-    /**
-     * @dev Returns the decimals places of the token.
-     */
-    function decimals() external view returns (uint8);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol
+++ /dev/null
@@ -1,95 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/extensions/draft-ERC20Permit.sol)
-
-pragma solidity ^0.8.0;
-
-import "./draft-IERC20Permit.sol";
-import "../ERC20.sol";
-import "../../../utils/cryptography/ECDSA.sol";
-import "../../../utils/cryptography/EIP712.sol";
-import "../../../utils/Counters.sol";
-
-/**
- * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
- * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
- *
- * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
- * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
- * need to send a transaction, and thus is not required to hold Ether at all.
- *
- * _Available since v3.4._
- */
-abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
-    using Counters for Counters.Counter;
-
-    mapping(address => Counters.Counter) private _nonces;
-
-    // solhint-disable-next-line var-name-mixedcase
-    bytes32 private constant _PERMIT_TYPEHASH =
-        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
-    /**
-     * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
-     * However, to ensure consistency with the upgradeable transpiler, we will continue
-     * to reserve a slot.
-     * @custom:oz-renamed-from _PERMIT_TYPEHASH
-     */
-    // solhint-disable-next-line var-name-mixedcase
-    bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
-
-    /**
-     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
-     *
-     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
-     */
-    constructor(string memory name) EIP712(name, "1") {}
-
-    /**
-     * @dev See {IERC20Permit-permit}.
-     */
-    function permit(
-        address owner,
-        address spender,
-        uint256 value,
-        uint256 deadline,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) public virtual override {
-        require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
-
-        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
-
-        bytes32 hash = _hashTypedDataV4(structHash);
-
-        address signer = ECDSA.recover(hash, v, r, s);
-        require(signer == owner, "ERC20Permit: invalid signature");
-
-        _approve(owner, spender, value);
-    }
-
-    /**
-     * @dev See {IERC20Permit-nonces}.
-     */
-    function nonces(address owner) public view virtual override returns (uint256) {
-        return _nonces[owner].current();
-    }
-
-    /**
-     * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
-     */
-    // solhint-disable-next-line func-name-mixedcase
-    function DOMAIN_SEPARATOR() external view override returns (bytes32) {
-        return _domainSeparatorV4();
-    }
-
-    /**
-     * @dev "Consume a nonce": return the current value and increment.
-     *
-     * _Available since v4.1._
-     */
-    function _useNonce(address owner) internal virtual returns (uint256 current) {
-        Counters.Counter storage nonce = _nonces[owner];
-        current = nonce.current();
-        nonce.increment();
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
- * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
- *
- * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
- * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
- * need to send a transaction, and thus is not required to hold Ether at all.
- */
-interface IERC20Permit {
-    /**
-     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
-     * given ``owner``'s signed approval.
-     *
-     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
-     * ordering also apply here.
-     *
-     * Emits an {Approval} event.
-     *
-     * Requirements:
-     *
-     * - `spender` cannot be the zero address.
-     * - `deadline` must be a timestamp in the future.
-     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
-     * over the EIP712-formatted function arguments.
-     * - the signature must use ``owner``'s current nonce (see {nonces}).
-     *
-     * For more information on the signature format, see the
-     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
-     * section].
-     */
-    function permit(
-        address owner,
-        address spender,
-        uint256 value,
-        uint256 deadline,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) external;
-
-    /**
-     * @dev Returns the current nonce for `owner`. This value must be
-     * included whenever a signature is generated for {permit}.
-     *
-     * Every successful call to {permit} increases ``owner``'s nonce by one. This
-     * prevents a signature from being used multiple times.
-     */
-    function nonces(address owner) external view returns (uint256);
-
-    /**
-     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
-     */
-    // solhint-disable-next-line func-name-mixedcase
-    function DOMAIN_SEPARATOR() external view returns (bytes32);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Context.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Context.sol
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Provides information about the current execution context, including the
- * sender of the transaction and its data. While these are generally available
- * via msg.sender and msg.data, they should not be accessed in such a direct
- * manner, since when dealing with meta-transactions the account sending and
- * paying for execution may not be the actual sender (as far as an application
- * is concerned).
- *
- * This contract is only required for intermediate, library-like contracts.
- */
-abstract contract Context {
-    function _msgSender() internal view virtual returns (address) {
-        return msg.sender;
-    }
-
-    function _msgData() internal view virtual returns (bytes calldata) {
-        return msg.data;
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Counters.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Counters.sol
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @title Counters
- * @author Matt Condon (@shrugs)
- * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
- * of elements in a mapping, issuing ERC721 ids, or counting request ids.
- *
- * Include with `using Counters for Counters.Counter;`
- */
-library Counters {
-    struct Counter {
-        // This variable should never be directly accessed by users of the library: interactions must be restricted to
-        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
-        // this feature: see https://github.com/ethereum/solidity/issues/4637
-        uint256 _value; // default: 0
-    }
-
-    function current(Counter storage counter) internal view returns (uint256) {
-        return counter._value;
-    }
-
-    function increment(Counter storage counter) internal {
-        unchecked {
-            counter._value += 1;
-        }
-    }
-
-    function decrement(Counter storage counter) internal {
-        uint256 value = counter._value;
-        require(value > 0, "Counter: decrement overflow");
-        unchecked {
-            counter._value = value - 1;
-        }
-    }
-
-    function reset(Counter storage counter) internal {
-        counter._value = 0;
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Strings.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/Strings.sol
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
-
-pragma solidity ^0.8.0;
-
-import "./math/Math.sol";
-
-/**
- * @dev String operations.
- */
-library Strings {
-    bytes16 private constant _SYMBOLS = "0123456789abcdef";
-    uint8 private constant _ADDRESS_LENGTH = 20;
-
-    /**
-     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
-     */
-    function toString(uint256 value) internal pure returns (string memory) {
-        unchecked {
-            uint256 length = Math.log10(value) + 1;
-            string memory buffer = new string(length);
-            uint256 ptr;
-            /// @solidity memory-safe-assembly
-            assembly {
-                ptr := add(buffer, add(32, length))
-            }
-            while (true) {
-                ptr--;
-                /// @solidity memory-safe-assembly
-                assembly {
-                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
-                }
-                value /= 10;
-                if (value == 0) break;
-            }
-            return buffer;
-        }
-    }
-
-    /**
-     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
-     */
-    function toHexString(uint256 value) internal pure returns (string memory) {
-        unchecked {
-            return toHexString(value, Math.log256(value) + 1);
-        }
-    }
-
-    /**
-     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
-     */
-    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
-        bytes memory buffer = new bytes(2 * length + 2);
-        buffer[0] = "0";
-        buffer[1] = "x";
-        for (uint256 i = 2 * length + 1; i > 1; --i) {
-            buffer[i] = _SYMBOLS[value & 0xf];
-            value >>= 4;
-        }
-        require(value == 0, "Strings: hex length insufficient");
-        return string(buffer);
-    }
-
-    /**
-     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
-     */
-    function toHexString(address addr) internal pure returns (string memory) {
-        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/ECDSA.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/ECDSA.sol
+++ /dev/null
@@ -1,213 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/ECDSA.sol)
-
-pragma solidity ^0.8.0;
-
-import "../Strings.sol";
-
-/**
- * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
- *
- * These functions can be used to verify that a message was signed by the holder
- * of the private keys of a given address.
- */
-library ECDSA {
-    enum RecoverError {
-        NoError,
-        InvalidSignature,
-        InvalidSignatureLength,
-        InvalidSignatureS,
-        InvalidSignatureV // Deprecated in v4.8
-    }
-
-    function _throwError(RecoverError error) private pure {
-        if (error == RecoverError.NoError) {
-            return; // no error: do nothing
-        } else if (error == RecoverError.InvalidSignature) {
-            revert("ECDSA: invalid signature");
-        } else if (error == RecoverError.InvalidSignatureLength) {
-            revert("ECDSA: invalid signature length");
-        } else if (error == RecoverError.InvalidSignatureS) {
-            revert("ECDSA: invalid signature 's' value");
-        }
-    }
-
-    /**
-     * @dev Returns the address that signed a hashed message (`hash`) with
-     * `signature` or error string. This address can then be used for verification purposes.
-     *
-     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
-     * this function rejects them by requiring the `s` value to be in the lower
-     * half order, and the `v` value to be either 27 or 28.
-     *
-     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
-     * verification to be secure: it is possible to craft signatures that
-     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
-     * this is by receiving a hash of the original message (which may otherwise
-     * be too long), and then calling {toEthSignedMessageHash} on it.
-     *
-     * Documentation for signature generation:
-     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
-     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
-     *
-     * _Available since v4.3._
-     */
-    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
-        if (signature.length == 65) {
-            bytes32 r;
-            bytes32 s;
-            uint8 v;
-            // ecrecover takes the signature parameters, and the only way to get them
-            // currently is to use assembly.
-            /// @solidity memory-safe-assembly
-            assembly {
-                r := mload(add(signature, 0x20))
-                s := mload(add(signature, 0x40))
-                v := byte(0, mload(add(signature, 0x60)))
-            }
-            return tryRecover(hash, v, r, s);
-        } else {
-            return (address(0), RecoverError.InvalidSignatureLength);
-        }
-    }
-
-    /**
-     * @dev Returns the address that signed a hashed message (`hash`) with
-     * `signature`. This address can then be used for verification purposes.
-     *
-     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
-     * this function rejects them by requiring the `s` value to be in the lower
-     * half order, and the `v` value to be either 27 or 28.
-     *
-     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
-     * verification to be secure: it is possible to craft signatures that
-     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
-     * this is by receiving a hash of the original message (which may otherwise
-     * be too long), and then calling {toEthSignedMessageHash} on it.
-     */
-    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
-        (address recovered, RecoverError error) = tryRecover(hash, signature);
-        _throwError(error);
-        return recovered;
-    }
-
-    /**
-     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
-     *
-     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
-     *
-     * _Available since v4.3._
-     */
-    function tryRecover(
-        bytes32 hash,
-        bytes32 r,
-        bytes32 vs
-    ) internal pure returns (address, RecoverError) {
-        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
-        uint8 v = uint8((uint256(vs) >> 255) + 27);
-        return tryRecover(hash, v, r, s);
-    }
-
-    /**
-     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
-     *
-     * _Available since v4.2._
-     */
-    function recover(
-        bytes32 hash,
-        bytes32 r,
-        bytes32 vs
-    ) internal pure returns (address) {
-        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
-        _throwError(error);
-        return recovered;
-    }
-
-    /**
-     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
-     * `r` and `s` signature fields separately.
-     *
-     * _Available since v4.3._
-     */
-    function tryRecover(
-        bytes32 hash,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) internal pure returns (address, RecoverError) {
-        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
-        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
-        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
-        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
-        //
-        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
-        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
-        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
-        // these malleable signatures as well.
-        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
-            return (address(0), RecoverError.InvalidSignatureS);
-        }
-
-        // If the signature is valid (and not malleable), return the signer address
-        address signer = ecrecover(hash, v, r, s);
-        if (signer == address(0)) {
-            return (address(0), RecoverError.InvalidSignature);
-        }
-
-        return (signer, RecoverError.NoError);
-    }
-
-    /**
-     * @dev Overload of {ECDSA-recover} that receives the `v`,
-     * `r` and `s` signature fields separately.
-     */
-    function recover(
-        bytes32 hash,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) internal pure returns (address) {
-        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
-        _throwError(error);
-        return recovered;
-    }
-
-    /**
-     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
-     * produces hash corresponding to the one signed with the
-     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
-     * JSON-RPC method as part of EIP-191.
-     *
-     * See {recover}.
-     */
-    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
-        // 32 is the length in bytes of hash,
-        // enforced by the type signature above
-        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
-    }
-
-    /**
-     * @dev Returns an Ethereum Signed Message, created from `s`. This
-     * produces hash corresponding to the one signed with the
-     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
-     * JSON-RPC method as part of EIP-191.
-     *
-     * See {recover}.
-     */
-    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
-        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
-    }
-
-    /**
-     * @dev Returns an Ethereum Signed Typed Data, created from a
-     * `domainSeparator` and a `structHash`. This produces hash corresponding
-     * to the one signed with the
-     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
-     * JSON-RPC method as part of EIP-712.
-     *
-     * See {recover}.
-     */
-    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
-        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/EIP712.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/cryptography/EIP712.sol
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
-
-pragma solidity ^0.8.0;
-
-import "./ECDSA.sol";
-
-/**
- * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
- *
- * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
- * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
- * they need in their contracts using a combination of `abi.encode` and `keccak256`.
- *
- * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
- * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
- * ({_hashTypedDataV4}).
- *
- * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
- * the chain id to protect against replay attacks on an eventual fork of the chain.
- *
- * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
- * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
- *
- * _Available since v3.4._
- */
-abstract contract EIP712 {
-    /* solhint-disable var-name-mixedcase */
-    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
-    // invalidate the cached domain separator if the chain id changes.
-    bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
-    uint256 private immutable _CACHED_CHAIN_ID;
-    address private immutable _CACHED_THIS;
-
-    bytes32 private immutable _HASHED_NAME;
-    bytes32 private immutable _HASHED_VERSION;
-    bytes32 private immutable _TYPE_HASH;
-
-    /* solhint-enable var-name-mixedcase */
-
-    /**
-     * @dev Initializes the domain separator and parameter caches.
-     *
-     * The meaning of `name` and `version` is specified in
-     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
-     *
-     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
-     * - `version`: the current major version of the signing domain.
-     *
-     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
-     * contract upgrade].
-     */
-    constructor(string memory name, string memory version) {
-        bytes32 hashedName = keccak256(bytes(name));
-        bytes32 hashedVersion = keccak256(bytes(version));
-        bytes32 typeHash = keccak256(
-            "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
-        );
-        _HASHED_NAME = hashedName;
-        _HASHED_VERSION = hashedVersion;
-        _CACHED_CHAIN_ID = block.chainid;
-        _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion);
-        _CACHED_THIS = address(this);
-        _TYPE_HASH = typeHash;
-    }
-
-    /**
-     * @dev Returns the domain separator for the current chain.
-     */
-    function _domainSeparatorV4() internal view returns (bytes32) {
-        if (address(this) == _CACHED_THIS && block.chainid == _CACHED_CHAIN_ID) {
-            return _CACHED_DOMAIN_SEPARATOR;
-        } else {
-            return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION);
-        }
-    }
-
-    function _buildDomainSeparator(
-        bytes32 typeHash,
-        bytes32 nameHash,
-        bytes32 versionHash
-    ) private view returns (bytes32) {
-        return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
-    }
-
-    /**
-     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
-     * function returns the hash of the fully encoded EIP712 message for this domain.
-     *
-     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
-     *
-     * ```solidity
-     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
-     *     keccak256("Mail(address to,string contents)"),
-     *     mailTo,
-     *     keccak256(bytes(mailContents))
-     * )));
-     * address signer = ECDSA.recover(digest, signature);
-     * ```
-     */
-    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
-        return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/Math.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/Math.sol
+++ /dev/null
@@ -1,345 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Standard math utilities missing in the Solidity language.
- */
-library Math {
-    enum Rounding {
-        Down, // Toward negative infinity
-        Up, // Toward infinity
-        Zero // Toward zero
-    }
-
-    /**
-     * @dev Returns the largest of two numbers.
-     */
-    function max(uint256 a, uint256 b) internal pure returns (uint256) {
-        return a > b ? a : b;
-    }
-
-    /**
-     * @dev Returns the smallest of two numbers.
-     */
-    function min(uint256 a, uint256 b) internal pure returns (uint256) {
-        return a < b ? a : b;
-    }
-
-    /**
-     * @dev Returns the average of two numbers. The result is rounded towards
-     * zero.
-     */
-    function average(uint256 a, uint256 b) internal pure returns (uint256) {
-        // (a + b) / 2 can overflow.
-        return (a & b) + (a ^ b) / 2;
-    }
-
-    /**
-     * @dev Returns the ceiling of the division of two numbers.
-     *
-     * This differs from standard division with `/` in that it rounds up instead
-     * of rounding down.
-     */
-    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
-        // (a + b - 1) / b can overflow on addition, so we distribute.
-        return a == 0 ? 0 : (a - 1) / b + 1;
-    }
-
-    /**
-     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
-     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
-     * with further edits by Uniswap Labs also under MIT license.
-     */
-    function mulDiv(
-        uint256 x,
-        uint256 y,
-        uint256 denominator
-    ) internal pure returns (uint256 result) {
-        unchecked {
-            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
-            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
-            // variables such that product = prod1 * 2^256 + prod0.
-            uint256 prod0; // Least significant 256 bits of the product
-            uint256 prod1; // Most significant 256 bits of the product
-            assembly {
-                let mm := mulmod(x, y, not(0))
-                prod0 := mul(x, y)
-                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
-            }
-
-            // Handle non-overflow cases, 256 by 256 division.
-            if (prod1 == 0) {
-                return prod0 / denominator;
-            }
-
-            // Make sure the result is less than 2^256. Also prevents denominator == 0.
-            require(denominator > prod1);
-
-            ///////////////////////////////////////////////
-            // 512 by 256 division.
-            ///////////////////////////////////////////////
-
-            // Make division exact by subtracting the remainder from [prod1 prod0].
-            uint256 remainder;
-            assembly {
-                // Compute remainder using mulmod.
-                remainder := mulmod(x, y, denominator)
-
-                // Subtract 256 bit number from 512 bit number.
-                prod1 := sub(prod1, gt(remainder, prod0))
-                prod0 := sub(prod0, remainder)
-            }
-
-            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
-            // See https://cs.stackexchange.com/q/138556/92363.
-
-            // Does not overflow because the denominator cannot be zero at this stage in the function.
-            uint256 twos = denominator & (~denominator + 1);
-            assembly {
-                // Divide denominator by twos.
-                denominator := div(denominator, twos)
-
-                // Divide [prod1 prod0] by twos.
-                prod0 := div(prod0, twos)
-
-                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
-                twos := add(div(sub(0, twos), twos), 1)
-            }
-
-            // Shift in bits from prod1 into prod0.
-            prod0 |= prod1 * twos;
-
-            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
-            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
-            // four bits. That is, denominator * inv = 1 mod 2^4.
-            uint256 inverse = (3 * denominator) ^ 2;
-
-            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
-            // in modular arithmetic, doubling the correct bits in each step.
-            inverse *= 2 - denominator * inverse; // inverse mod 2^8
-            inverse *= 2 - denominator * inverse; // inverse mod 2^16
-            inverse *= 2 - denominator * inverse; // inverse mod 2^32
-            inverse *= 2 - denominator * inverse; // inverse mod 2^64
-            inverse *= 2 - denominator * inverse; // inverse mod 2^128
-            inverse *= 2 - denominator * inverse; // inverse mod 2^256
-
-            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
-            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
-            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
-            // is no longer required.
-            result = prod0 * inverse;
-            return result;
-        }
-    }
-
-    /**
-     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
-     */
-    function mulDiv(
-        uint256 x,
-        uint256 y,
-        uint256 denominator,
-        Rounding rounding
-    ) internal pure returns (uint256) {
-        uint256 result = mulDiv(x, y, denominator);
-        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
-            result += 1;
-        }
-        return result;
-    }
-
-    /**
-     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
-     *
-     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
-     */
-    function sqrt(uint256 a) internal pure returns (uint256) {
-        if (a == 0) {
-            return 0;
-        }
-
-        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
-        //
-        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
-        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
-        //
-        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
-        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
-        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
-        //
-        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
-        uint256 result = 1 << (log2(a) >> 1);
-
-        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
-        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
-        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
-        // into the expected uint128 result.
-        unchecked {
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            return min(result, a / result);
-        }
-    }
-
-    /**
-     * @notice Calculates sqrt(a), following the selected rounding direction.
-     */
-    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = sqrt(a);
-            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
-        }
-    }
-
-    /**
-     * @dev Return the log in base 2, rounded down, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log2(uint256 value) internal pure returns (uint256) {
-        uint256 result = 0;
-        unchecked {
-            if (value >> 128 > 0) {
-                value >>= 128;
-                result += 128;
-            }
-            if (value >> 64 > 0) {
-                value >>= 64;
-                result += 64;
-            }
-            if (value >> 32 > 0) {
-                value >>= 32;
-                result += 32;
-            }
-            if (value >> 16 > 0) {
-                value >>= 16;
-                result += 16;
-            }
-            if (value >> 8 > 0) {
-                value >>= 8;
-                result += 8;
-            }
-            if (value >> 4 > 0) {
-                value >>= 4;
-                result += 4;
-            }
-            if (value >> 2 > 0) {
-                value >>= 2;
-                result += 2;
-            }
-            if (value >> 1 > 0) {
-                result += 1;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = log2(value);
-            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
-        }
-    }
-
-    /**
-     * @dev Return the log in base 10, rounded down, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log10(uint256 value) internal pure returns (uint256) {
-        uint256 result = 0;
-        unchecked {
-            if (value >= 10**64) {
-                value /= 10**64;
-                result += 64;
-            }
-            if (value >= 10**32) {
-                value /= 10**32;
-                result += 32;
-            }
-            if (value >= 10**16) {
-                value /= 10**16;
-                result += 16;
-            }
-            if (value >= 10**8) {
-                value /= 10**8;
-                result += 8;
-            }
-            if (value >= 10**4) {
-                value /= 10**4;
-                result += 4;
-            }
-            if (value >= 10**2) {
-                value /= 10**2;
-                result += 2;
-            }
-            if (value >= 10**1) {
-                result += 1;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = log10(value);
-            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
-        }
-    }
-
-    /**
-     * @dev Return the log in base 256, rounded down, of a positive value.
-     * Returns 0 if given 0.
-     *
-     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
-     */
-    function log256(uint256 value) internal pure returns (uint256) {
-        uint256 result = 0;
-        unchecked {
-            if (value >> 128 > 0) {
-                value >>= 128;
-                result += 16;
-            }
-            if (value >> 64 > 0) {
-                value >>= 64;
-                result += 8;
-            }
-            if (value >> 32 > 0) {
-                value >>= 32;
-                result += 4;
-            }
-            if (value >> 16 > 0) {
-                value >>= 16;
-                result += 2;
-            }
-            if (value >> 8 > 0) {
-                result += 1;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = log256(value);
-            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
-        }
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/SafeCast.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/@openzeppelin/contracts/utils/math/SafeCast.sol
+++ /dev/null
@@ -1,1136 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
-// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
- * checks.
- *
- * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
- * easily result in undesired exploitation or bugs, since developers usually
- * assume that overflows raise errors. `SafeCast` restores this intuition by
- * reverting the transaction when such an operation overflows.
- *
- * Using this library instead of the unchecked operations eliminates an entire
- * class of bugs, so it's recommended to use it always.
- *
- * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
- * all math on `uint256` and `int256` and then downcasting.
- */
-library SafeCast {
-    /**
-     * @dev Returns the downcasted uint248 from uint256, reverting on
-     * overflow (when the input is greater than largest uint248).
-     *
-     * Counterpart to Solidity's `uint248` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 248 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint248(uint256 value) internal pure returns (uint248) {
-        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
-        return uint248(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint240 from uint256, reverting on
-     * overflow (when the input is greater than largest uint240).
-     *
-     * Counterpart to Solidity's `uint240` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 240 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint240(uint256 value) internal pure returns (uint240) {
-        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
-        return uint240(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint232 from uint256, reverting on
-     * overflow (when the input is greater than largest uint232).
-     *
-     * Counterpart to Solidity's `uint232` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 232 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint232(uint256 value) internal pure returns (uint232) {
-        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
-        return uint232(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint224 from uint256, reverting on
-     * overflow (when the input is greater than largest uint224).
-     *
-     * Counterpart to Solidity's `uint224` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 224 bits
-     *
-     * _Available since v4.2._
-     */
-    function toUint224(uint256 value) internal pure returns (uint224) {
-        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
-        return uint224(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint216 from uint256, reverting on
-     * overflow (when the input is greater than largest uint216).
-     *
-     * Counterpart to Solidity's `uint216` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 216 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint216(uint256 value) internal pure returns (uint216) {
-        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
-        return uint216(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint208 from uint256, reverting on
-     * overflow (when the input is greater than largest uint208).
-     *
-     * Counterpart to Solidity's `uint208` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 208 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint208(uint256 value) internal pure returns (uint208) {
-        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
-        return uint208(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint200 from uint256, reverting on
-     * overflow (when the input is greater than largest uint200).
-     *
-     * Counterpart to Solidity's `uint200` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 200 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint200(uint256 value) internal pure returns (uint200) {
-        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
-        return uint200(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint192 from uint256, reverting on
-     * overflow (when the input is greater than largest uint192).
-     *
-     * Counterpart to Solidity's `uint192` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 192 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint192(uint256 value) internal pure returns (uint192) {
-        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
-        return uint192(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint184 from uint256, reverting on
-     * overflow (when the input is greater than largest uint184).
-     *
-     * Counterpart to Solidity's `uint184` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 184 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint184(uint256 value) internal pure returns (uint184) {
-        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
-        return uint184(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint176 from uint256, reverting on
-     * overflow (when the input is greater than largest uint176).
-     *
-     * Counterpart to Solidity's `uint176` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 176 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint176(uint256 value) internal pure returns (uint176) {
-        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
-        return uint176(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint168 from uint256, reverting on
-     * overflow (when the input is greater than largest uint168).
-     *
-     * Counterpart to Solidity's `uint168` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 168 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint168(uint256 value) internal pure returns (uint168) {
-        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
-        return uint168(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint160 from uint256, reverting on
-     * overflow (when the input is greater than largest uint160).
-     *
-     * Counterpart to Solidity's `uint160` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 160 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint160(uint256 value) internal pure returns (uint160) {
-        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
-        return uint160(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint152 from uint256, reverting on
-     * overflow (when the input is greater than largest uint152).
-     *
-     * Counterpart to Solidity's `uint152` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 152 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint152(uint256 value) internal pure returns (uint152) {
-        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
-        return uint152(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint144 from uint256, reverting on
-     * overflow (when the input is greater than largest uint144).
-     *
-     * Counterpart to Solidity's `uint144` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 144 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint144(uint256 value) internal pure returns (uint144) {
-        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
-        return uint144(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint136 from uint256, reverting on
-     * overflow (when the input is greater than largest uint136).
-     *
-     * Counterpart to Solidity's `uint136` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 136 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint136(uint256 value) internal pure returns (uint136) {
-        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
-        return uint136(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint128 from uint256, reverting on
-     * overflow (when the input is greater than largest uint128).
-     *
-     * Counterpart to Solidity's `uint128` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 128 bits
-     *
-     * _Available since v2.5._
-     */
-    function toUint128(uint256 value) internal pure returns (uint128) {
-        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
-        return uint128(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint120 from uint256, reverting on
-     * overflow (when the input is greater than largest uint120).
-     *
-     * Counterpart to Solidity's `uint120` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 120 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint120(uint256 value) internal pure returns (uint120) {
-        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
-        return uint120(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint112 from uint256, reverting on
-     * overflow (when the input is greater than largest uint112).
-     *
-     * Counterpart to Solidity's `uint112` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 112 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint112(uint256 value) internal pure returns (uint112) {
-        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
-        return uint112(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint104 from uint256, reverting on
-     * overflow (when the input is greater than largest uint104).
-     *
-     * Counterpart to Solidity's `uint104` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 104 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint104(uint256 value) internal pure returns (uint104) {
-        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
-        return uint104(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint96 from uint256, reverting on
-     * overflow (when the input is greater than largest uint96).
-     *
-     * Counterpart to Solidity's `uint96` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 96 bits
-     *
-     * _Available since v4.2._
-     */
-    function toUint96(uint256 value) internal pure returns (uint96) {
-        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
-        return uint96(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint88 from uint256, reverting on
-     * overflow (when the input is greater than largest uint88).
-     *
-     * Counterpart to Solidity's `uint88` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 88 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint88(uint256 value) internal pure returns (uint88) {
-        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
-        return uint88(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint80 from uint256, reverting on
-     * overflow (when the input is greater than largest uint80).
-     *
-     * Counterpart to Solidity's `uint80` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 80 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint80(uint256 value) internal pure returns (uint80) {
-        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
-        return uint80(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint72 from uint256, reverting on
-     * overflow (when the input is greater than largest uint72).
-     *
-     * Counterpart to Solidity's `uint72` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 72 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint72(uint256 value) internal pure returns (uint72) {
-        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
-        return uint72(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint64 from uint256, reverting on
-     * overflow (when the input is greater than largest uint64).
-     *
-     * Counterpart to Solidity's `uint64` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 64 bits
-     *
-     * _Available since v2.5._
-     */
-    function toUint64(uint256 value) internal pure returns (uint64) {
-        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
-        return uint64(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint56 from uint256, reverting on
-     * overflow (when the input is greater than largest uint56).
-     *
-     * Counterpart to Solidity's `uint56` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 56 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint56(uint256 value) internal pure returns (uint56) {
-        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
-        return uint56(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint48 from uint256, reverting on
-     * overflow (when the input is greater than largest uint48).
-     *
-     * Counterpart to Solidity's `uint48` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 48 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint48(uint256 value) internal pure returns (uint48) {
-        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
-        return uint48(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint40 from uint256, reverting on
-     * overflow (when the input is greater than largest uint40).
-     *
-     * Counterpart to Solidity's `uint40` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 40 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint40(uint256 value) internal pure returns (uint40) {
-        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
-        return uint40(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint32 from uint256, reverting on
-     * overflow (when the input is greater than largest uint32).
-     *
-     * Counterpart to Solidity's `uint32` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 32 bits
-     *
-     * _Available since v2.5._
-     */
-    function toUint32(uint256 value) internal pure returns (uint32) {
-        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
-        return uint32(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint24 from uint256, reverting on
-     * overflow (when the input is greater than largest uint24).
-     *
-     * Counterpart to Solidity's `uint24` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 24 bits
-     *
-     * _Available since v4.7._
-     */
-    function toUint24(uint256 value) internal pure returns (uint24) {
-        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
-        return uint24(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint16 from uint256, reverting on
-     * overflow (when the input is greater than largest uint16).
-     *
-     * Counterpart to Solidity's `uint16` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 16 bits
-     *
-     * _Available since v2.5._
-     */
-    function toUint16(uint256 value) internal pure returns (uint16) {
-        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
-        return uint16(value);
-    }
-
-    /**
-     * @dev Returns the downcasted uint8 from uint256, reverting on
-     * overflow (when the input is greater than largest uint8).
-     *
-     * Counterpart to Solidity's `uint8` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 8 bits
-     *
-     * _Available since v2.5._
-     */
-    function toUint8(uint256 value) internal pure returns (uint8) {
-        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
-        return uint8(value);
-    }
-
-    /**
-     * @dev Converts a signed int256 into an unsigned uint256.
-     *
-     * Requirements:
-     *
-     * - input must be greater than or equal to 0.
-     *
-     * _Available since v3.0._
-     */
-    function toUint256(int256 value) internal pure returns (uint256) {
-        require(value >= 0, "SafeCast: value must be positive");
-        return uint256(value);
-    }
-
-    /**
-     * @dev Returns the downcasted int248 from int256, reverting on
-     * overflow (when the input is less than smallest int248 or
-     * greater than largest int248).
-     *
-     * Counterpart to Solidity's `int248` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 248 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt248(int256 value) internal pure returns (int248 downcasted) {
-        downcasted = int248(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int240 from int256, reverting on
-     * overflow (when the input is less than smallest int240 or
-     * greater than largest int240).
-     *
-     * Counterpart to Solidity's `int240` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 240 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt240(int256 value) internal pure returns (int240 downcasted) {
-        downcasted = int240(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int232 from int256, reverting on
-     * overflow (when the input is less than smallest int232 or
-     * greater than largest int232).
-     *
-     * Counterpart to Solidity's `int232` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 232 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt232(int256 value) internal pure returns (int232 downcasted) {
-        downcasted = int232(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int224 from int256, reverting on
-     * overflow (when the input is less than smallest int224 or
-     * greater than largest int224).
-     *
-     * Counterpart to Solidity's `int224` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 224 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt224(int256 value) internal pure returns (int224 downcasted) {
-        downcasted = int224(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int216 from int256, reverting on
-     * overflow (when the input is less than smallest int216 or
-     * greater than largest int216).
-     *
-     * Counterpart to Solidity's `int216` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 216 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt216(int256 value) internal pure returns (int216 downcasted) {
-        downcasted = int216(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int208 from int256, reverting on
-     * overflow (when the input is less than smallest int208 or
-     * greater than largest int208).
-     *
-     * Counterpart to Solidity's `int208` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 208 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt208(int256 value) internal pure returns (int208 downcasted) {
-        downcasted = int208(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int200 from int256, reverting on
-     * overflow (when the input is less than smallest int200 or
-     * greater than largest int200).
-     *
-     * Counterpart to Solidity's `int200` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 200 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt200(int256 value) internal pure returns (int200 downcasted) {
-        downcasted = int200(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int192 from int256, reverting on
-     * overflow (when the input is less than smallest int192 or
-     * greater than largest int192).
-     *
-     * Counterpart to Solidity's `int192` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 192 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt192(int256 value) internal pure returns (int192 downcasted) {
-        downcasted = int192(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int184 from int256, reverting on
-     * overflow (when the input is less than smallest int184 or
-     * greater than largest int184).
-     *
-     * Counterpart to Solidity's `int184` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 184 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt184(int256 value) internal pure returns (int184 downcasted) {
-        downcasted = int184(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int176 from int256, reverting on
-     * overflow (when the input is less than smallest int176 or
-     * greater than largest int176).
-     *
-     * Counterpart to Solidity's `int176` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 176 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt176(int256 value) internal pure returns (int176 downcasted) {
-        downcasted = int176(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int168 from int256, reverting on
-     * overflow (when the input is less than smallest int168 or
-     * greater than largest int168).
-     *
-     * Counterpart to Solidity's `int168` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 168 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt168(int256 value) internal pure returns (int168 downcasted) {
-        downcasted = int168(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int160 from int256, reverting on
-     * overflow (when the input is less than smallest int160 or
-     * greater than largest int160).
-     *
-     * Counterpart to Solidity's `int160` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 160 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt160(int256 value) internal pure returns (int160 downcasted) {
-        downcasted = int160(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int152 from int256, reverting on
-     * overflow (when the input is less than smallest int152 or
-     * greater than largest int152).
-     *
-     * Counterpart to Solidity's `int152` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 152 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt152(int256 value) internal pure returns (int152 downcasted) {
-        downcasted = int152(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int144 from int256, reverting on
-     * overflow (when the input is less than smallest int144 or
-     * greater than largest int144).
-     *
-     * Counterpart to Solidity's `int144` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 144 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt144(int256 value) internal pure returns (int144 downcasted) {
-        downcasted = int144(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int136 from int256, reverting on
-     * overflow (when the input is less than smallest int136 or
-     * greater than largest int136).
-     *
-     * Counterpart to Solidity's `int136` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 136 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt136(int256 value) internal pure returns (int136 downcasted) {
-        downcasted = int136(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int128 from int256, reverting on
-     * overflow (when the input is less than smallest int128 or
-     * greater than largest int128).
-     *
-     * Counterpart to Solidity's `int128` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 128 bits
-     *
-     * _Available since v3.1._
-     */
-    function toInt128(int256 value) internal pure returns (int128 downcasted) {
-        downcasted = int128(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int120 from int256, reverting on
-     * overflow (when the input is less than smallest int120 or
-     * greater than largest int120).
-     *
-     * Counterpart to Solidity's `int120` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 120 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt120(int256 value) internal pure returns (int120 downcasted) {
-        downcasted = int120(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int112 from int256, reverting on
-     * overflow (when the input is less than smallest int112 or
-     * greater than largest int112).
-     *
-     * Counterpart to Solidity's `int112` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 112 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt112(int256 value) internal pure returns (int112 downcasted) {
-        downcasted = int112(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int104 from int256, reverting on
-     * overflow (when the input is less than smallest int104 or
-     * greater than largest int104).
-     *
-     * Counterpart to Solidity's `int104` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 104 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt104(int256 value) internal pure returns (int104 downcasted) {
-        downcasted = int104(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int96 from int256, reverting on
-     * overflow (when the input is less than smallest int96 or
-     * greater than largest int96).
-     *
-     * Counterpart to Solidity's `int96` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 96 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt96(int256 value) internal pure returns (int96 downcasted) {
-        downcasted = int96(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int88 from int256, reverting on
-     * overflow (when the input is less than smallest int88 or
-     * greater than largest int88).
-     *
-     * Counterpart to Solidity's `int88` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 88 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt88(int256 value) internal pure returns (int88 downcasted) {
-        downcasted = int88(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int80 from int256, reverting on
-     * overflow (when the input is less than smallest int80 or
-     * greater than largest int80).
-     *
-     * Counterpart to Solidity's `int80` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 80 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt80(int256 value) internal pure returns (int80 downcasted) {
-        downcasted = int80(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int72 from int256, reverting on
-     * overflow (when the input is less than smallest int72 or
-     * greater than largest int72).
-     *
-     * Counterpart to Solidity's `int72` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 72 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt72(int256 value) internal pure returns (int72 downcasted) {
-        downcasted = int72(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int64 from int256, reverting on
-     * overflow (when the input is less than smallest int64 or
-     * greater than largest int64).
-     *
-     * Counterpart to Solidity's `int64` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 64 bits
-     *
-     * _Available since v3.1._
-     */
-    function toInt64(int256 value) internal pure returns (int64 downcasted) {
-        downcasted = int64(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int56 from int256, reverting on
-     * overflow (when the input is less than smallest int56 or
-     * greater than largest int56).
-     *
-     * Counterpart to Solidity's `int56` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 56 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt56(int256 value) internal pure returns (int56 downcasted) {
-        downcasted = int56(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int48 from int256, reverting on
-     * overflow (when the input is less than smallest int48 or
-     * greater than largest int48).
-     *
-     * Counterpart to Solidity's `int48` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 48 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt48(int256 value) internal pure returns (int48 downcasted) {
-        downcasted = int48(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int40 from int256, reverting on
-     * overflow (when the input is less than smallest int40 or
-     * greater than largest int40).
-     *
-     * Counterpart to Solidity's `int40` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 40 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt40(int256 value) internal pure returns (int40 downcasted) {
-        downcasted = int40(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int32 from int256, reverting on
-     * overflow (when the input is less than smallest int32 or
-     * greater than largest int32).
-     *
-     * Counterpart to Solidity's `int32` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 32 bits
-     *
-     * _Available since v3.1._
-     */
-    function toInt32(int256 value) internal pure returns (int32 downcasted) {
-        downcasted = int32(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int24 from int256, reverting on
-     * overflow (when the input is less than smallest int24 or
-     * greater than largest int24).
-     *
-     * Counterpart to Solidity's `int24` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 24 bits
-     *
-     * _Available since v4.7._
-     */
-    function toInt24(int256 value) internal pure returns (int24 downcasted) {
-        downcasted = int24(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int16 from int256, reverting on
-     * overflow (when the input is less than smallest int16 or
-     * greater than largest int16).
-     *
-     * Counterpart to Solidity's `int16` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 16 bits
-     *
-     * _Available since v3.1._
-     */
-    function toInt16(int256 value) internal pure returns (int16 downcasted) {
-        downcasted = int16(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
-    }
-
-    /**
-     * @dev Returns the downcasted int8 from int256, reverting on
-     * overflow (when the input is less than smallest int8 or
-     * greater than largest int8).
-     *
-     * Counterpart to Solidity's `int8` operator.
-     *
-     * Requirements:
-     *
-     * - input must fit into 8 bits
-     *
-     * _Available since v3.1._
-     */
-    function toInt8(int256 value) internal pure returns (int8 downcasted) {
-        downcasted = int8(value);
-        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
-    }
-
-    /**
-     * @dev Converts an unsigned uint256 into a signed int256.
-     *
-     * Requirements:
-     *
-     * - input must be less than or equal to maxInt256.
-     *
-     * _Available since v3.0._
-     */
-    function toInt256(uint256 value) internal pure returns (int256) {
-        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
-        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
-        return int256(value);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/ZeppelinContract.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/ZeppelinContract.sol
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.9;
-
-import "./@openzeppelin/contracts/token/ERC20/ERC20.sol";
-import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
-import "./@openzeppelin/contracts/security/Pausable.sol";
-import "./@openzeppelin/contracts/access/Ownable.sol";
-import "./@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
-import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
-import "./@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol";
-
-contract ZeppelinContract is ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint {
-    constructor()
-        ERC20("ZeppelinContract", "UNQ")
-        ERC20Permit("ZeppelinContract")
-    {}
-
-    function pause() public onlyOwner {
-        _pause();
-    }
-
-    function unpause() public onlyOwner {
-        _unpause();
-    }
-
-    function mint(address to, uint256 amount) public onlyOwner {
-        _mint(to, amount);
-    }
-
-    function _beforeTokenTransfer(address from, address to, uint256 amount)
-        internal
-        whenNotPaused
-        override
-    {
-        super._beforeTokenTransfer(from, to, amount);
-    }
-
-    // The following functions are overrides required by Solidity.
-
-    function _afterTokenTransfer(address from, address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._afterTokenTransfer(from, to, amount);
-    }
-
-    function _mint(address to, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._mint(to, amount);
-    }
-
-    function _burn(address account, uint256 amount)
-        internal
-        override(ERC20, ERC20Votes)
-    {
-        super._burn(account, amount);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.abidiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.abi
+++ /dev/null
@@ -1 +0,0 @@
-[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"pos","type":"uint32"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint224","name":"votes","type":"uint224"}],"internalType":"struct ERC20Votes.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"flashFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC3156FlashBorrower","name":"receiver","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"maxFlashLoan","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.bindiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC20/bin/ZeppelinContract.bin
+++ /dev/null
@@ -1 +0,0 @@
-6101406040523480156200001257600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525080604051806040016040528060018152602001603160f81b8152506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b8152508160039081620000ad919062000269565b506004620000bc828262000269565b50506005805460ff1916905550620000d4336200016a565b815160209283012081519183019190912060e08290526101008190524660a0818152604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f818801819052818301969096526060810194909452608080850193909352308483018190528151808603909301835260c094850190915281519190950120905291909152610120525062000335565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c90821680620001ef57607f821691505b6020821081036200021057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200026457600081815260208120601f850160051c810160208610156200023f5750805b601f850160051c820191505b8181101562000260578281556001016200024b565b5050505b505050565b81516001600160401b03811115620002855762000285620001c4565b6200029d81620002968454620001da565b8462000216565b602080601f831160018114620002d55760008415620002bc5750858301515b600019600386901b1c1916600185901b17855562000260565b600085815260208120601f198616915b828110156200030657888601518255948401946001909101908401620002e5565b5085821015620003255787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e05161010051610120516125f06200038560003960006112ad015260006112fc015260006112d7015260006112300152600061125a0152600061128401526125f06000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806370a082311161011a5780639ab24eb0116100ad578063d505accf1161007c578063d505accf1461046a578063d9d98ce41461047d578063dd62ed3e14610490578063f1127ed8146104a3578063f2fde38b146104e057600080fd5b80639ab24eb01461041e578063a457c2d714610431578063a9059cbb14610444578063c3cda5201461045757600080fd5b80638456cb59116100e95780638456cb59146103e55780638da5cb5b146103ed5780638e539e8c1461040357806395d89b411461041657600080fd5b806370a082311461038e578063715018a6146103b757806379cc6790146103bf5780637ecebe00146103d257600080fd5b80633f4ba83a1161019d5780635c19a95c1161016c5780635c19a95c146103225780635c975abb146103355780635cffe9de14610340578063613255ab146103535780636fcfff451461036657600080fd5b80633f4ba83a146102ae57806340c10f19146102b857806342966c68146102cb578063587cde1e146102de57600080fd5b8063313ce567116101d9578063313ce567146102715780633644e5151461028057806339509351146102885780633a46b1a81461029b57600080fd5b806306fdde031461020b578063095ea7b31461022957806318160ddd1461024c57806323b872dd1461025e575b600080fd5b6102136104f3565b604051610220919061217e565b60405180910390f35b61023c6102373660046121e1565b610585565b6040519015158152602001610220565b6002545b604051908152602001610220565b61023c61026c36600461220d565b61059f565b60405160128152602001610220565b6102506105c3565b61023c6102963660046121e1565b6105d2565b6102506102a93660046121e1565b6105f4565b6102b6610673565b005b6102b66102c63660046121e1565b610685565b6102b66102d936600461224e565b61069b565b61030a6102ec366004612267565b6001600160a01b039081166000908152600860205260409020541690565b6040516001600160a01b039091168152602001610220565b6102b6610330366004612267565b6106a8565b60055460ff1661023c565b61023c61034e366004612284565b6106b2565b610250610361366004612267565b610896565b610379610374366004612267565b6108be565b60405163ffffffff9091168152602001610220565b61025061039c366004612267565b6001600160a01b031660009081526020819052604090205490565b6102b66108e0565b6102b66103cd3660046121e1565b6108f2565b6102506103e0366004612267565b610907565b6102b6610925565b60055461010090046001600160a01b031661030a565b61025061041136600461224e565b610935565b610213610991565b61025061042c366004612267565b6109a0565b61023c61043f3660046121e1565b610a27565b61023c6104523660046121e1565b610aa2565b6102b6610465366004612339565b610ab0565b6102b6610478366004612393565b610be6565b61025061048b3660046121e1565b610d4a565b61025061049e366004612401565b610dab565b6104b66104b136600461243a565b610dd6565b60408051825163ffffffff1681526020928301516001600160e01b03169281019290925201610220565b6102b66104ee366004612267565b610e5a565b60606003805461050290612471565b80601f016020809104026020016040519081016040528092919081815260200182805461052e90612471565b801561057b5780601f106105505761010080835404028352916020019161057b565b820191906000526020600020905b81548152906001019060200180831161055e57829003601f168201915b5050505050905090565b600033610593818585610ed0565b60019150505b92915050565b6000336105ad858285610ff4565b6105b885858561106e565b506001949350505050565b60006105cd611223565b905090565b6000336105938185856105e58383610dab565b6105ef91906124bb565b610ed0565b600043821061064a5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e65640060448201526064015b60405180910390fd5b6001600160a01b038316600090815260096020526040902061066c908361134a565b9392505050565b61067b611441565b6106836114a1565b565b61068d611441565b61069782826114f3565b5050565b6106a533826114fd565b50565b6106a53382611507565b60006106bd85610896565b8411156107205760405162461bcd60e51b815260206004820152602b60248201527f4552433230466c6173684d696e743a20616d6f756e742065786365656473206d60448201526a30bc233630b9b42637b0b760a91b6064820152608401610641565b600061072c8686610d4a565b905061073887866114f3565b6040516323e30c8b60e01b81527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9906001600160a01b038916906323e30c8b906107909033908b908b9088908c908c906004016124ce565b6020604051808303816000875af11580156107af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d3919061252a565b1461082c5760405162461bcd60e51b8152602060048201526024808201527f4552433230466c6173684d696e743a20696e76616c69642072657475726e2076604482015263616c756560e01b6064820152608401610641565b6000610842883061083d858a6124bb565b610ff4565b81158061085657506001600160a01b038116155b156108735761086e8861086984896124bb565b6114fd565b610888565b61087d88876114fd565b61088888828461106e565b506001979650505050505050565b60006001600160a01b03821630146108af576000610599565b60025461059990600019612543565b6001600160a01b03811660009081526009602052604081205461059990611580565b6108e8611441565b61068360006115e9565b6108fd823383610ff4565b61069782826114fd565b6001600160a01b038116600090815260066020526040812054610599565b61092d611441565b610683611643565b60004382106109865760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610641565b610599600a8361134a565b60606004805461050290612471565b6001600160a01b0381166000908152600960205260408120548015610a14576001600160a01b03831660009081526009602052604090206109e2600183612543565b815481106109f2576109f2612556565b60009182526020909120015464010000000090046001600160e01b0316610a17565b60005b6001600160e01b03169392505050565b60003381610a358286610dab565b905083811015610a955760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610641565b6105b88286868403610ed0565b60003361059381858561106e565b83421115610b005760405162461bcd60e51b815260206004820152601d60248201527f4552433230566f7465733a207369676e617475726520657870697265640000006044820152606401610641565b604080517fe48329057bfd03d55e49b547132e39cffd9c1820ad7b9d4c5307691425d15adf60208201526001600160a01b038816918101919091526060810186905260808101859052600090610b7a90610b729060a00160405160208183030381529060405280519060200120611680565b8585856116ce565b9050610b85816116f6565b8614610bd35760405162461bcd60e51b815260206004820152601960248201527f4552433230566f7465733a20696e76616c6964206e6f6e6365000000000000006044820152606401610641565b610bdd8188611507565b50505050505050565b83421115610c365760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610641565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9888888610c658c6116f6565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610cc082611680565b90506000610cd0828787876116ce565b9050896001600160a01b0316816001600160a01b031614610d335760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610641565b610d3e8a8a8a610ed0565b50505050505050505050565b60006001600160a01b0383163014610da45760405162461bcd60e51b815260206004820152601b60248201527f4552433230466c6173684d696e743a2077726f6e6720746f6b656e00000000006044820152606401610641565b600061066c565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60408051808201909152600080825260208201526001600160a01b0383166000908152600960205260409020805463ffffffff8416908110610e1a57610e1a612556565b60009182526020918290206040805180820190915291015463ffffffff8116825264010000000090046001600160e01b0316918101919091529392505050565b610e62611441565b6001600160a01b038116610ec75760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610641565b6106a5816115e9565b6001600160a01b038316610f325760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610641565b6001600160a01b038216610f935760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610641565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006110008484610dab565b90506000198114611068578181101561105b5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610641565b6110688484848403610ed0565b50505050565b6001600160a01b0383166110d25760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610641565b6001600160a01b0382166111345760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610641565b61113f83838361171e565b6001600160a01b038316600090815260208190526040902054818110156111b75760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610641565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a361106884848461172b565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561127c57507f000000000000000000000000000000000000000000000000000000000000000046145b156112a657507f000000000000000000000000000000000000000000000000000000000000000090565b50604080517f00000000000000000000000000000000000000000000000000000000000000006020808301919091527f0000000000000000000000000000000000000000000000000000000000000000828401527f000000000000000000000000000000000000000000000000000000000000000060608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b8154600090818160058111156113a457600061136584611736565b61136f9085612543565b600088815260209020909150869082015463ffffffff161115611394578091506113a2565b61139f8160016124bb565b92505b505b808210156113f15760006113b8838361181e565b600088815260209020909150869082015463ffffffff1611156113dd578091506113eb565b6113e88160016124bb565b92505b506113a4565b801561142b5761141486611406600184612543565b600091825260209091200190565b5464010000000090046001600160e01b031661142e565b60005b6001600160e01b03169695505050505050565b6005546001600160a01b036101009091041633146106835760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610641565b6114a9611839565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6106978282611882565b610697828261190c565b6001600160a01b038281166000818152600860208181526040808420805485845282862054949093528787166001600160a01b03198416811790915590519190951694919391928592917f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f9190a4611068828483611924565b600063ffffffff8211156115e55760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610641565b5090565b600580546001600160a01b03838116610100818102610100600160a81b031985161790945560405193909204169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b61164b611a61565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586114d63390565b600061059961168d611223565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b60008060006116df87878787611aa7565b915091506116ec81611b6b565b5095945050505050565b6001600160a01b03811660009081526006602052604090208054600181018255905b50919050565b611726611a61565b505050565b611726838383611cb5565b60008160000361174857506000919050565b6000600161175584611ce7565b901c6001901b9050600181848161176e5761176e61256c565b048201901c905060018184816117865761178661256c565b048201901c9050600181848161179e5761179e61256c565b048201901c905060018184816117b6576117b661256c565b048201901c905060018184816117ce576117ce61256c565b048201901c905060018184816117e6576117e661256c565b048201901c905060018184816117fe576117fe61256c565b048201901c905061066c818285816118185761181861256c565b04611d7b565b600061182d6002848418612582565b61066c908484166124bb565b60055460ff166106835760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610641565b61188c8282611d91565b6002546001600160e01b0310156118fe5760405162461bcd60e51b815260206004820152603060248201527f4552433230566f7465733a20746f74616c20737570706c79207269736b73206f60448201526f766572666c6f77696e6720766f74657360801b6064820152608401610641565b611068600a611e6483611e70565b6119168282611fc4565b611068600a61210983611e70565b816001600160a01b0316836001600160a01b0316141580156119465750600081115b15611726576001600160a01b038316156119d4576001600160a01b038316600090815260096020526040812081906119819061210985611e70565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516119c9929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615611726576001600160a01b03821660009081526009602052604081208190611a0a90611e6485611e70565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a7248383604051611a52929190918252602082015260400190565b60405180910390a25050505050565b60055460ff16156106835760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610641565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115611ade5750600090506003611b62565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611b32573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116611b5b57600060019250925050611b62565b9150600090505b94509492505050565b6000816004811115611b7f57611b7f6125a4565b03611b875750565b6001816004811115611b9b57611b9b6125a4565b03611be85760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610641565b6002816004811115611bfc57611bfc6125a4565b03611c495760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610641565b6003816004811115611c5d57611c5d6125a4565b036106a55760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610641565b6001600160a01b0383811660009081526008602052604080822054858416835291205461172692918216911683611924565b600080608083901c15611cfc57608092831c92015b604083901c15611d0e57604092831c92015b602083901c15611d2057602092831c92015b601083901c15611d3257601092831c92015b600883901c15611d4457600892831c92015b600483901c15611d5657600492831c92015b600283901c15611d6857600292831c92015b600183901c156105995760010192915050565b6000818310611d8a578161066c565b5090919050565b6001600160a01b038216611de75760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610641565b611df36000838361171e565b8060026000828254611e0591906124bb565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36106976000838361172b565b600061066c82846124bb565b82546000908190818115611ebd57611e8d87611406600185612543565b60408051808201909152905463ffffffff8116825264010000000090046001600160e01b03166020820152611ed2565b60408051808201909152600080825260208201525b905080602001516001600160e01b03169350611ef284868863ffffffff16565b9250600082118015611f0a5750805163ffffffff1643145b15611f4f57611f1883612115565b611f2788611406600186612543565b80546001600160e01b03929092166401000000000263ffffffff909216919091179055611fba565b866040518060400160405280611f6443611580565b63ffffffff168152602001611f7886612115565b6001600160e01b0390811690915282546001810184556000938452602093849020835194909301519091166401000000000263ffffffff909316929092179101555b5050935093915050565b6001600160a01b0382166120245760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610641565b6120308260008361171e565b6001600160a01b038216600090815260208190526040902054818110156120a45760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610641565b6001600160a01b0383166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36117268360008461172b565b600061066c8284612543565b60006001600160e01b038211156115e55760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610641565b600060208083528351808285015260005b818110156121ab5785810183015185820160400152820161218f565b506000604082860101526040601f19601f8301168501019250505092915050565b6001600160a01b03811681146106a557600080fd5b600080604083850312156121f457600080fd5b82356121ff816121cc565b946020939093013593505050565b60008060006060848603121561222257600080fd5b833561222d816121cc565b9250602084013561223d816121cc565b929592945050506040919091013590565b60006020828403121561226057600080fd5b5035919050565b60006020828403121561227957600080fd5b813561066c816121cc565b60008060008060006080868803121561229c57600080fd5b85356122a7816121cc565b945060208601356122b7816121cc565b935060408601359250606086013567ffffffffffffffff808211156122db57600080fd5b818801915088601f8301126122ef57600080fd5b8135818111156122fe57600080fd5b89602082850101111561231057600080fd5b9699959850939650602001949392505050565b803560ff8116811461233457600080fd5b919050565b60008060008060008060c0878903121561235257600080fd5b863561235d816121cc565b9550602087013594506040870135935061237960608801612323565b92506080870135915060a087013590509295509295509295565b600080600080600080600060e0888a0312156123ae57600080fd5b87356123b9816121cc565b965060208801356123c9816121cc565b955060408801359450606088013593506123e560808901612323565b925060a0880135915060c0880135905092959891949750929550565b6000806040838503121561241457600080fd5b823561241f816121cc565b9150602083013561242f816121cc565b809150509250929050565b6000806040838503121561244d57600080fd5b8235612458816121cc565b9150602083013563ffffffff8116811461242f57600080fd5b600181811c9082168061248557607f821691505b60208210810361171857634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115610599576105996124a5565b6001600160a01b03878116825286166020820152604081018590526060810184905260a06080820181905281018290526000828460c0840137600060c0848401015260c0601f19601f8501168301019050979650505050505050565b60006020828403121561253c57600080fd5b5051919050565b81810381811115610599576105996124a5565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b60008261259f57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052602160045260246000fdfea264697066735822122057e08ed799a9773c2de4d8c40122801fc9994042a2e3e3ac01e5aa1c384b1c4b64736f6c63430008110033
\ No newline at end of file
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/README.mddiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# OpenZeppelin Contracts
-
-The files in this directory were sourced unmodified from OpenZeppelin Contracts v4.8.1.
-
-They are not meant to be edited.
-
-The originals can be found on [GitHub] and [npm].
-
-[GitHub]: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.8.1
-[npm]: https://www.npmjs.com/package/@openzeppelin/contracts/v/4.8.1
-
-Generated with OpenZeppelin Contracts Wizard (https://zpl.in/wizard).
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/access/Ownable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/access/Ownable.sol
+++ /dev/null
@@ -1,83 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
-
-pragma solidity ^0.8.0;
-
-import "../utils/Context.sol";
-
-/**
- * @dev Contract module which provides a basic access control mechanism, where
- * there is an account (an owner) that can be granted exclusive access to
- * specific functions.
- *
- * By default, the owner account will be the one that deploys the contract. This
- * can later be changed with {transferOwnership}.
- *
- * This module is used through inheritance. It will make available the modifier
- * `onlyOwner`, which can be applied to your functions to restrict their use to
- * the owner.
- */
-abstract contract Ownable is Context {
-    address private _owner;
-
-    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
-
-    /**
-     * @dev Initializes the contract setting the deployer as the initial owner.
-     */
-    constructor() {
-        _transferOwnership(_msgSender());
-    }
-
-    /**
-     * @dev Throws if called by any account other than the owner.
-     */
-    modifier onlyOwner() {
-        _checkOwner();
-        _;
-    }
-
-    /**
-     * @dev Returns the address of the current owner.
-     */
-    function owner() public view virtual returns (address) {
-        return _owner;
-    }
-
-    /**
-     * @dev Throws if the sender is not the owner.
-     */
-    function _checkOwner() internal view virtual {
-        require(owner() == _msgSender(), "Ownable: caller is not the owner");
-    }
-
-    /**
-     * @dev Leaves the contract without owner. It will not be possible to call
-     * `onlyOwner` functions anymore. Can only be called by the current owner.
-     *
-     * NOTE: Renouncing ownership will leave the contract without an owner,
-     * thereby removing any functionality that is only available to the owner.
-     */
-    function renounceOwnership() public virtual onlyOwner {
-        _transferOwnership(address(0));
-    }
-
-    /**
-     * @dev Transfers ownership of the contract to a new account (`newOwner`).
-     * Can only be called by the current owner.
-     */
-    function transferOwnership(address newOwner) public virtual onlyOwner {
-        require(newOwner != address(0), "Ownable: new owner is the zero address");
-        _transferOwnership(newOwner);
-    }
-
-    /**
-     * @dev Transfers ownership of the contract to a new account (`newOwner`).
-     * Internal function without access restriction.
-     */
-    function _transferOwnership(address newOwner) internal virtual {
-        address oldOwner = _owner;
-        _owner = newOwner;
-        emit OwnershipTransferred(oldOwner, newOwner);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/ERC721.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/ERC721.sol
+++ /dev/null
@@ -1,503 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/ERC721.sol)
-
-pragma solidity ^0.8.0;
-
-import "./IERC721.sol";
-import "./IERC721Receiver.sol";
-import "./extensions/IERC721Metadata.sol";
-import "../../utils/Address.sol";
-import "../../utils/Context.sol";
-import "../../utils/Strings.sol";
-import "../../utils/introspection/ERC165.sol";
-
-/**
- * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
- * the Metadata extension, but not including the Enumerable extension, which is available separately as
- * {ERC721Enumerable}.
- */
-contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
-    using Address for address;
-    using Strings for uint256;
-
-    // Token name
-    string private _name;
-
-    // Token symbol
-    string private _symbol;
-
-    // Mapping from token ID to owner address
-    mapping(uint256 => address) private _owners;
-
-    // Mapping owner address to token count
-    mapping(address => uint256) private _balances;
-
-    // Mapping from token ID to approved address
-    mapping(uint256 => address) private _tokenApprovals;
-
-    // Mapping from owner to operator approvals
-    mapping(address => mapping(address => bool)) private _operatorApprovals;
-
-    /**
-     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
-     */
-    constructor(string memory name_, string memory symbol_) {
-        _name = name_;
-        _symbol = symbol_;
-    }
-
-    /**
-     * @dev See {IERC165-supportsInterface}.
-     */
-    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
-        return
-            interfaceId == type(IERC721).interfaceId ||
-            interfaceId == type(IERC721Metadata).interfaceId ||
-            super.supportsInterface(interfaceId);
-    }
-
-    /**
-     * @dev See {IERC721-balanceOf}.
-     */
-    function balanceOf(address owner) public view virtual override returns (uint256) {
-        require(owner != address(0), "ERC721: address zero is not a valid owner");
-        return _balances[owner];
-    }
-
-    /**
-     * @dev See {IERC721-ownerOf}.
-     */
-    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
-        address owner = _ownerOf(tokenId);
-        require(owner != address(0), "ERC721: invalid token ID");
-        return owner;
-    }
-
-    /**
-     * @dev See {IERC721Metadata-name}.
-     */
-    function name() public view virtual override returns (string memory) {
-        return _name;
-    }
-
-    /**
-     * @dev See {IERC721Metadata-symbol}.
-     */
-    function symbol() public view virtual override returns (string memory) {
-        return _symbol;
-    }
-
-    /**
-     * @dev See {IERC721Metadata-tokenURI}.
-     */
-    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
-        _requireMinted(tokenId);
-
-        string memory baseURI = _baseURI();
-        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
-    }
-
-    /**
-     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
-     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
-     * by default, can be overridden in child contracts.
-     */
-    function _baseURI() internal view virtual returns (string memory) {
-        return "";
-    }
-
-    /**
-     * @dev See {IERC721-approve}.
-     */
-    function approve(address to, uint256 tokenId) public virtual override {
-        address owner = ERC721.ownerOf(tokenId);
-        require(to != owner, "ERC721: approval to current owner");
-
-        require(
-            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
-            "ERC721: approve caller is not token owner or approved for all"
-        );
-
-        _approve(to, tokenId);
-    }
-
-    /**
-     * @dev See {IERC721-getApproved}.
-     */
-    function getApproved(uint256 tokenId) public view virtual override returns (address) {
-        _requireMinted(tokenId);
-
-        return _tokenApprovals[tokenId];
-    }
-
-    /**
-     * @dev See {IERC721-setApprovalForAll}.
-     */
-    function setApprovalForAll(address operator, bool approved) public virtual override {
-        _setApprovalForAll(_msgSender(), operator, approved);
-    }
-
-    /**
-     * @dev See {IERC721-isApprovedForAll}.
-     */
-    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
-        return _operatorApprovals[owner][operator];
-    }
-
-    /**
-     * @dev See {IERC721-transferFrom}.
-     */
-    function transferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) public virtual override {
-        //solhint-disable-next-line max-line-length
-        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
-
-        _transfer(from, to, tokenId);
-    }
-
-    /**
-     * @dev See {IERC721-safeTransferFrom}.
-     */
-    function safeTransferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) public virtual override {
-        safeTransferFrom(from, to, tokenId, "");
-    }
-
-    /**
-     * @dev See {IERC721-safeTransferFrom}.
-     */
-    function safeTransferFrom(
-        address from,
-        address to,
-        uint256 tokenId,
-        bytes memory data
-    ) public virtual override {
-        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
-        _safeTransfer(from, to, tokenId, data);
-    }
-
-    /**
-     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
-     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
-     *
-     * `data` is additional data, it has no specified format and it is sent in call to `to`.
-     *
-     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
-     * implement alternative mechanisms to perform token transfer, such as signature-based.
-     *
-     * Requirements:
-     *
-     * - `from` cannot be the zero address.
-     * - `to` cannot be the zero address.
-     * - `tokenId` token must exist and be owned by `from`.
-     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
-     *
-     * Emits a {Transfer} event.
-     */
-    function _safeTransfer(
-        address from,
-        address to,
-        uint256 tokenId,
-        bytes memory data
-    ) internal virtual {
-        _transfer(from, to, tokenId);
-        require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
-    }
-
-    /**
-     * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
-     */
-    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
-        return _owners[tokenId];
-    }
-
-    /**
-     * @dev Returns whether `tokenId` exists.
-     *
-     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
-     *
-     * Tokens start existing when they are minted (`_mint`),
-     * and stop existing when they are burned (`_burn`).
-     */
-    function _exists(uint256 tokenId) internal view virtual returns (bool) {
-        return _ownerOf(tokenId) != address(0);
-    }
-
-    /**
-     * @dev Returns whether `spender` is allowed to manage `tokenId`.
-     *
-     * Requirements:
-     *
-     * - `tokenId` must exist.
-     */
-    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
-        address owner = ERC721.ownerOf(tokenId);
-        return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
-    }
-
-    /**
-     * @dev Safely mints `tokenId` and transfers it to `to`.
-     *
-     * Requirements:
-     *
-     * - `tokenId` must not exist.
-     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
-     *
-     * Emits a {Transfer} event.
-     */
-    function _safeMint(address to, uint256 tokenId) internal virtual {
-        _safeMint(to, tokenId, "");
-    }
-
-    /**
-     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
-     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
-     */
-    function _safeMint(
-        address to,
-        uint256 tokenId,
-        bytes memory data
-    ) internal virtual {
-        _mint(to, tokenId);
-        require(
-            _checkOnERC721Received(address(0), to, tokenId, data),
-            "ERC721: transfer to non ERC721Receiver implementer"
-        );
-    }
-
-    /**
-     * @dev Mints `tokenId` and transfers it to `to`.
-     *
-     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
-     *
-     * Requirements:
-     *
-     * - `tokenId` must not exist.
-     * - `to` cannot be the zero address.
-     *
-     * Emits a {Transfer} event.
-     */
-    function _mint(address to, uint256 tokenId) internal virtual {
-        require(to != address(0), "ERC721: mint to the zero address");
-        require(!_exists(tokenId), "ERC721: token already minted");
-
-        _beforeTokenTransfer(address(0), to, tokenId, 1);
-
-        // Check that tokenId was not minted by `_beforeTokenTransfer` hook
-        require(!_exists(tokenId), "ERC721: token already minted");
-
-        unchecked {
-            // Will not overflow unless all 2**256 token ids are minted to the same owner.
-            // Given that tokens are minted one by one, it is impossible in practice that
-            // this ever happens. Might change if we allow batch minting.
-            // The ERC fails to describe this case.
-            _balances[to] += 1;
-        }
-
-        _owners[tokenId] = to;
-
-        emit Transfer(address(0), to, tokenId);
-
-        _afterTokenTransfer(address(0), to, tokenId, 1);
-    }
-
-    /**
-     * @dev Destroys `tokenId`.
-     * The approval is cleared when the token is burned.
-     * This is an internal function that does not check if the sender is authorized to operate on the token.
-     *
-     * Requirements:
-     *
-     * - `tokenId` must exist.
-     *
-     * Emits a {Transfer} event.
-     */
-    function _burn(uint256 tokenId) internal virtual {
-        address owner = ERC721.ownerOf(tokenId);
-
-        _beforeTokenTransfer(owner, address(0), tokenId, 1);
-
-        // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
-        owner = ERC721.ownerOf(tokenId);
-
-        // Clear approvals
-        delete _tokenApprovals[tokenId];
-
-        unchecked {
-            // Cannot overflow, as that would require more tokens to be burned/transferred
-            // out than the owner initially received through minting and transferring in.
-            _balances[owner] -= 1;
-        }
-        delete _owners[tokenId];
-
-        emit Transfer(owner, address(0), tokenId);
-
-        _afterTokenTransfer(owner, address(0), tokenId, 1);
-    }
-
-    /**
-     * @dev Transfers `tokenId` from `from` to `to`.
-     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
-     *
-     * Requirements:
-     *
-     * - `to` cannot be the zero address.
-     * - `tokenId` token must be owned by `from`.
-     *
-     * Emits a {Transfer} event.
-     */
-    function _transfer(
-        address from,
-        address to,
-        uint256 tokenId
-    ) internal virtual {
-        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
-        require(to != address(0), "ERC721: transfer to the zero address");
-
-        _beforeTokenTransfer(from, to, tokenId, 1);
-
-        // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
-        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
-
-        // Clear approvals from the previous owner
-        delete _tokenApprovals[tokenId];
-
-        unchecked {
-            // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
-            // `from`'s balance is the number of token held, which is at least one before the current
-            // transfer.
-            // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
-            // all 2**256 token ids to be minted, which in practice is impossible.
-            _balances[from] -= 1;
-            _balances[to] += 1;
-        }
-        _owners[tokenId] = to;
-
-        emit Transfer(from, to, tokenId);
-
-        _afterTokenTransfer(from, to, tokenId, 1);
-    }
-
-    /**
-     * @dev Approve `to` to operate on `tokenId`
-     *
-     * Emits an {Approval} event.
-     */
-    function _approve(address to, uint256 tokenId) internal virtual {
-        _tokenApprovals[tokenId] = to;
-        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
-    }
-
-    /**
-     * @dev Approve `operator` to operate on all of `owner` tokens
-     *
-     * Emits an {ApprovalForAll} event.
-     */
-    function _setApprovalForAll(
-        address owner,
-        address operator,
-        bool approved
-    ) internal virtual {
-        require(owner != operator, "ERC721: approve to caller");
-        _operatorApprovals[owner][operator] = approved;
-        emit ApprovalForAll(owner, operator, approved);
-    }
-
-    /**
-     * @dev Reverts if the `tokenId` has not been minted yet.
-     */
-    function _requireMinted(uint256 tokenId) internal view virtual {
-        require(_exists(tokenId), "ERC721: invalid token ID");
-    }
-
-    /**
-     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
-     * The call is not executed if the target address is not a contract.
-     *
-     * @param from address representing the previous owner of the given token ID
-     * @param to target address that will receive the tokens
-     * @param tokenId uint256 ID of the token to be transferred
-     * @param data bytes optional data to send along with the call
-     * @return bool whether the call correctly returned the expected magic value
-     */
-    function _checkOnERC721Received(
-        address from,
-        address to,
-        uint256 tokenId,
-        bytes memory data
-    ) private returns (bool) {
-        if (to.isContract()) {
-            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
-                return retval == IERC721Receiver.onERC721Received.selector;
-            } catch (bytes memory reason) {
-                if (reason.length == 0) {
-                    revert("ERC721: transfer to non ERC721Receiver implementer");
-                } else {
-                    /// @solidity memory-safe-assembly
-                    assembly {
-                        revert(add(32, reason), mload(reason))
-                    }
-                }
-            }
-        } else {
-            return true;
-        }
-    }
-
-    /**
-     * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
-     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
-     *
-     * Calling conditions:
-     *
-     * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
-     * - When `from` is zero, the tokens will be minted for `to`.
-     * - When `to` is zero, ``from``'s tokens will be burned.
-     * - `from` and `to` are never both zero.
-     * - `batchSize` is non-zero.
-     *
-     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
-     */
-    function _beforeTokenTransfer(
-        address from,
-        address to,
-        uint256, /* firstTokenId */
-        uint256 batchSize
-    ) internal virtual {
-        if (batchSize > 1) {
-            if (from != address(0)) {
-                _balances[from] -= batchSize;
-            }
-            if (to != address(0)) {
-                _balances[to] += batchSize;
-            }
-        }
-    }
-
-    /**
-     * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
-     * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
-     *
-     * Calling conditions:
-     *
-     * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
-     * - When `from` is zero, the tokens were minted for `to`.
-     * - When `to` is zero, ``from``'s tokens were burned.
-     * - `from` and `to` are never both zero.
-     * - `batchSize` is non-zero.
-     *
-     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
-     */
-    function _afterTokenTransfer(
-        address from,
-        address to,
-        uint256 firstTokenId,
-        uint256 batchSize
-    ) internal virtual {}
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721.sol
+++ /dev/null
@@ -1,145 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)
-
-pragma solidity ^0.8.0;
-
-import "../../utils/introspection/IERC165.sol";
-
-/**
- * @dev Required interface of an ERC721 compliant contract.
- */
-interface IERC721 is IERC165 {
-    /**
-     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
-     */
-    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
-
-    /**
-     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
-     */
-    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
-
-    /**
-     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
-     */
-    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
-
-    /**
-     * @dev Returns the number of tokens in ``owner``'s account.
-     */
-    function balanceOf(address owner) external view returns (uint256 balance);
-
-    /**
-     * @dev Returns the owner of the `tokenId` token.
-     *
-     * Requirements:
-     *
-     * - `tokenId` must exist.
-     */
-    function ownerOf(uint256 tokenId) external view returns (address owner);
-
-    /**
-     * @dev Safely transfers `tokenId` token from `from` to `to`.
-     *
-     * Requirements:
-     *
-     * - `from` cannot be the zero address.
-     * - `to` cannot be the zero address.
-     * - `tokenId` token must exist and be owned by `from`.
-     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
-     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
-     *
-     * Emits a {Transfer} event.
-     */
-    function safeTransferFrom(
-        address from,
-        address to,
-        uint256 tokenId,
-        bytes calldata data
-    ) external;
-
-    /**
-     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
-     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
-     *
-     * Requirements:
-     *
-     * - `from` cannot be the zero address.
-     * - `to` cannot be the zero address.
-     * - `tokenId` token must exist and be owned by `from`.
-     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
-     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
-     *
-     * Emits a {Transfer} event.
-     */
-    function safeTransferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) external;
-
-    /**
-     * @dev Transfers `tokenId` token from `from` to `to`.
-     *
-     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
-     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
-     * understand this adds an external call which potentially creates a reentrancy vulnerability.
-     *
-     * Requirements:
-     *
-     * - `from` cannot be the zero address.
-     * - `to` cannot be the zero address.
-     * - `tokenId` token must be owned by `from`.
-     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
-     *
-     * Emits a {Transfer} event.
-     */
-    function transferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) external;
-
-    /**
-     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
-     * The approval is cleared when the token is transferred.
-     *
-     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
-     *
-     * Requirements:
-     *
-     * - The caller must own the token or be an approved operator.
-     * - `tokenId` must exist.
-     *
-     * Emits an {Approval} event.
-     */
-    function approve(address to, uint256 tokenId) external;
-
-    /**
-     * @dev Approve or remove `operator` as an operator for the caller.
-     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
-     *
-     * Requirements:
-     *
-     * - The `operator` cannot be the caller.
-     *
-     * Emits an {ApprovalForAll} event.
-     */
-    function setApprovalForAll(address operator, bool _approved) external;
-
-    /**
-     * @dev Returns the account approved for `tokenId` token.
-     *
-     * Requirements:
-     *
-     * - `tokenId` must exist.
-     */
-    function getApproved(uint256 tokenId) external view returns (address operator);
-
-    /**
-     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
-     *
-     * See {setApprovalForAll}
-     */
-    function isApprovedForAll(address owner, address operator) external view returns (bool);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721Receiver.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @title ERC721 token receiver interface
- * @dev Interface for any contract that wants to support safeTransfers
- * from ERC721 asset contracts.
- */
-interface IERC721Receiver {
-    /**
-     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
-     * by `operator` from `from`, this function is called.
-     *
-     * It must return its Solidity selector to confirm the token transfer.
-     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
-     *
-     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
-     */
-    function onERC721Received(
-        address operator,
-        address from,
-        uint256 tokenId,
-        bytes calldata data
-    ) external returns (bytes4);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Burnable.sol)
-
-pragma solidity ^0.8.0;
-
-import "../ERC721.sol";
-import "../../../utils/Context.sol";
-
-/**
- * @title ERC721 Burnable Token
- * @dev ERC721 Token that can be burned (destroyed).
- */
-abstract contract ERC721Burnable is Context, ERC721 {
-    /**
-     * @dev Burns `tokenId`. See {ERC721-_burn}.
-     *
-     * Requirements:
-     *
-     * - The caller must own `tokenId` or be an approved operator.
-     */
-    function burn(uint256 tokenId) public virtual {
-        //solhint-disable-next-line max-line-length
-        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
-        _burn(tokenId);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol
+++ /dev/null
@@ -1,159 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/extensions/ERC721Enumerable.sol)
-
-pragma solidity ^0.8.0;
-
-import "../ERC721.sol";
-import "./IERC721Enumerable.sol";
-
-/**
- * @dev This implements an optional extension of {ERC721} defined in the EIP that adds
- * enumerability of all the token ids in the contract as well as all token ids owned by each
- * account.
- */
-abstract contract ERC721Enumerable is ERC721, IERC721Enumerable {
-    // Mapping from owner to list of owned token IDs
-    mapping(address => mapping(uint256 => uint256)) private _ownedTokens;
-
-    // Mapping from token ID to index of the owner tokens list
-    mapping(uint256 => uint256) private _ownedTokensIndex;
-
-    // Array with all token ids, used for enumeration
-    uint256[] private _allTokens;
-
-    // Mapping from token id to position in the allTokens array
-    mapping(uint256 => uint256) private _allTokensIndex;
-
-    /**
-     * @dev See {IERC165-supportsInterface}.
-     */
-    function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) {
-        return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId);
-    }
-
-    /**
-     * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
-     */
-    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
-        require(index < ERC721.balanceOf(owner), "ERC721Enumerable: owner index out of bounds");
-        return _ownedTokens[owner][index];
-    }
-
-    /**
-     * @dev See {IERC721Enumerable-totalSupply}.
-     */
-    function totalSupply() public view virtual override returns (uint256) {
-        return _allTokens.length;
-    }
-
-    /**
-     * @dev See {IERC721Enumerable-tokenByIndex}.
-     */
-    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
-        require(index < ERC721Enumerable.totalSupply(), "ERC721Enumerable: global index out of bounds");
-        return _allTokens[index];
-    }
-
-    /**
-     * @dev See {ERC721-_beforeTokenTransfer}.
-     */
-    function _beforeTokenTransfer(
-        address from,
-        address to,
-        uint256 firstTokenId,
-        uint256 batchSize
-    ) internal virtual override {
-        super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
-
-        if (batchSize > 1) {
-            // Will only trigger during construction. Batch transferring (minting) is not available afterwards.
-            revert("ERC721Enumerable: consecutive transfers not supported");
-        }
-
-        uint256 tokenId = firstTokenId;
-
-        if (from == address(0)) {
-            _addTokenToAllTokensEnumeration(tokenId);
-        } else if (from != to) {
-            _removeTokenFromOwnerEnumeration(from, tokenId);
-        }
-        if (to == address(0)) {
-            _removeTokenFromAllTokensEnumeration(tokenId);
-        } else if (to != from) {
-            _addTokenToOwnerEnumeration(to, tokenId);
-        }
-    }
-
-    /**
-     * @dev Private function to add a token to this extension's ownership-tracking data structures.
-     * @param to address representing the new owner of the given token ID
-     * @param tokenId uint256 ID of the token to be added to the tokens list of the given address
-     */
-    function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
-        uint256 length = ERC721.balanceOf(to);
-        _ownedTokens[to][length] = tokenId;
-        _ownedTokensIndex[tokenId] = length;
-    }
-
-    /**
-     * @dev Private function to add a token to this extension's token tracking data structures.
-     * @param tokenId uint256 ID of the token to be added to the tokens list
-     */
-    function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
-        _allTokensIndex[tokenId] = _allTokens.length;
-        _allTokens.push(tokenId);
-    }
-
-    /**
-     * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
-     * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for
-     * gas optimizations e.g. when performing a transfer operation (avoiding double writes).
-     * This has O(1) time complexity, but alters the order of the _ownedTokens array.
-     * @param from address representing the previous owner of the given token ID
-     * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
-     */
-    function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
-        // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
-        // then delete the last slot (swap and pop).
-
-        uint256 lastTokenIndex = ERC721.balanceOf(from) - 1;
-        uint256 tokenIndex = _ownedTokensIndex[tokenId];
-
-        // When the token to delete is the last token, the swap operation is unnecessary
-        if (tokenIndex != lastTokenIndex) {
-            uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
-
-            _ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
-            _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
-        }
-
-        // This also deletes the contents at the last position of the array
-        delete _ownedTokensIndex[tokenId];
-        delete _ownedTokens[from][lastTokenIndex];
-    }
-
-    /**
-     * @dev Private function to remove a token from this extension's token tracking data structures.
-     * This has O(1) time complexity, but alters the order of the _allTokens array.
-     * @param tokenId uint256 ID of the token to be removed from the tokens list
-     */
-    function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
-        // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
-        // then delete the last slot (swap and pop).
-
-        uint256 lastTokenIndex = _allTokens.length - 1;
-        uint256 tokenIndex = _allTokensIndex[tokenId];
-
-        // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
-        // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
-        // an 'if' statement (like in _removeTokenFromOwnerEnumeration)
-        uint256 lastTokenId = _allTokens[lastTokenIndex];
-
-        _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
-        _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
-
-        // This also deletes the contents at the last position of the array
-        delete _allTokensIndex[tokenId];
-        _allTokens.pop();
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/extensions/ERC721URIStorage.sol)
-
-pragma solidity ^0.8.0;
-
-import "../ERC721.sol";
-
-/**
- * @dev ERC721 token with storage based token URI management.
- */
-abstract contract ERC721URIStorage is ERC721 {
-    using Strings for uint256;
-
-    // Optional mapping for token URIs
-    mapping(uint256 => string) private _tokenURIs;
-
-    /**
-     * @dev See {IERC721Metadata-tokenURI}.
-     */
-    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
-        _requireMinted(tokenId);
-
-        string memory _tokenURI = _tokenURIs[tokenId];
-        string memory base = _baseURI();
-
-        // If there is no base URI, return the token URI.
-        if (bytes(base).length == 0) {
-            return _tokenURI;
-        }
-        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
-        if (bytes(_tokenURI).length > 0) {
-            return string(abi.encodePacked(base, _tokenURI));
-        }
-
-        return super.tokenURI(tokenId);
-    }
-
-    /**
-     * @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
-     *
-     * Requirements:
-     *
-     * - `tokenId` must exist.
-     */
-    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
-        require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token");
-        _tokenURIs[tokenId] = _tokenURI;
-    }
-
-    /**
-     * @dev See {ERC721-_burn}. This override additionally checks to see if a
-     * token-specific URI was set for the token, and if so, it deletes the token URI from
-     * the storage mapping.
-     */
-    function _burn(uint256 tokenId) internal virtual override {
-        super._burn(tokenId);
-
-        if (bytes(_tokenURIs[tokenId]).length != 0) {
-            delete _tokenURIs[tokenId];
-        }
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)
-
-pragma solidity ^0.8.0;
-
-import "../IERC721.sol";
-
-/**
- * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
- * @dev See https://eips.ethereum.org/EIPS/eip-721
- */
-interface IERC721Enumerable is IERC721 {
-    /**
-     * @dev Returns the total amount of tokens stored by the contract.
-     */
-    function totalSupply() external view returns (uint256);
-
-    /**
-     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
-     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
-     */
-    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
-
-    /**
-     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
-     * Use along with {totalSupply} to enumerate all tokens.
-     */
-    function tokenByIndex(uint256 index) external view returns (uint256);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
-
-pragma solidity ^0.8.0;
-
-import "../IERC721.sol";
-
-/**
- * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
- * @dev See https://eips.ethereum.org/EIPS/eip-721
- */
-interface IERC721Metadata is IERC721 {
-    /**
-     * @dev Returns the token collection name.
-     */
-    function name() external view returns (string memory);
-
-    /**
-     * @dev Returns the token collection symbol.
-     */
-    function symbol() external view returns (string memory);
-
-    /**
-     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
-     */
-    function tokenURI(uint256 tokenId) external view returns (string memory);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Address.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Address.sol
+++ /dev/null
@@ -1,244 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
-
-pragma solidity ^0.8.1;
-
-/**
- * @dev Collection of functions related to the address type
- */
-library Address {
-    /**
-     * @dev Returns true if `account` is a contract.
-     *
-     * [IMPORTANT]
-     * ====
-     * It is unsafe to assume that an address for which this function returns
-     * false is an externally-owned account (EOA) and not a contract.
-     *
-     * Among others, `isContract` will return false for the following
-     * types of addresses:
-     *
-     *  - an externally-owned account
-     *  - a contract in construction
-     *  - an address where a contract will be created
-     *  - an address where a contract lived, but was destroyed
-     * ====
-     *
-     * [IMPORTANT]
-     * ====
-     * You shouldn't rely on `isContract` to protect against flash loan attacks!
-     *
-     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
-     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
-     * constructor.
-     * ====
-     */
-    function isContract(address account) internal view returns (bool) {
-        // This method relies on extcodesize/address.code.length, which returns 0
-        // for contracts in construction, since the code is only stored at the end
-        // of the constructor execution.
-
-        return account.code.length > 0;
-    }
-
-    /**
-     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
-     * `recipient`, forwarding all available gas and reverting on errors.
-     *
-     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
-     * of certain opcodes, possibly making contracts go over the 2300 gas limit
-     * imposed by `transfer`, making them unable to receive funds via
-     * `transfer`. {sendValue} removes this limitation.
-     *
-     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
-     *
-     * IMPORTANT: because control is transferred to `recipient`, care must be
-     * taken to not create reentrancy vulnerabilities. Consider using
-     * {ReentrancyGuard} or the
-     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
-     */
-    function sendValue(address payable recipient, uint256 amount) internal {
-        require(address(this).balance >= amount, "Address: insufficient balance");
-
-        (bool success, ) = recipient.call{value: amount}("");
-        require(success, "Address: unable to send value, recipient may have reverted");
-    }
-
-    /**
-     * @dev Performs a Solidity function call using a low level `call`. A
-     * plain `call` is an unsafe replacement for a function call: use this
-     * function instead.
-     *
-     * If `target` reverts with a revert reason, it is bubbled up by this
-     * function (like regular Solidity function calls).
-     *
-     * Returns the raw returned data. To convert to the expected return value,
-     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
-     *
-     * Requirements:
-     *
-     * - `target` must be a contract.
-     * - calling `target` with `data` must not revert.
-     *
-     * _Available since v3.1._
-     */
-    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
-        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
-    }
-
-    /**
-     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
-     * `errorMessage` as a fallback revert reason when `target` reverts.
-     *
-     * _Available since v3.1._
-     */
-    function functionCall(
-        address target,
-        bytes memory data,
-        string memory errorMessage
-    ) internal returns (bytes memory) {
-        return functionCallWithValue(target, data, 0, errorMessage);
-    }
-
-    /**
-     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
-     * but also transferring `value` wei to `target`.
-     *
-     * Requirements:
-     *
-     * - the calling contract must have an ETH balance of at least `value`.
-     * - the called Solidity function must be `payable`.
-     *
-     * _Available since v3.1._
-     */
-    function functionCallWithValue(
-        address target,
-        bytes memory data,
-        uint256 value
-    ) internal returns (bytes memory) {
-        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
-    }
-
-    /**
-     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
-     * with `errorMessage` as a fallback revert reason when `target` reverts.
-     *
-     * _Available since v3.1._
-     */
-    function functionCallWithValue(
-        address target,
-        bytes memory data,
-        uint256 value,
-        string memory errorMessage
-    ) internal returns (bytes memory) {
-        require(address(this).balance >= value, "Address: insufficient balance for call");
-        (bool success, bytes memory returndata) = target.call{value: value}(data);
-        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
-    }
-
-    /**
-     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
-     * but performing a static call.
-     *
-     * _Available since v3.3._
-     */
-    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
-        return functionStaticCall(target, data, "Address: low-level static call failed");
-    }
-
-    /**
-     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
-     * but performing a static call.
-     *
-     * _Available since v3.3._
-     */
-    function functionStaticCall(
-        address target,
-        bytes memory data,
-        string memory errorMessage
-    ) internal view returns (bytes memory) {
-        (bool success, bytes memory returndata) = target.staticcall(data);
-        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
-    }
-
-    /**
-     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
-     * but performing a delegate call.
-     *
-     * _Available since v3.4._
-     */
-    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
-        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
-    }
-
-    /**
-     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
-     * but performing a delegate call.
-     *
-     * _Available since v3.4._
-     */
-    function functionDelegateCall(
-        address target,
-        bytes memory data,
-        string memory errorMessage
-    ) internal returns (bytes memory) {
-        (bool success, bytes memory returndata) = target.delegatecall(data);
-        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
-    }
-
-    /**
-     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
-     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
-     *
-     * _Available since v4.8._
-     */
-    function verifyCallResultFromTarget(
-        address target,
-        bool success,
-        bytes memory returndata,
-        string memory errorMessage
-    ) internal view returns (bytes memory) {
-        if (success) {
-            if (returndata.length == 0) {
-                // only check isContract if the call was successful and the return data is empty
-                // otherwise we already know that it was a contract
-                require(isContract(target), "Address: call to non-contract");
-            }
-            return returndata;
-        } else {
-            _revert(returndata, errorMessage);
-        }
-    }
-
-    /**
-     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
-     * revert reason or using the provided one.
-     *
-     * _Available since v4.3._
-     */
-    function verifyCallResult(
-        bool success,
-        bytes memory returndata,
-        string memory errorMessage
-    ) internal pure returns (bytes memory) {
-        if (success) {
-            return returndata;
-        } else {
-            _revert(returndata, errorMessage);
-        }
-    }
-
-    function _revert(bytes memory returndata, string memory errorMessage) private pure {
-        // Look for revert reason and bubble it up if present
-        if (returndata.length > 0) {
-            // The easiest way to bubble the revert reason is using memory via assembly
-            /// @solidity memory-safe-assembly
-            assembly {
-                let returndata_size := mload(returndata)
-                revert(add(32, returndata), returndata_size)
-            }
-        } else {
-            revert(errorMessage);
-        }
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Context.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Context.sol
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Provides information about the current execution context, including the
- * sender of the transaction and its data. While these are generally available
- * via msg.sender and msg.data, they should not be accessed in such a direct
- * manner, since when dealing with meta-transactions the account sending and
- * paying for execution may not be the actual sender (as far as an application
- * is concerned).
- *
- * This contract is only required for intermediate, library-like contracts.
- */
-abstract contract Context {
-    function _msgSender() internal view virtual returns (address) {
-        return msg.sender;
-    }
-
-    function _msgData() internal view virtual returns (bytes calldata) {
-        return msg.data;
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Counters.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Counters.sol
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @title Counters
- * @author Matt Condon (@shrugs)
- * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
- * of elements in a mapping, issuing ERC721 ids, or counting request ids.
- *
- * Include with `using Counters for Counters.Counter;`
- */
-library Counters {
-    struct Counter {
-        // This variable should never be directly accessed by users of the library: interactions must be restricted to
-        // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
-        // this feature: see https://github.com/ethereum/solidity/issues/4637
-        uint256 _value; // default: 0
-    }
-
-    function current(Counter storage counter) internal view returns (uint256) {
-        return counter._value;
-    }
-
-    function increment(Counter storage counter) internal {
-        unchecked {
-            counter._value += 1;
-        }
-    }
-
-    function decrement(Counter storage counter) internal {
-        uint256 value = counter._value;
-        require(value > 0, "Counter: decrement overflow");
-        unchecked {
-            counter._value = value - 1;
-        }
-    }
-
-    function reset(Counter storage counter) internal {
-        counter._value = 0;
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Strings.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/Strings.sol
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
-
-pragma solidity ^0.8.0;
-
-import "./math/Math.sol";
-
-/**
- * @dev String operations.
- */
-library Strings {
-    bytes16 private constant _SYMBOLS = "0123456789abcdef";
-    uint8 private constant _ADDRESS_LENGTH = 20;
-
-    /**
-     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
-     */
-    function toString(uint256 value) internal pure returns (string memory) {
-        unchecked {
-            uint256 length = Math.log10(value) + 1;
-            string memory buffer = new string(length);
-            uint256 ptr;
-            /// @solidity memory-safe-assembly
-            assembly {
-                ptr := add(buffer, add(32, length))
-            }
-            while (true) {
-                ptr--;
-                /// @solidity memory-safe-assembly
-                assembly {
-                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
-                }
-                value /= 10;
-                if (value == 0) break;
-            }
-            return buffer;
-        }
-    }
-
-    /**
-     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
-     */
-    function toHexString(uint256 value) internal pure returns (string memory) {
-        unchecked {
-            return toHexString(value, Math.log256(value) + 1);
-        }
-    }
-
-    /**
-     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
-     */
-    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
-        bytes memory buffer = new bytes(2 * length + 2);
-        buffer[0] = "0";
-        buffer[1] = "x";
-        for (uint256 i = 2 * length + 1; i > 1; --i) {
-            buffer[i] = _SYMBOLS[value & 0xf];
-            value >>= 4;
-        }
-        require(value == 0, "Strings: hex length insufficient");
-        return string(buffer);
-    }
-
-    /**
-     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
-     */
-    function toHexString(address addr) internal pure returns (string memory) {
-        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/ERC165.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/ERC165.sol
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
-
-pragma solidity ^0.8.0;
-
-import "./IERC165.sol";
-
-/**
- * @dev Implementation of the {IERC165} interface.
- *
- * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
- * for the additional interface id that will be supported. For example:
- *
- * ```solidity
- * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
- *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
- * }
- * ```
- *
- * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
- */
-abstract contract ERC165 is IERC165 {
-    /**
-     * @dev See {IERC165-supportsInterface}.
-     */
-    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
-        return interfaceId == type(IERC165).interfaceId;
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/IERC165.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/introspection/IERC165.sol
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Interface of the ERC165 standard, as defined in the
- * https://eips.ethereum.org/EIPS/eip-165[EIP].
- *
- * Implementers can declare support of contract interfaces, which can then be
- * queried by others ({ERC165Checker}).
- *
- * For an implementation, see {ERC165}.
- */
-interface IERC165 {
-    /**
-     * @dev Returns true if this contract implements the interface defined by
-     * `interfaceId`. See the corresponding
-     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
-     * to learn more about how these ids are created.
-     *
-     * This function call must use less than 30 000 gas.
-     */
-    function supportsInterface(bytes4 interfaceId) external view returns (bool);
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/math/Math.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/@openzeppelin/contracts/utils/math/Math.sol
+++ /dev/null
@@ -1,345 +0,0 @@
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
-
-pragma solidity ^0.8.0;
-
-/**
- * @dev Standard math utilities missing in the Solidity language.
- */
-library Math {
-    enum Rounding {
-        Down, // Toward negative infinity
-        Up, // Toward infinity
-        Zero // Toward zero
-    }
-
-    /**
-     * @dev Returns the largest of two numbers.
-     */
-    function max(uint256 a, uint256 b) internal pure returns (uint256) {
-        return a > b ? a : b;
-    }
-
-    /**
-     * @dev Returns the smallest of two numbers.
-     */
-    function min(uint256 a, uint256 b) internal pure returns (uint256) {
-        return a < b ? a : b;
-    }
-
-    /**
-     * @dev Returns the average of two numbers. The result is rounded towards
-     * zero.
-     */
-    function average(uint256 a, uint256 b) internal pure returns (uint256) {
-        // (a + b) / 2 can overflow.
-        return (a & b) + (a ^ b) / 2;
-    }
-
-    /**
-     * @dev Returns the ceiling of the division of two numbers.
-     *
-     * This differs from standard division with `/` in that it rounds up instead
-     * of rounding down.
-     */
-    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
-        // (a + b - 1) / b can overflow on addition, so we distribute.
-        return a == 0 ? 0 : (a - 1) / b + 1;
-    }
-
-    /**
-     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
-     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
-     * with further edits by Uniswap Labs also under MIT license.
-     */
-    function mulDiv(
-        uint256 x,
-        uint256 y,
-        uint256 denominator
-    ) internal pure returns (uint256 result) {
-        unchecked {
-            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
-            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
-            // variables such that product = prod1 * 2^256 + prod0.
-            uint256 prod0; // Least significant 256 bits of the product
-            uint256 prod1; // Most significant 256 bits of the product
-            assembly {
-                let mm := mulmod(x, y, not(0))
-                prod0 := mul(x, y)
-                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
-            }
-
-            // Handle non-overflow cases, 256 by 256 division.
-            if (prod1 == 0) {
-                return prod0 / denominator;
-            }
-
-            // Make sure the result is less than 2^256. Also prevents denominator == 0.
-            require(denominator > prod1);
-
-            ///////////////////////////////////////////////
-            // 512 by 256 division.
-            ///////////////////////////////////////////////
-
-            // Make division exact by subtracting the remainder from [prod1 prod0].
-            uint256 remainder;
-            assembly {
-                // Compute remainder using mulmod.
-                remainder := mulmod(x, y, denominator)
-
-                // Subtract 256 bit number from 512 bit number.
-                prod1 := sub(prod1, gt(remainder, prod0))
-                prod0 := sub(prod0, remainder)
-            }
-
-            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
-            // See https://cs.stackexchange.com/q/138556/92363.
-
-            // Does not overflow because the denominator cannot be zero at this stage in the function.
-            uint256 twos = denominator & (~denominator + 1);
-            assembly {
-                // Divide denominator by twos.
-                denominator := div(denominator, twos)
-
-                // Divide [prod1 prod0] by twos.
-                prod0 := div(prod0, twos)
-
-                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
-                twos := add(div(sub(0, twos), twos), 1)
-            }
-
-            // Shift in bits from prod1 into prod0.
-            prod0 |= prod1 * twos;
-
-            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
-            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
-            // four bits. That is, denominator * inv = 1 mod 2^4.
-            uint256 inverse = (3 * denominator) ^ 2;
-
-            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
-            // in modular arithmetic, doubling the correct bits in each step.
-            inverse *= 2 - denominator * inverse; // inverse mod 2^8
-            inverse *= 2 - denominator * inverse; // inverse mod 2^16
-            inverse *= 2 - denominator * inverse; // inverse mod 2^32
-            inverse *= 2 - denominator * inverse; // inverse mod 2^64
-            inverse *= 2 - denominator * inverse; // inverse mod 2^128
-            inverse *= 2 - denominator * inverse; // inverse mod 2^256
-
-            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
-            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
-            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
-            // is no longer required.
-            result = prod0 * inverse;
-            return result;
-        }
-    }
-
-    /**
-     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
-     */
-    function mulDiv(
-        uint256 x,
-        uint256 y,
-        uint256 denominator,
-        Rounding rounding
-    ) internal pure returns (uint256) {
-        uint256 result = mulDiv(x, y, denominator);
-        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
-            result += 1;
-        }
-        return result;
-    }
-
-    /**
-     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
-     *
-     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
-     */
-    function sqrt(uint256 a) internal pure returns (uint256) {
-        if (a == 0) {
-            return 0;
-        }
-
-        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
-        //
-        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
-        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
-        //
-        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
-        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
-        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
-        //
-        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
-        uint256 result = 1 << (log2(a) >> 1);
-
-        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
-        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
-        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
-        // into the expected uint128 result.
-        unchecked {
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            result = (result + a / result) >> 1;
-            return min(result, a / result);
-        }
-    }
-
-    /**
-     * @notice Calculates sqrt(a), following the selected rounding direction.
-     */
-    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = sqrt(a);
-            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
-        }
-    }
-
-    /**
-     * @dev Return the log in base 2, rounded down, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log2(uint256 value) internal pure returns (uint256) {
-        uint256 result = 0;
-        unchecked {
-            if (value >> 128 > 0) {
-                value >>= 128;
-                result += 128;
-            }
-            if (value >> 64 > 0) {
-                value >>= 64;
-                result += 64;
-            }
-            if (value >> 32 > 0) {
-                value >>= 32;
-                result += 32;
-            }
-            if (value >> 16 > 0) {
-                value >>= 16;
-                result += 16;
-            }
-            if (value >> 8 > 0) {
-                value >>= 8;
-                result += 8;
-            }
-            if (value >> 4 > 0) {
-                value >>= 4;
-                result += 4;
-            }
-            if (value >> 2 > 0) {
-                value >>= 2;
-                result += 2;
-            }
-            if (value >> 1 > 0) {
-                result += 1;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = log2(value);
-            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
-        }
-    }
-
-    /**
-     * @dev Return the log in base 10, rounded down, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log10(uint256 value) internal pure returns (uint256) {
-        uint256 result = 0;
-        unchecked {
-            if (value >= 10**64) {
-                value /= 10**64;
-                result += 64;
-            }
-            if (value >= 10**32) {
-                value /= 10**32;
-                result += 32;
-            }
-            if (value >= 10**16) {
-                value /= 10**16;
-                result += 16;
-            }
-            if (value >= 10**8) {
-                value /= 10**8;
-                result += 8;
-            }
-            if (value >= 10**4) {
-                value /= 10**4;
-                result += 4;
-            }
-            if (value >= 10**2) {
-                value /= 10**2;
-                result += 2;
-            }
-            if (value >= 10**1) {
-                result += 1;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = log10(value);
-            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
-        }
-    }
-
-    /**
-     * @dev Return the log in base 256, rounded down, of a positive value.
-     * Returns 0 if given 0.
-     *
-     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
-     */
-    function log256(uint256 value) internal pure returns (uint256) {
-        uint256 result = 0;
-        unchecked {
-            if (value >> 128 > 0) {
-                value >>= 128;
-                result += 16;
-            }
-            if (value >> 64 > 0) {
-                value >>= 64;
-                result += 8;
-            }
-            if (value >> 32 > 0) {
-                value >>= 32;
-                result += 4;
-            }
-            if (value >> 16 > 0) {
-                value >>= 16;
-                result += 2;
-            }
-            if (value >> 8 > 0) {
-                result += 1;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
-     * Returns 0 if given 0.
-     */
-    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
-        unchecked {
-            uint256 result = log256(value);
-            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
-        }
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/ZeppelinContract.soldiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/ZeppelinContract.sol
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.9;
-
-import "./@openzeppelin/contracts/token/ERC721/ERC721.sol";
-import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
-import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
-import "./@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
-import "./@openzeppelin/contracts/access/Ownable.sol";
-import "./@openzeppelin/contracts/utils/Counters.sol";
-
-contract ZeppelinContract is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Burnable, Ownable {
-    using Counters for Counters.Counter;
-
-    Counters.Counter private _tokenIdCounter;
-
-    constructor() ERC721("ZeppelinContract", "UNQ") {}
-
-    function _baseURI() internal pure override returns (string memory) {
-        return "test";
-    }
-
-    function safeMint(address to, string memory uri) public onlyOwner {
-        uint256 tokenId = _tokenIdCounter.current();
-        _tokenIdCounter.increment();
-        _safeMint(to, tokenId);
-        _setTokenURI(tokenId, uri);
-    }
-
-    // The following functions are overrides required by Solidity.
-
-    function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
-        internal
-        override(ERC721, ERC721Enumerable)
-    {
-        super._beforeTokenTransfer(from, to, tokenId, batchSize);
-    }
-
-    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
-        super._burn(tokenId);
-    }
-
-    function tokenURI(uint256 tokenId)
-        public
-        view
-        override(ERC721, ERC721URIStorage)
-        returns (string memory)
-    {
-        return super.tokenURI(tokenId);
-    }
-
-    function supportsInterface(bytes4 interfaceId)
-        public
-        view
-        override(ERC721, ERC721Enumerable)
-        returns (bool)
-    {
-        return super.supportsInterface(interfaceId);
-    }
-}
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.abidiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.abi
+++ /dev/null
@@ -1 +0,0 @@
-[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"string","name":"uri","type":"string"}],"name":"safeMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
deletedjs-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.bindiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/openZeppelin/ERC721/bin/ZeppelinContract.bin
+++ /dev/null
@@ -1 +0,0 @@
-60806040523480156200001157600080fd5b506040518060400160405280601081526020016f16995c1c195b1a5b90dbdb9d1c9858dd60821b81525060405180604001604052806003815260200162554e5160e81b815250816000908162000068919062000195565b50600162000077828262000195565b505050620000946200008e6200009a60201b60201c565b6200009e565b62000261565b3390565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200011b57607f821691505b6020821081036200013c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200019057600081815260208120601f850160051c810160208610156200016b5750805b601f850160051c820191505b818110156200018c5782815560010162000177565b5050505b505050565b81516001600160401b03811115620001b157620001b1620000f0565b620001c981620001c2845462000106565b8462000142565b602080601f831160018114620002015760008415620001e85750858301515b600019600386901b1c1916600185901b1785556200018c565b600085815260208120601f198616915b82811015620002325788860151825594840194600190910190840162000211565b5085821015620002515787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b611f0d80620002716000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80636352211e116100b8578063a22cb4651161007c578063a22cb46514610271578063b88d4fde14610284578063c87b56dd14610297578063d204c45e146102aa578063e985e9c5146102bd578063f2fde38b146102f957600080fd5b80636352211e1461022a57806370a082311461023d578063715018a6146102505780638da5cb5b1461025857806395d89b411461026957600080fd5b806323b872dd116100ff57806323b872dd146101cb5780632f745c59146101de57806342842e0e146101f157806342966c68146102045780634f6ccce71461021757600080fd5b806301ffc9a71461013c57806306fdde0314610164578063081812fc14610179578063095ea7b3146101a457806318160ddd146101b9575b600080fd5b61014f61014a3660046118ab565b61030c565b60405190151581526020015b60405180910390f35b61016c61031d565b60405161015b9190611918565b61018c61018736600461192b565b6103af565b6040516001600160a01b03909116815260200161015b565b6101b76101b2366004611960565b6103d6565b005b6008545b60405190815260200161015b565b6101b76101d936600461198a565b6104f0565b6101bd6101ec366004611960565b610522565b6101b76101ff36600461198a565b6105b8565b6101b761021236600461192b565b6105d3565b6101bd61022536600461192b565b610604565b61018c61023836600461192b565b610697565b6101bd61024b3660046119c6565b6106f7565b6101b761077d565b600b546001600160a01b031661018c565b61016c610791565b6101b761027f3660046119e1565b6107a0565b6101b7610292366004611aa9565b6107af565b61016c6102a536600461192b565b6107e7565b6101b76102b8366004611b25565b6107f2565b61014f6102cb366004611b87565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b6101b76103073660046119c6565b610829565b60006103178261089f565b92915050565b60606000805461032c90611bba565b80601f016020809104026020016040519081016040528092919081815260200182805461035890611bba565b80156103a55780601f1061037a576101008083540402835291602001916103a5565b820191906000526020600020905b81548152906001019060200180831161038857829003601f168201915b5050505050905090565b60006103ba826108c4565b506000908152600460205260409020546001600160a01b031690565b60006103e182610697565b9050806001600160a01b0316836001600160a01b0316036104535760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b60648201526084015b60405180910390fd5b336001600160a01b038216148061046f575061046f81336102cb565b6104e15760405162461bcd60e51b815260206004820152603d60248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f7420746f60448201527f6b656e206f776e6572206f7220617070726f76656420666f7220616c6c000000606482015260840161044a565b6104eb8383610923565b505050565b6104fb335b82610991565b6105175760405162461bcd60e51b815260040161044a90611bf4565b6104eb838383610a10565b600061052d836106f7565b821061058f5760405162461bcd60e51b815260206004820152602b60248201527f455243373231456e756d657261626c653a206f776e657220696e646578206f7560448201526a74206f6620626f756e647360a81b606482015260840161044a565b506001600160a01b03919091166000908152600660209081526040808320938352929052205490565b6104eb838383604051806020016040528060008152506107af565b6105dc336104f5565b6105f85760405162461bcd60e51b815260040161044a90611bf4565b61060181610b81565b50565b600061060f60085490565b82106106725760405162461bcd60e51b815260206004820152602c60248201527f455243373231456e756d657261626c653a20676c6f62616c20696e646578206f60448201526b7574206f6620626f756e647360a01b606482015260840161044a565b6008828154811061068557610685611c41565b90600052602060002001549050919050565b6000818152600260205260408120546001600160a01b0316806103175760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b60006001600160a01b0382166107615760405162461bcd60e51b815260206004820152602960248201527f4552433732313a2061646472657373207a65726f206973206e6f7420612076616044820152683634b21037bbb732b960b91b606482015260840161044a565b506001600160a01b031660009081526003602052604090205490565b610785610b8a565b61078f6000610be4565b565b60606001805461032c90611bba565b6107ab338383610c36565b5050565b6107b93383610991565b6107d55760405162461bcd60e51b815260040161044a90611bf4565b6107e184848484610d04565b50505050565b606061031782610d37565b6107fa610b8a565b6000610805600c5490565b9050610815600c80546001019055565b61081f8382610e4b565b6104eb8183610e65565b610831610b8a565b6001600160a01b0381166108965760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161044a565b61060181610be4565b60006001600160e01b0319821663780e9d6360e01b1480610317575061031782610ef8565b6000818152600260205260409020546001600160a01b03166106015760405162461bcd60e51b8152602060048201526018602482015277115490cdcc8c4e881a5b9d985b1a59081d1bdad95b88125160421b604482015260640161044a565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061095882610697565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b60008061099d83610697565b9050806001600160a01b0316846001600160a01b031614806109e457506001600160a01b0380821660009081526005602090815260408083209388168352929052205460ff165b80610a085750836001600160a01b03166109fd846103af565b6001600160a01b0316145b949350505050565b826001600160a01b0316610a2382610697565b6001600160a01b031614610a495760405162461bcd60e51b815260040161044a90611c57565b6001600160a01b038216610aab5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b606482015260840161044a565b610ab88383836001610f48565b826001600160a01b0316610acb82610697565b6001600160a01b031614610af15760405162461bcd60e51b815260040161044a90611c57565b600081815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0387811680865260038552838620805460001901905590871680865283862080546001019055868652600290945282852080549092168417909155905184937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61060181610f54565b600b546001600160a01b0316331461078f5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161044a565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b031603610c975760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c657200000000000000604482015260640161044a565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b610d0f848484610a10565b610d1b84848484610f94565b6107e15760405162461bcd60e51b815260040161044a90611c9c565b6060610d42826108c4565b6000828152600a602052604081208054610d5b90611bba565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8790611bba565b8015610dd45780601f10610da957610100808354040283529160200191610dd4565b820191906000526020600020905b815481529060010190602001808311610db757829003601f168201915b505050505090506000610dfe6040805180820190915260048152631d195cdd60e21b602082015290565b90508051600003610e10575092915050565b815115610e42578082604051602001610e2a929190611cee565b60405160208183030381529060405292505050919050565b610a0884611095565b6107ab828260405180602001604052806000815250611115565b6000828152600260205260409020546001600160a01b0316610ee05760405162461bcd60e51b815260206004820152602e60248201527f45524337323155524953746f726167653a2055524920736574206f66206e6f6e60448201526d32bc34b9ba32b73a103a37b5b2b760911b606482015260840161044a565b6000828152600a602052604090206104eb8282611d6b565b60006001600160e01b031982166380ac58cd60e01b1480610f2957506001600160e01b03198216635b5e139f60e01b145b8061031757506301ffc9a760e01b6001600160e01b0319831614610317565b6107e184848484611148565b610f5d81611288565b6000818152600a602052604090208054610f7690611bba565b159050610601576000818152600a6020526040812061060191611847565b60006001600160a01b0384163b1561108a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290610fd8903390899088908890600401611e2b565b6020604051808303816000875af1925050508015611013575060408051601f3d908101601f1916820190925261101091810190611e68565b60015b611070573d808015611041576040519150601f19603f3d011682016040523d82523d6000602084013e611046565b606091505b5080516000036110685760405162461bcd60e51b815260040161044a90611c9c565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050610a08565b506001949350505050565b60606110a0826108c4565b60006110c36040805180820190915260048152631d195cdd60e21b602082015290565b905060008151116110e3576040518060200160405280600081525061110e565b806110ed8461132b565b6040516020016110fe929190611cee565b6040516020818303038152906040525b9392505050565b61111f83836113be565b61112c6000848484610f94565b6104eb5760405162461bcd60e51b815260040161044a90611c9c565b61115484848484611557565b60018111156111c35760405162461bcd60e51b815260206004820152603560248201527f455243373231456e756d657261626c653a20636f6e7365637574697665207472604482015274185b9cd9995c9cc81b9bdd081cdd5c1c1bdc9d1959605a1b606482015260840161044a565b816001600160a01b03851661121f5761121a81600880546000838152600960205260408120829055600182018355919091527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee30155565b611242565b836001600160a01b0316856001600160a01b0316146112425761124285826115df565b6001600160a01b03841661125e576112598161167c565b611281565b846001600160a01b0316846001600160a01b03161461128157611281848261172b565b5050505050565b600061129382610697565b90506112a3816000846001610f48565b6112ac82610697565b600083815260046020908152604080832080546001600160a01b03199081169091556001600160a01b0385168085526003845282852080546000190190558785526002909352818420805490911690555192935084927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908390a45050565b606060006113388361176f565b600101905060008167ffffffffffffffff81111561135857611358611a1d565b6040519080825280601f01601f191660200182016040528015611382576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461138c57509392505050565b6001600160a01b0382166114145760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604482015260640161044a565b6000818152600260205260409020546001600160a01b0316156114795760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b611487600083836001610f48565b6000818152600260205260409020546001600160a01b0316156114ec5760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e74656400000000604482015260640161044a565b6001600160a01b038216600081815260036020908152604080832080546001019055848352600290915280822080546001600160a01b0319168417905551839291907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60018111156107e1576001600160a01b0384161561159d576001600160a01b03841660009081526003602052604081208054839290611597908490611e9b565b90915550505b6001600160a01b038316156107e1576001600160a01b038316600090815260036020526040812080548392906115d4908490611eae565b909155505050505050565b600060016115ec846106f7565b6115f69190611e9b565b600083815260076020526040902054909150808214611649576001600160a01b03841660009081526006602090815260408083208584528252808320548484528184208190558352600790915290208190555b5060009182526007602090815260408084208490556001600160a01b039094168352600681528383209183525290812055565b60085460009061168e90600190611e9b565b600083815260096020526040812054600880549394509092849081106116b6576116b6611c41565b9060005260206000200154905080600883815481106116d7576116d7611c41565b600091825260208083209091019290925582815260099091526040808220849055858252812055600880548061170f5761170f611ec1565b6001900381819060005260206000200160009055905550505050565b6000611736836106f7565b6001600160a01b039093166000908152600660209081526040808320868452825280832085905593825260079052919091209190915550565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106117ae5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106117da576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106117f857662386f26fc10000830492506010015b6305f5e1008310611810576305f5e100830492506008015b612710831061182457612710830492506004015b60648310611836576064830492506002015b600a83106103175760010192915050565b50805461185390611bba565b6000825580601f10611863575050565b601f01602090049060005260206000209081019061060191905b80821115611891576000815560010161187d565b5090565b6001600160e01b03198116811461060157600080fd5b6000602082840312156118bd57600080fd5b813561110e81611895565b60005b838110156118e35781810151838201526020016118cb565b50506000910152565b600081518084526119048160208601602086016118c8565b601f01601f19169290920160200192915050565b60208152600061110e60208301846118ec565b60006020828403121561193d57600080fd5b5035919050565b80356001600160a01b038116811461195b57600080fd5b919050565b6000806040838503121561197357600080fd5b61197c83611944565b946020939093013593505050565b60008060006060848603121561199f57600080fd5b6119a884611944565b92506119b660208501611944565b9150604084013590509250925092565b6000602082840312156119d857600080fd5b61110e82611944565b600080604083850312156119f457600080fd5b6119fd83611944565b915060208301358015158114611a1257600080fd5b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff80841115611a4e57611a4e611a1d565b604051601f8501601f19908116603f01168101908282118183101715611a7657611a76611a1d565b81604052809350858152868686011115611a8f57600080fd5b858560208301376000602087830101525050509392505050565b60008060008060808587031215611abf57600080fd5b611ac885611944565b9350611ad660208601611944565b925060408501359150606085013567ffffffffffffffff811115611af957600080fd5b8501601f81018713611b0a57600080fd5b611b1987823560208401611a33565b91505092959194509250565b60008060408385031215611b3857600080fd5b611b4183611944565b9150602083013567ffffffffffffffff811115611b5d57600080fd5b8301601f81018513611b6e57600080fd5b611b7d85823560208401611a33565b9150509250929050565b60008060408385031215611b9a57600080fd5b611ba383611944565b9150611bb160208401611944565b90509250929050565b600181811c90821680611bce57607f821691505b602082108103611bee57634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252602d908201527f4552433732313a2063616c6c6572206973206e6f7420746f6b656e206f776e6560408201526c1c881bdc88185c1c1c9bdd9959609a1b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b60208082526025908201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060408201526437bbb732b960d91b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60008351611d008184602088016118c8565b835190830190611d148183602088016118c8565b01949350505050565b601f8211156104eb57600081815260208120601f850160051c81016020861015611d445750805b601f850160051c820191505b81811015611d6357828155600101611d50565b505050505050565b815167ffffffffffffffff811115611d8557611d85611a1d565b611d9981611d938454611bba565b84611d1d565b602080601f831160018114611dce5760008415611db65750858301515b600019600386901b1c1916600185901b178555611d63565b600085815260208120601f198616915b82811015611dfd57888601518255948401946001909101908401611dde565b5085821015611e1b5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611e5e908301846118ec565b9695505050505050565b600060208284031215611e7a57600080fd5b815161110e81611895565b634e487b7160e01b600052601160045260246000fd5b8181038181111561031757610317611e85565b8082018082111561031757610317611e85565b634e487b7160e01b600052603160045260246000fdfea26469706673582212207960bc65d7484ab1502ccdc352c91f0fb91535a525cf99e1b6392e242548279a64736f6c63430008110033
\ No newline at end of file
deletedjs-packages/scripts/src/benchmarks/utils/types.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/benchmarks/utils/types.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-export interface IFeeGas {
-  fee?: number | bigint,
-  gas?: number | bigint,
-  substrate?: number,
-  zeppelin?: {
-    fee: number | bigint,
-    gas: number | bigint,
-  }
-
-}
-export interface IFeeGasCsv extends IFeeGasVm {
-  function: string,
-}
-export interface IFeeGasVm{
-    ethFee?: number | bigint,
-    ethGas?: number | bigint,
-    substrate?: number,
-    zeppelinFee?: number | bigint,
-    zeppelinGas?: number | bigint
-}
-export interface IFunctionFee {
-  [name: string]: IFeeGas
-}
-
-
-export abstract class FunctionFeeVM {
-  [name: string]: IFeeGasVm
-
-  static toCsv(model: FunctionFeeVM): IFeeGasCsv[]{
-    const res: IFeeGasCsv[] = [];
-    Object.keys(model).forEach(key => {
-      res.push({
-        function: key,
-        ethFee: model[key].ethFee,
-        ethGas: model[key].ethGas,
-        substrate: model[key].substrate,
-        zeppelinFee: model[key].zeppelinFee,
-        zeppelinGas: model[key].zeppelinGas,
-      });
-    });
-    return res;
-  }
-  static fromModel(model: IFunctionFee): FunctionFeeVM {
-    const res: FunctionFeeVM = {};
-
-    Object.keys(model).forEach(key => {
-      res[key] = {};
-      if(model[key].fee && model[key].gas) {
-        res[key].ethFee = model[key].fee;
-        res[key].ethGas = model[key].gas;
-      }
-      if(model[key].substrate)
-        res[key].substrate = model[key].substrate;
-      if(model[key].zeppelin) {
-        res[key].zeppelinFee = model[key].zeppelin?.fee;
-        res[key].zeppelinGas = model[key].zeppelin?.gas;
-      }
-    });
-
-    return res;
-  }
-}
\ No newline at end of file
deletedjs-packages/scripts/src/calibrate.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/calibrate.ts
+++ /dev/null
@@ -1,318 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingEthPlaygrounds} from '@unique/tests/src/eth/util/index.js';
-import {EthUniqueHelper} from '@unique/tests/src//eth/util/playgrounds/unique.dev.js';
-
-class Fract {
-  static ZERO = new Fract(0n);
-  constructor(public readonly a: bigint, public readonly b: bigint = 1n) {
-    if(b === 0n) throw new Error('division by zero');
-    if(b < 0n) throw new Error('missing normalization');
-  }
-
-  mul(other: Fract) {
-    return new Fract(this.a * other.a, this.b * other.b).optimize();
-  }
-
-  div(other: Fract) {
-    return this.mul(other.inv());
-  }
-
-  plus(other: Fract) {
-    if(this.b === other.b) {
-      return new Fract(this.a + other.a, this.b);
-    }
-    return new Fract(this.a * other.b + other.a * this.b, this.b * other.b).optimize();
-  }
-
-  minus(other: Fract) {
-    return this.plus(other.neg());
-  }
-
-  neg() {
-    return new Fract(-this.a, this.b);
-  }
-  inv() {
-    if(this.a < 0) {
-      return new Fract(-this.b, -this.a);
-    } else {
-      return new Fract(this.b, this.a);
-    }
-  }
-
-  optimize() {
-    function gcd(x: bigint, y: bigint) {
-      if(x < 0n)
-        x = -x;
-      if(y < 0n)
-        y = -y;
-      while(y) {
-        const t = y;
-        y = x % y;
-        x = t;
-      }
-      return x;
-    }
-    const v = gcd(this.a, this.b);
-    return new Fract(this.a / v, this.b / v);
-  }
-
-  toBigInt() {
-    return this.a / this.b;
-  }
-  toNumber() {
-    const v = this.optimize();
-    return Number(v.a) / Number(v.b);
-  }
-  toString() {
-    const v = this.optimize();
-    return `${v.a} / ${v.b}`;
-  }
-
-  lt(other: Fract) {
-    return this.a * other.b < other.a * this.b;
-  }
-  eq(other: Fract) {
-    return this.a * other.b === other.a * this.b;
-  }
-
-  sqrt() {
-    if(this.a < 0n) {
-      throw new Error('square root of negative numbers is not supported');
-    }
-
-    if(this.lt(new Fract(2n))) {
-      return this;
-    }
-
-    function newtonIteration(n: Fract, x0: Fract): Fract {
-      const x1 = rpn(n, x0, '/', x0, '+', new Fract(2n), '/');
-      if(x0.eq(x1) || x0.eq(x1.minus(new Fract(1n)))) {
-        return x0;
-      }
-      return newtonIteration(n, x1);
-    }
-
-    return newtonIteration(this, new Fract(1n));
-  }
-}
-
-type Op = Fract | '+' | '-' | '*' | '/' | 'dup' | Op[];
-function rpn(...ops: (Op)[]) {
-  const stack: Fract[] = [];
-  for(const op of ops) {
-    if(op instanceof Fract) {
-      stack.push(op);
-    } else if(op === '+') {
-      if(stack.length < 2)
-        throw new Error('stack underflow');
-      const b = stack.pop()!;
-      const a = stack.pop()!;
-      stack.push(a.plus(b));
-    } else if(op === '*') {
-      if(stack.length < 2)
-        throw new Error('stack underflow');
-      const b = stack.pop()!;
-      const a = stack.pop()!;
-      stack.push(a.mul(b));
-    } else if(op === '-') {
-      if(stack.length < 2)
-        throw new Error('stack underflow');
-      const b = stack.pop()!;
-      const a = stack.pop()!;
-      stack.push(a.minus(b));
-    } else if(op === '/') {
-      if(stack.length < 2)
-        throw new Error('stack underflow');
-      const b = stack.pop()!;
-      const a = stack.pop()!;
-      stack.push(a.div(b));
-    } else if(op === 'dup') {
-      if(stack.length < 1)
-        throw new Error('stack underflow');
-      const a = stack.pop()!;
-      stack.push(a);
-      stack.push(a);
-    } else if(Array.isArray(op)) {
-      stack.push(rpn(...op));
-    } else {
-      throw new Error(`unknown operand: ${op}`);
-    }
-  }
-  if(stack.length != 1)
-    throw new Error('one element should be left on stack');
-  return stack[0]!;
-}
-
-function linearRegression(points: { x: Fract, y: Fract }[]) {
-  let sumxy = Fract.ZERO;
-  let sumx = Fract.ZERO;
-  let sumy = Fract.ZERO;
-  let sumx2 = Fract.ZERO;
-  const n = points.length;
-  for(let i = 0; i < n; i++) {
-    const p = points[i];
-    sumxy = rpn(p.x, p.y, '*', sumxy, '+');
-    sumx = sumx.plus(p.x);
-    sumy = sumy.plus(p.y);
-    sumx2 = rpn(p.x, p.x, '*', sumx2, '+');
-  }
-
-  const nb = new Fract(BigInt(n));
-
-  const a = rpn(
-    [nb, sumxy, '*', sumx, sumy, '*', '-'],
-    [nb, sumx2, '*', sumx, sumx, '*', '-'],
-    '/',
-  );
-  const b = rpn(
-    [sumy, a, sumx, '*', '-'],
-    nb,
-    '/',
-  );
-
-  return {a, b};
-}
-
-const hypothesisLinear = (a: Fract, b: Fract) => (x: Fract) => rpn(x, a, '*', b, '+');
-
-// function error(points: { x: Fract, y: Fract }[], hypothesis: (a: Fract) => Fract) {
-//   return points.map(p => {
-//     const v = hypothesis(p.x);
-//     const vv = p.y;
-
-//     return rpn(v, vv, '-', 'dup', '*');
-//   }).reduce((a, b) => a.plus(b), Fract.ZERO).sqrt().div(new Fract(BigInt(points.length)));
-// }
-
-async function calibrateWeightToFee(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {
-  const alice = await privateKey('//Alice');
-  const bob = await privateKey('//Bob');
-  const dataPoints = [];
-
-  {
-    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-    await token.transfer(alice, {Substrate: bob.address});
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-
-    console.log(`\t[NFT transfer] Original price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
-  }
-
-  const api = helper.getApi();
-  const base = (await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt();
-  for(let i = -5; i < 5; i++) {
-    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(base + base / 1000n * BigInt(i))));
-
-    const coefficient = new Fract((await api.query.configuration.weightToFeeCoefficientOverride() as any).toBigInt());
-    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-    await token.transfer(alice, {Substrate: bob.address});
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-
-    const transferPrice = new Fract(aliceBalanceBefore - aliceBalanceAfter);
-
-    dataPoints.push({x: transferPrice, y: coefficient});
-  }
-  const {a, b} = linearRegression(dataPoints);
-
-  const hyp = hypothesisLinear(a, b);
-  // console.log(`\t[NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`);
-
-  // 0.1 UNQ
-  const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(1n, 10n), '*'));
-  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setWeightToFeeCoefficientOverride(perfectValue.toBigInt())));
-
-  {
-    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-    await token.transfer(alice, {Substrate: bob.address});
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-
-    console.log(`\t[NFT transfer] Calibrated price: ${Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal())} UNQ`);
-  }
-}
-
-async function calibrateMinGasPrice(helper: EthUniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>) {
-  const alice = await privateKey('//Alice');
-  const caller = await helper.eth.createAccountWithBalance(alice);
-  const receiver = helper.eth.createAccount();
-  const dataPoints = [];
-
-  {
-    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
-    const token = await collection.mintToken(alice, {Ethereum: caller});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
-
-    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));
-
-    console.log(`\t[ETH NFT transfer] Original price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
-  }
-
-  const api = helper.getApi();
-  // const defaultCoeff = (api.consts.configuration.defaultMinGasPrice as any).toBigInt();
-  const base = (await api.query.configuration.minGasPriceOverride() as any).toBigInt();
-  for(let i = -8; i < 8; i++) {
-    const gasPrice = base + base / 100000n * BigInt(i);
-    const gasPriceStr = '0x' + gasPrice.toString(16);
-    await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(gasPrice)));
-
-    const coefficient = new Fract((await api.query.configuration.minGasPriceOverride() as any).toBigInt());
-    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
-    const token = await collection.mintToken(alice, {Ethereum: caller});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
-
-    const transferPrice = new Fract(await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gasPrice: gasPriceStr, gas: helper.eth.DEFAULT_GAS})));
-
-    dataPoints.push({x: transferPrice, y: coefficient});
-  }
-
-  const {a, b} = linearRegression(dataPoints);
-
-  const hyp = hypothesisLinear(a, b);
-  // console.log(`\t[ETH NFT transfer] Error: ${_error(dataPoints, hyp).toNumber()}`);
-
-  // 0.15 UNQ
-  const perfectValue = hyp(rpn(new Fract(helper.balance.getOneTokenNominal()), new Fract(15n, 100n), '*'));
-  await helper.signTransaction(alice, api.tx.sudo.sudo(api.tx.configuration.setMinGasPriceOverride(perfectValue.toBigInt())));
-
-  {
-    const collection = await helper.nft.mintCollection(alice, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
-    const token = await collection.mintToken(alice, {Ethereum: caller});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
-
-    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, token.tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS}));
-
-    console.log(`\t[ETH NFT transfer] Calibrated price: ${Number(cost) / Number(helper.balance.getOneTokenNominal())} UNQ`);
-  }
-}
-
-// eslint-disable-next-line @typescript-eslint/no-floating-promises
-(async () => {
-  await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {
-    // Subsequent runs reduce error, as price line is not actually straight, this is a curve
-
-    const iterations = 3;
-
-    console.log('[Calibrate WeightToFee]');
-    for(let i = 0; i < iterations; i++) {
-      await calibrateWeightToFee(helper, privateKey);
-    }
-
-    console.log();
-
-    console.log('[Calibrate MinGasPrice]');
-    for(let i = 0; i < iterations; i++) {
-      await calibrateMinGasPrice(helper, privateKey);
-    }
-  });
-})();
deletedjs-packages/scripts/src/calibrateApply.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/calibrateApply.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import {readFile, writeFile} from 'fs/promises';
-import path from 'path';
-import {makeNames, usingPlaygrounds} from '@unique/tests/src/util/index.js';
-
-const {dirname} = makeNames(import.meta.url);
-
-const formatNumber = (num: string): string => num.split('').reverse().join('').replace(/([0-9]{3})/g, '$1_').split('').reverse().join('').replace(/^_/, '');
-
-(async () => {
-  let weightToFeeCoefficientOverride: string;
-  let minGasPriceOverride: string;
-  await usingPlaygrounds(async (helpers, _privateKey) => {
-    weightToFeeCoefficientOverride = (await helpers.getApi().query.configuration.weightToFeeCoefficientOverride() as any).toBigInt().toString();
-    minGasPriceOverride = (await helpers.getApi().query.configuration.minGasPriceOverride() as any).toBigInt().toString();
-  });
-  const constantsFile = path.resolve(dirname, '../../primitives/common/src/constants.rs');
-  let constants = (await readFile(constantsFile)).toString();
-
-  let weight2feeFound = false;
-  constants = constants.replace(/(\/\*<weight2fee>\*\/)[0-9_]+(\/\*<\/weight2fee>\*\/)/, (_f, p, s) => {
-    weight2feeFound = true;
-    return p+formatNumber(weightToFeeCoefficientOverride)+s;
-  });
-  if(!weight2feeFound) {
-    throw new Error('failed to find weight2fee marker in source code');
-  }
-
-  let minGasPriceFound = false;
-  constants = constants.replace(/(\/\*<mingasprice>\*\/)[0-9_]+(\/\*<\/mingasprice>\*\/)/, (_f, p, s) => {
-    minGasPriceFound = true;
-    return p+formatNumber(minGasPriceOverride)+s;
-  });
-  if(!minGasPriceFound) {
-    throw new Error('failed to find mingasprice marker in source code');
-  }
-
-  await writeFile(constantsFile, constants);
-})().catch(e => {
-  console.log(e.stack);
-});
deletedjs-packages/scripts/src/fetchMetadata.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/fetchMetadata.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import {writeFile} from 'fs/promises';
-import {join} from 'path';
-import {exit} from 'process';
-import {fileURLToPath} from 'url';
-
-const url = process.env.RPC_URL;
-if(!url) throw new Error('RPC_URL is not set');
-
-const srcDir = fileURLToPath(new URL('.', import.meta.url));
-
-for(let i = 0; i < 10; i++) {
-  try {
-    console.log(`Trying to fetch metadata, retry ${i + 1}/${10}`);
-    const response = await fetch(url, {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-      },
-      redirect: 'follow',
-      body: JSON.stringify({
-        jsonrpc: '2.0',
-        id: '1',
-        method: 'state_getMetadata',
-        params: [],
-      }),
-    });
-    const json = await response.json();
-    const output = join(srcDir, 'metadata.json');
-    console.log(`Received response, saving to ${output}`);
-    await writeFile(output, JSON.stringify(json));
-    exit(0);
-  } catch (e) {
-    console.error('Failed to request metadata:');
-    console.error(e);
-    console.error('Waiting 1 minute');
-    await new Promise(res => setTimeout(res, 60 * 1000));
-  }
-}
-console.error('Out of retries');
-exit(1);
deletedjs-packages/scripts/src/generateEnv.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/generateEnv.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import {ApiPromise, WsProvider} from '@polkadot/api';
-import {readFile} from 'fs/promises';
-import {join} from 'path';
-import {makeNames} from '@unique/tests/src/util/index.js';
-
-const {dirname} = makeNames(import.meta.url);
-
-async function fetchVersion(chain: string): Promise<string> {
-  const api = await ApiPromise.create({provider: new WsProvider(chain)});
-  const last = (await api.query.system.lastRuntimeUpgrade()).toJSON();
-  await api.disconnect();
-  return (last as any).specVersion.toString();
-}
-
-function setVar(env: string, key: string, value: string): string {
-  let found = false;
-  const newEnv = env.replace(new RegExp(`${key}=.+?\n`), () => {
-    found = true;
-    return `${key}=${value}\n`;
-  });
-  if(!found) throw new Error(`env key "${key}" is not found`);
-  return newEnv;
-}
-
-// Fetch and format version string
-async function ff(url: string, regex: RegExp, rep: string | ((substring: string, ...params:any[]) => string)): Promise<string> {
-  const ver = await fetchVersion(url);
-  if(ver.match(regex) === null)
-    throw new Error(`bad regex for ${url}`);
-  return ver.replace(regex, rep as any);
-}
-function fixupUnique(version: string): string {
-  if(version === 'release-v930033')
-    return 'release-v930033-fix-gas-price';
-  if(version === 'release-v930034')
-    return 'release-v930034-fix-gas-price';
-  return version;
-}
-
-(async () => {
-  let env = (await readFile(join(dirname, '../../.env'))).toString();
-  await Promise.all([
-    ff('wss://rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'POLKADOT_MAINNET_BRANCH', v)),
-    ff('wss://statemint-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINT_BUILD_BRANCH', v)),
-    ff('wss://acala-rpc-0.aca-api.network/', /^(.)(..)(.)$/, '$1.$2.$3').then(v => env = setVar(env, 'ACALA_BUILD_BRANCH', v)),
-    ff('wss://wss.api.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONBEAM_BUILD_BRANCH', v)),
-    ff('wss://ws.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'UNIQUE_MAINNET_BRANCH', fixupUnique(v))),
-
-    ff('wss://kusama-rpc.polkadot.io/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'KUSAMA_MAINNET_BRANCH', v)),
-    ff('wss://statemine-rpc.polkadot.io/', /^(....)$/, 'release-parachains-v$1').then(v => env = setVar(env, 'STATEMINE_BUILD_BRANCH', v)),
-    ff('wss://karura-rpc-0.aca-api.network/', /^(.)(..)(.)$/, 'release-karura-$1.$2.$3').then(v => env = setVar(env, 'KARURA_BUILD_BRANCH', v)),
-    ff('wss://wss.api.moonriver.moonbeam.network/', /^(....)$/, 'runtime-$1').then(v => env = setVar(env, 'MOONRIVER_BUILD_BRANCH', v)),
-    ff('wss://ws-quartz.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'QUARTZ_MAINNET_BRANCH', fixupUnique(v))),
-
-    ff('wss://ws-westend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEWEST_MAINNET_BRANCH', v)),
-    ff('wss://westmint-rpc.polkadot.io/', /^(....)$/, 'parachains-v$1').then(v => env = setVar(env, 'WESTMINT_BUILD_BRANCH', v)),
-    ff('wss://ws-opal.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'OPAL_MAINNET_BRANCH', fixupUnique(v))),
-
-    ff('wss://ws-eastend.unique.network/', /^(.)(..)(.)$/, 'release-v0.$1.$2').then(v => env = setVar(env, 'UNIQUEEAST_MAINNET_BRANCH', v)),
-    ff('wss://ws-sapphire.unique.network/', /^(......)$/, 'release-v$1').then(v => env = setVar(env, 'SAPPHIRE_MAINNET_BRANCH', fixupUnique(v))),
-
-    ff('wss://rpc.astar.network/', /^(.+)$/, (_, r) => {
-      switch (r) {
-        case '55': return 'v5.3.0';
-        case '57': return 'v5.4.0';
-        case '61': return 'v5.11.0';
-        case '66': return 'v5.18.0';
-        default: throw new Error('unknown astar branch for runtime ' + r);
-      }
-    }).then(v => env = setVar(env, 'ASTAR_BUILD_BRANCH', v)),
-    ff('wss://shiden.api.onfinality.io/public-ws', /^(.+)$/, (_, r) => {
-      switch (r) {
-        case '90': return 'v4.49.0';
-        case '96': return 'v5.4.0';
-        case '100': return 'v5.10.0';
-        case '104': return 'v5.15.0';
-        case '106': return 'v5.18.0';
-        default: throw new Error('unknown shiden branch for runtime ' + r);
-      }
-    }).then(v => env = setVar(env, 'SHIDEN_BUILD_BRANCH', v)),
-  ]);
-  console.log(env);
-})().catch(e => {
-  console.error('Fatal');
-  console.error(e.stack);
-  process.exit(1);
-});
deletedjs-packages/scripts/src/generate_types/functions.shdiffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/functions.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-
-function do_rpc {
-	curl -s --header "Content-Type: application/json" -XPOST --data "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"$1\",\"params\":[$2]}" $RPC_URL
-}
deletedjs-packages/scripts/src/generate_types/generate_types_package.shdiffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/generate_types_package.sh
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/env bash
-
-set -eu
-
-DIR=$(realpath $(dirname "$0"))
-TEMPLATE=$DIR/types_template
-GIT_REPO=git@github.com:UniqueNetwork/unique-types-js.git
-
-. $DIR/functions.sh
-
-usage() {
-	echo "Usage: [RPC_URL=http://localhost:9944] $0 <--rc|--release> [--force] [--push] [--rpc-url=http://localhost:9944]" 1>&2
-	exit 1
-}
-
-rc=
-release=
-force=
-push=
-
-for i in "$@"; do
-case $i in
-	--rc)
-		rc=1
-		if test "$release"; then usage; fi
-		;;
-	--release)
-		release=1
-		if test "$rc"; then usage; fi
-		;;
-	--force)
-		force=1
-		;;
-	--push)
-		push=1
-		;;
-	--rpc-url=*)
-		RPC_URL=${i#*=}
-		;;
-	*)
-		usage
-		;;
-esac
-done
-
-if test \( ! \( "$rc" -o "$release" \) \) -o \( "${RPC_URL=}" = "" \); then
-	usage
-elif test "$rc"; then
-	echo "Rc build"
-else
-	echo "Release build"
-fi
-
-cd $DIR/..
-yarn polkadot-types
-
-version=$(do_rpc state_getRuntimeVersion "")
-spec_version=$(echo $version | jq -r .result.specVersion)
-spec_name=$(echo $version | jq -r .result.specName)
-echo "Spec version: $spec_version, name: $spec_name"
-
-case $spec_name in
-	opal)
-		package_name=@unique-nft/opal-testnet-types
-		repo_branch=opal-testnet
-		repo_tag=$repo_branch
-		;;
-	sapphire)
-		package_name=@unique-nft/sapphire-mainnet-types
-		repo_branch=sapphire-mainnet
-		repo_tag=$repo_branch
-		;;
-	quartz)
-		package_name=@unique-nft/quartz-mainnet-types
-		repo_branch=quartz-mainnet
-		repo_tag=$repo_branch
-		;;
-	unique)
-		package_name=@unique-nft/unique-mainnet-types
-		repo_branch=master
-		repo_tag=unique-mainnet
-		;;
-	*)
-		echo "unknown spec name: $spec_name"
-		exit 1
-		;;
-esac
-
-if test "$rc" = 1; then
-	if "$spec_name" != opal; then
-		echo "rc types can only be based on opal spec"
-		exit 1
-	fi
-	package_name=@unique-nft/rc-types
-	repo_branch=rc
-	repo_tag=$repo_branch
-fi
-
-package_version=${spec_version:0:4}.$(echo ${spec_version:4:4} | sed 's/^0*//').
-last_patch=NEVER
-for tag in $(git ls-remote -t --refs $GIT_REPO | cut -f 2 | sort -r); do
-	tag_prefix=refs/tags/$repo_tag-v$package_version
-	if [[ $tag == $tag_prefix* ]]; then
-		last_patch=${tag#$tag_prefix}
-		break;
-	fi
-done
-echo "Package version: ${package_version}X, name: $package_name"
-echo "Last published: $package_version$last_patch"
-
-if test "$last_patch" = "NEVER"; then
-	new_package_version=${package_version}0
-else
-	new_package_version=${package_version}$((last_patch+1))
-fi
-package_version=${package_version}$last_patch
-echo "New package version: $new_package_version"
-
-pjsapi_ver=^$(cat $DIR/../package.json | jq -r '.dependencies."@polkadot/api"' | sed -e "s/^\^//")
-tsnode_ver=^$(cat $DIR/../package.json | jq -r '.devDependencies."ts-node"' | sed -e "s/^\^//")
-ts_ver=^$(cat $DIR/../package.json | jq -r '.devDependencies."typescript"' | sed -e "s/^\^//")
-
-gen=$(mktemp -d)
-pushd $gen
-git clone $GIT_REPO -b $repo_branch --depth 1 .
-if test "$last_patch" != "NEVER"; then
-	git reset --hard $repo_tag-v$package_version
-fi
-git rm -r "*"
-popd
-
-# Using old package_version here, becaue we first check if
-# there is any difference between generated and already uplaoded types
-cat $TEMPLATE/package.json \
-| jq '.private = false' - \
-| jq '.name = "'$package_name'"' - \
-| jq '.version = "'$package_version'"' - \
-| jq '.peerDependencies."@polkadot/api" = "'$pjsapi_ver'"' - \
-| jq '.peerDependencies."@polkadot/types" = "'$pjsapi_ver'"' - \
-| jq '.devDependencies."@polkadot/api" = "'$pjsapi_ver'"' - \
-| jq '.devDependencies."@polkadot/types" = "'$pjsapi_ver'"' - \
-| jq '.devDependencies."ts-node" = "'$tsnode_ver'"' - \
-| jq '.devDependencies."typescript" = "'$ts_ver'"' - \
-> $gen/package.json
-for file in .gitignore .npmignore README.md tsconfig.json; do
-	cp $TEMPLATE/$file $gen/
-done
-package_name_replacement=$(printf '%s\n' "$package_name" | sed -e 's/[\/&]/\\&/g')
-sed -i 's/PKGNAME/'$package_name_replacement'/' $gen/README.md
-
-rsync -ar --exclude .gitignore src/interfaces/ $gen
-for file in $gen/augment-* $gen/**/types.ts $gen/registry.ts; do
-	sed -i '1s;^;//@ts-nocheck\n;' $file
-done
-
-pushd $gen
-git add .
-popd
-
-pushd $gen
-if git diff --quiet HEAD && test ! "$force"; then
-	echo "no changes detected"
-	exit 0
-fi
-popd
-
-mv $gen/package.json $gen/package.old.json
-cat $gen/package.old.json \
-| jq '.version = "'$new_package_version'"' - \
-> $gen/package.json
-rm $gen/package.old.json
-pushd $gen
-git add package.json
-popd
-
-echo "package.json contents:"
-cat $gen/package.json
-echo "overall diff:"
-pushd $gen
-git status
-git diff HEAD || true
-popd
-
-# This check is only active if running in interactive terminal
-if [ -t 0 ]; then
-	read -p "Is everything ok at $gen [y/n]? " -n 1 -r
-	echo
-	if [[ ! $REPLY =~ ^[Yy]$ ]]; then
-		echo "Aborting!"
-		exit 1
-	fi
-fi
-
-pushd $gen
-yarn
-yarn prepublish
-git commit -m "chore: upgrade types to v$new_package_version"
-git tag --force $repo_tag-v$new_package_version
-if test "$push" = 1; then
-	git push --tags --force -u origin HEAD
-else
-	echo "--push not given, origin repo left intact"
-	echo "To publish manually, go to $gen, and run \"git push --tags --force -u origin HEAD\""
-fi
-popd
deletedjs-packages/scripts/src/generate_types/readyness.jsdiffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/readyness.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import {ApiPromise, WsProvider} from '@polkadot/api';
-
-const connect = async () => {
-  const wsEndpoint = 'ws://127.0.0.1:9944';
-  const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});
-  await api.isReadyOrError;
-
-  const head = (await api.rpc.chain.getHeader()).number.toNumber();
-  await api.disconnect();
-  if(head < 1) throw Error('No block #1');
-
-};
-
-const sleep = time => new Promise(resolve => {
-  setTimeout(() => resolve(), time);
-});
-
-const main = async () => {
-  // eslint-disable-next-line no-constant-condition
-  while(true) {
-    try {
-      await connect();
-      break;
-    }
-    catch (e) {
-      await sleep(10000);
-      console.log(e);
-    }
-  }
-};
-
-main().then(() => process.exit(0)).catch(e => {
-  console.error(e);
-  process.exit(1);
-});
deletedjs-packages/scripts/src/generate_types/types_template/.gitignorediffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/types_template/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-*.js
-*.map
-*.d.ts
-/node_modules
-metadata.json
deletedjs-packages/scripts/src/generate_types/types_template/.npmignorediffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/types_template/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-/src
deletedjs-packages/scripts/src/generate_types/types_template/README.mddiffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/types_template/README.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# PKGNAME
-
-Unique network api types
-
-Do not edit by hand, those types are generated automatically, and definitions are located in chain repo
-
-## Using types
-
-Install library:
-
-```bash
-yarn add --dev PKGNAME
-```
-
-Replace polkadot.js types with our chain types adding corresponding path override to the tsconfig `compilerOptions.paths` section:
-
-```json
-// in tsconfig.json
-{
-  "compilerOptions": {
-    "paths": {
-      "@polkadot/types/lookup": ["node_modules/PKGNAME/types-lookup"]
-    }
-  }
-}
-```
-
-Since polkadot v7 api augmentations not loaded by default, in every file, where you need to access `api.tx`, `api.query`, `api.rpc`, etc; you should explicitly import corresponding augmentation before any other `polkadot.js` related import:
-```
-import 'PKGNAME/augment-api';
-```
deletedjs-packages/scripts/src/generate_types/types_template/package.jsondiffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/types_template/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "name": "TODO",
-  "private": true,
-  "version": "TODO",
-  "main": "index.js",
-  "repository": "git@github.com:UniqueNetwork/unique-types-js.git",
-  "homepage": "https://unique.network/",
-  "license": "MIT",
-  "scripts": {
-    "prepublish": "tsc -d"
-  },
-  "peerDependencies": {
-    "@polkadot/api": "TODO",
-    "@polkadot/types": "TODO"
-  },
-  "devDependencies": {
-    "@polkadot/api": "TODO",
-    "@polkadot/types": "TODO",
-    "ts-node": "TODO",
-    "typescript": "TODO"
-  }
-}
deletedjs-packages/scripts/src/generate_types/types_template/tsconfig.jsondiffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/types_template/tsconfig.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-	"exclude": [
-		"node_modules",
-		"node_modules/**/*",
-		"../node_modules/**/*",
-		"**/node_modules/**/*"
-	],
-	"compilerOptions": {
-		"target": "ES2020",
-		"moduleResolution": "node",
-		"esModuleInterop": true,
-		"resolveJsonModule": true,
-		"module": "commonjs",
-		"sourceMap": true,
-		"outDir": ".",
-		"rootDir": ".",
-		"strict": true,
-		"paths": {
-		},
-		"skipLibCheck": true,
-	},
-	"include": [
-		"**/*",
-	],
-	"lib": [
-		"es2017"
-	],
-}
deletedjs-packages/scripts/src/generate_types/wait_for_first_block.shdiffbeforeafterboth
--- a/js-packages/scripts/src/generate_types/wait_for_first_block.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-
-DIR=$(dirname "$0")
-
-. $DIR/functions.sh
-
-last_block_id=0
-block_id=0
-function get_block {
-	block_id_hex=$(do_rpc chain_getHeader | jq -r .result.number)
-	block_id=$((${block_id_hex}))
-	echo Id = $block_id
-}
-
-function had_new_block {
-	last_block_id=$block_id
-	get_block
-	if (( last_block_id != 0 && block_id > last_block_id )); then
-		return 0
-	fi
-	return 1
-}
-
-function reset_check {
-	last_block_id=0
-	block_id=0
-}
-
-while ! had_new_block; do
-	echo "Waiting for next block..."
-	sleep 12
-done
-reset_check
-
-echo "Chain is running, but lets wait for another block after a minute, to avoid startup flakiness."
-sleep 60
-
-while ! had_new_block; do
-	echo "Waiting for another block..."
-	sleep 12
-done
-
-echo "Chain is running!"
deletedjs-packages/scripts/src/metadata.jsondiffbeforeafterboth
--- a/js-packages/scripts/src/metadata.json
+++ /dev/null
@@ -1 +0,0 @@
-{"jsonrpc":"2.0","result":"0x6d6574610eed0a000c1c73705f636f72651863727970746f2c4163636f756e7449643332000004000401205b75383b2033325d0000040000032000000008000800000503000c08306672616d655f73797374656d2c4163636f756e74496e666f08144e6f6e636501102c4163636f756e74446174610114001401146e6f6e63651001144e6f6e6365000124636f6e73756d657273100120526566436f756e7400012470726f766964657273100120526566436f756e7400012c73756666696369656e7473100120526566436f756e740001106461746114012c4163636f756e74446174610000100000050500140c3c70616c6c65745f62616c616e6365731474797065732c4163636f756e7444617461041c42616c616e63650118001001106672656518011c42616c616e6365000120726573657276656418011c42616c616e636500011866726f7a656e18011c42616c616e6365000114666c6167731c01284578747261466c61677300001800000507001c0c3c70616c6c65745f62616c616e636573147479706573284578747261466c61677300000400180110753132380000200c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540124000c01186e6f726d616c2401045400012c6f7065726174696f6e616c240104540001246d616e6461746f7279240104540000240c2873705f77656967687473247765696768745f76321857656967687400000801207265665f74696d6528010c75363400012870726f6f665f73697a6528010c7536340000280000062c002c000005060030083c7072696d69746976655f74797065731048323536000004000401205b75383b2033325d000034000002080038102873705f72756e74696d651c67656e65726963186469676573741844696765737400000401106c6f67733c013c5665633c4469676573744974656d3e00003c000002400040102873705f72756e74696d651c67656e6572696318646967657374284469676573744974656d0001142850726552756e74696d650800440144436f6e73656e737573456e67696e654964000034011c5665633c75383e00060024436f6e73656e7375730800440144436f6e73656e737573456e67696e654964000034011c5665633c75383e000400105365616c0800440144436f6e73656e737573456e67696e654964000034011c5665633c75383e000500144f74686572040034011c5665633c75383e0000006452756e74696d65456e7669726f6e6d656e74557064617465640008000044000003040000000800480000024c004c08306672616d655f73797374656d2c4576656e745265636f7264080445015004540130000c011470686173652106011450686173650001146576656e7450010445000118746f706963735d0501185665633c543e00005008306f70616c5f72756e74696d653052756e74696d654576656e740001981853797374656d04005401706672616d655f73797374656d3a3a4576656e743c52756e74696d653e000000485374617465547269654d6967726174696f6e04007801ac70616c6c65745f73746174655f747269655f6d6967726174696f6e3a3a4576656e743c52756e74696d653e0001003c50617261636861696e53797374656d04008401bc63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d3a3a4576656e743c52756e74696d653e00140044436f6c6c61746f7253656c656374696f6e04008c01a470616c6c65745f636f6c6c61746f725f73656c656374696f6e3a3a4576656e743c52756e74696d653e0017001c53657373696f6e040090015470616c6c65745f73657373696f6e3a3a4576656e740018002042616c616e636573040094017c70616c6c65745f62616c616e6365733a3a4576656e743c52756e74696d653e001e00485472616e73616374696f6e5061796d656e7404009c01a870616c6c65745f7472616e73616374696f6e5f7061796d656e743a3a4576656e743c52756e74696d653e0021002054726561737572790400a0017c70616c6c65745f74726561737572793a3a4576656e743c52756e74696d653e002200105375646f0400a4016c70616c6c65745f7375646f3a3a4576656e743c52756e74696d653e0023001c56657374696e670400b401706f726d6c5f76657374696e673a3a4576656e743c52756e74696d653e0025001c58546f6b656e730400c001706f726d6c5f78746f6b656e733a3a4576656e743c52756e74696d653e00260018546f6b656e7304000901016c6f726d6c5f746f6b656e733a3a4576656e743c52756e74696d653e002700204964656e7469747904001501017c70616c6c65745f6964656e746974793a3a4576656e743c52756e74696d653e00280020507265696d61676504001901017c70616c6c65745f707265696d6167653a3a4576656e743c52756e74696d653e0029002444656d6f637261637904001d01018070616c6c65745f64656d6f63726163793a3a4576656e743c52756e74696d653e002a001c436f756e63696c0400310101fc70616c6c65745f636f6c6c6563746976653a3a4576656e743c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365313e002b0048546563686e6963616c436f6d6d69747465650400390101fc70616c6c65745f636f6c6c6563746976653a3a4576656e743c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365323e002c0044436f756e63696c4d656d6265727368697004003d0101fc70616c6c65745f6d656d626572736869703a3a4576656e743c52756e74696d652c2070616c6c65745f6d656d626572736869703a3a496e7374616e6365313e002d0070546563686e6963616c436f6d6d69747465654d656d626572736869700400410101fc70616c6c65745f6d656d626572736869703a3a4576656e743c52756e74696d652c2070616c6c65745f6d656d626572736869703a3a496e7374616e6365323e002e005046656c6c6f7773686970436f6c6c6563746976650400450101a070616c6c65745f72616e6b65645f636f6c6c6563746976653a3a4576656e743c52756e74696d653e002f004c46656c6c6f77736869705265666572656e646104005501018070616c6c65745f7265666572656e64613a3a4576656e743c52756e74696d653e003000245363686564756c65720400b905018070616c6c65745f7363686564756c65723a3a4576656e743c52756e74696d653e0031002458636d7051756575650400bd0501a463756d756c75735f70616c6c65745f78636d705f71756575653a3a4576656e743c52756e74696d653e0032002c506f6c6b61646f7458636d0400c105016870616c6c65745f78636d3a3a4576656e743c52756e74696d653e0033002843756d756c757358636d0400c905018863756d756c75735f70616c6c65745f78636d3a3a4576656e743c52756e74696d653e00340020446d7051756575650400cd0501a063756d756c75735f70616c6c65745f646d705f71756575653a3a4576656e743c52756e74696d653e00350034436f6e66696775726174696f6e0400d105019070616c6c65745f636f6e66696775726174696f6e3a3a4576656e743c52756e74696d653e003f0018436f6d6d6f6e0400d505017470616c6c65745f636f6d6d6f6e3a3a4576656e743c52756e74696d653e004200245374727563747572650400d905018070616c6c65745f7374727563747572653a3a4576656e743c52756e74696d653e0046003041707050726f6d6f74696f6e0400dd05019070616c6c65745f6170705f70726f6d6f74696f6e3a3a4576656e743c52756e74696d653e00490034466f726569676e4173736574730400e105019470616c6c65745f666f726569676e5f6173736574733a3a4576656e743c52756e74696d653e0050000c45564d0400e505016870616c6c65745f65766d3a3a4576656e743c52756e74696d653e00640020457468657265756d0400e905015870616c6c65745f657468657265756d3a3a4576656e740065004845766d436f6e747261637448656c7065727304000d0601ac70616c6c65745f65766d5f636f6e74726163745f68656c706572733a3a4576656e743c52756e74696d653e0097003045766d4d6967726174696f6e04001106019070616c6c65745f65766d5f6d6967726174696f6e3a3a4576656e743c52756e74696d653e0099002c4d61696e74656e616e636504001506018870616c6c65745f6d61696e74656e616e63653a3a4576656e743c52756e74696d653e009a001c5574696c69747904001906015470616c6c65745f7574696c6974793a3a4576656e74009c0024546573745574696c7304001d06018470616c6c65745f746573745f7574696c733a3a4576656e743c52756e74696d653e00ff0000540c306672616d655f73797374656d1870616c6c6574144576656e740404540001184045787472696e7369635375636365737304013464697370617463685f696e666f5801304469737061746368496e666f00000490416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656408013864697370617463685f6572726f7264013444697370617463684572726f7200013464697370617463685f696e666f5801304469737061746368496e666f00010450416e2065787472696e736963206661696c65642e2c436f64655570646174656400020450603a636f6465602077617320757064617465642e284e65774163636f756e7404011c6163636f756e74000130543a3a4163636f756e7449640003046841206e6577206163636f756e742077617320637265617465642e344b696c6c65644163636f756e7404011c6163636f756e74000130543a3a4163636f756e74496400040458416e206163636f756e7420776173207265617065642e2052656d61726b656408011873656e646572000130543a3a4163636f756e7449640001106861736830011c543a3a48617368000504704f6e206f6e2d636861696e2072656d61726b2068617070656e65642e04704576656e7420666f72207468652053797374656d2070616c6c65742e580c346672616d655f737570706f7274206469737061746368304469737061746368496e666f00000c0118776569676874240118576569676874000114636c6173735c01344469737061746368436c617373000120706179735f6665656001105061797300005c0c346672616d655f737570706f7274206469737061746368344469737061746368436c61737300010c184e6f726d616c0000002c4f7065726174696f6e616c000100244d616e6461746f727900020000600c346672616d655f737570706f727420646973706174636810506179730001080c596573000000084e6f0001000064082873705f72756e74696d653444697370617463684572726f72000138144f746865720000003043616e6e6f744c6f6f6b7570000100244261644f726967696e000200184d6f64756c65040068012c4d6f64756c654572726f7200030044436f6e73756d657252656d61696e696e670004002c4e6f50726f76696465727300050040546f6f4d616e79436f6e73756d65727300060014546f6b656e04006c0128546f6b656e4572726f720007002841726974686d65746963040070013c41726974686d657469634572726f72000800345472616e73616374696f6e616c04007401485472616e73616374696f6e616c4572726f7200090024457868617573746564000a0028436f7272757074696f6e000b002c556e617661696c61626c65000c0038526f6f744e6f74416c6c6f776564000d000068082873705f72756e74696d652c4d6f64756c654572726f720000080114696e64657808010875380001146572726f7244018c5b75383b204d41585f4d4f44554c455f4552524f525f454e434f4445445f53495a455d00006c082873705f72756e74696d6528546f6b656e4572726f720001284046756e6473556e617661696c61626c65000000304f6e6c7950726f76696465720001003042656c6f774d696e696d756d0002003043616e6e6f7443726561746500030030556e6b6e6f776e41737365740004001846726f7a656e0005002c556e737570706f727465640006004043616e6e6f74437265617465486f6c64000700344e6f74457870656e6461626c650008001c426c6f636b65640009000070083473705f61726974686d657469633c41726974686d657469634572726f7200010c24556e646572666c6f77000000204f766572666c6f77000100384469766973696f6e42795a65726f0002000074082873705f72756e74696d65485472616e73616374696f6e616c4572726f72000108304c696d6974526561636865640000001c4e6f4c6179657200010000780c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574144576656e74040454000110204d696772617465640c010c746f7010010c7533320001146368696c6410010c75333200011c636f6d707574657c01404d6967726174696f6e436f6d707574650000083901476976656e206e756d626572206f66206028746f702c206368696c642960206b6579732077657265206d6967726174656420726573706563746976656c792c20776974682074686520676976656e2860636f6d70757465602e1c536c617368656408010c77686f000130543a3a4163636f756e744964000118616d6f756e7418013042616c616e63654f663c543e000104b4536f6d65206163636f756e7420676f7420736c61736865642062792074686520676976656e20616d6f756e742e544175746f4d6967726174696f6e46696e697368656400020484546865206175746f206d6967726174696f6e207461736b2066696e69736865642e1848616c7465640401146572726f728001204572726f723c543e000304ec4d6967726174696f6e20676f742068616c7465642064756520746f20616e206572726f72206f72206d6973732d636f6e66696775726174696f6e2e0470496e6e6572206576656e7473206f6620746869732070616c6c65742e7c0c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574404d6967726174696f6e436f6d70757465000108185369676e6564000000104175746f00010000800c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574144572726f720404540001183c4d61785369676e65644c696d697473000004804d6178207369676e6564206c696d697473206e6f74207265737065637465642e284b6579546f6f4c6f6e6700011cb441206b657920776173206c6f6e676572207468616e2074686520636f6e66696775726564206d6178696d756d2e00110154686973206d65616e73207468617420746865206d6967726174696f6e2068616c746564206174207468652063757272656e74205b6050726f6772657373605d20616e64010163616e20626520726573756d656420776974682061206c6172676572205b6063726174653a3a436f6e6669673a3a4d61784b65794c656e605d2076616c75652e21015265747279696e672077697468207468652073616d65205b6063726174653a3a436f6e6669673a3a4d61784b65794c656e605d2076616c75652077696c6c206e6f7420776f726b2e45015468652076616c75652073686f756c64206f6e6c7920626520696e6372656173656420746f2061766f696420612073746f72616765206d6967726174696f6e20666f72207468652063757272656e746c799073746f726564205b6063726174653a3a50726f67726573733a3a4c6173744b6579605d2e384e6f74456e6f75676846756e6473000204947375626d697474657220646f6573206e6f74206861766520656e6f7567682066756e64732e284261645769746e65737300030468426164207769746e65737320646174612070726f76696465642e645369676e65644d6967726174696f6e4e6f74416c6c6f77656400040425015369676e6564206d6967726174696f6e206973206e6f7420616c6c6f776564206265636175736520746865206d6178696d756d206c696d6974206973206e6f7420736574207965742e304261644368696c64526f6f7400050460426164206368696c6420726f6f742070726f76696465642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e840c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144576656e7404045400011c6056616c69646174696f6e46756e6374696f6e53746f726564000004d05468652076616c69646174696f6e2066756e6374696f6e20686173206265656e207363686564756c656420746f206170706c792e6456616c69646174696f6e46756e6374696f6e4170706c69656404015472656c61795f636861696e5f626c6f636b5f6e756d10015452656c6179436861696e426c6f636b4e756d62657200010445015468652076616c69646174696f6e2066756e6374696f6e20776173206170706c696564206173206f662074686520636f6e7461696e65642072656c617920636861696e20626c6f636b206e756d6265722e6c56616c69646174696f6e46756e6374696f6e446973636172646564000204b05468652072656c61792d636861696e2061626f727465642074686520757067726164652070726f636573732e4455706772616465417574686f72697a6564040124636f64655f6861736830011c543a3a486173680003047c416e207570677261646520686173206265656e20617574686f72697a65642e60446f776e776172644d657373616765735265636569766564040114636f756e7410010c7533320004040101536f6d6520646f776e77617264206d657373616765732068617665206265656e20726563656976656420616e642077696c6c2062652070726f6365737365642e64446f776e776172644d6573736167657350726f63657373656408012c7765696768745f75736564240118576569676874000120646d715f6865616430014472656c61795f636861696e3a3a48617368000504e0446f776e77617264206d6573736167657320776572652070726f636573736564207573696e672074686520676976656e207765696768742e445570776172644d65737361676553656e740401306d6573736167655f6861736888013c4f7074696f6e3c58636d486173683e000604b8416e20757077617264206d657373616765207761732073656e7420746f207468652072656c617920636861696e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c65748804184f7074696f6e04045401040108104e6f6e6500000010536f6d6504000400000100008c0c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144576656e7404045400011844496e76756c6e657261626c654164646564040130696e76756c6e657261626c65000130543a3a4163636f756e7449640000004c496e76756c6e657261626c6552656d6f766564040130696e76756c6e657261626c65000130543a3a4163636f756e7449640001003c4c6963656e73654f627461696e65640801286163636f756e745f6964000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e0002003c4c6963656e736552656c65617365640801286163636f756e745f6964000130543a3a4163636f756e7449640001406465706f7369745f72657475726e656418013042616c616e63654f663c543e0003003843616e64696461746541646465640401286163636f756e745f6964000130543a3a4163636f756e7449640004004043616e64696461746552656d6f7665640401286163636f756e745f6964000130543a3a4163636f756e744964000500047c54686520604576656e746020656e756d206f6620746869732070616c6c6574900c3870616c6c65745f73657373696f6e1870616c6c6574144576656e74000104284e657753657373696f6e04013473657373696f6e5f696e64657810013053657373696f6e496e64657800000839014e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f74207468659c626c6f636b206e756d626572206173207468652074797065206d6967687420737567676573742e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574940c3c70616c6c65745f62616c616e6365731870616c6c6574144576656e740804540004490001541c456e646f77656408011c6163636f756e74000130543a3a4163636f756e744964000130667265655f62616c616e6365180128543a3a42616c616e6365000004b8416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e20447573744c6f737408011c6163636f756e74000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650001083d01416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77204578697374656e7469616c4465706f7369742c78726573756c74696e6720696e20616e206f75747269676874206c6f73732e205472616e736665720c011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650002044c5472616e73666572207375636365656465642e2842616c616e636553657408010c77686f000130543a3a4163636f756e74496400011066726565180128543a3a42616c616e636500030468412062616c616e6365207761732073657420627920726f6f742e20526573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000404e0536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e28556e726573657276656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000504e8536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e4852657365727665526570617472696174656410011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500014864657374696e6174696f6e5f7374617475739801185374617475730006084d01536f6d652062616c616e636520776173206d6f7665642066726f6d207468652072657365727665206f6620746865206669727374206163636f756e7420746f20746865207365636f6e64206163636f756e742ed846696e616c20617267756d656e7420696e64696361746573207468652064657374696e6174696f6e2062616c616e636520747970652e1c4465706f73697408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000704d8536f6d6520616d6f756e7420776173206465706f73697465642028652e672e20666f72207472616e73616374696f6e2066656573292e20576974686472617708010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650008041d01536f6d6520616d6f756e74207761732077697468647261776e2066726f6d20746865206163636f756e742028652e672e20666f72207472616e73616374696f6e2066656573292e1c536c617368656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650009040101536f6d6520616d6f756e74207761732072656d6f7665642066726f6d20746865206163636f756e742028652e672e20666f72206d69736265686176696f72292e184d696e74656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000a049c536f6d6520616d6f756e7420776173206d696e74656420696e746f20616e206163636f756e742e184275726e656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000b049c536f6d6520616d6f756e7420776173206275726e65642066726f6d20616e206163636f756e742e2453757370656e64656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000c041501536f6d6520616d6f756e74207761732073757370656e6465642066726f6d20616e206163636f756e74202869742063616e20626520726573746f726564206c61746572292e20526573746f72656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000d04a4536f6d6520616d6f756e742077617320726573746f72656420696e746f20616e206163636f756e742e20557067726164656404010c77686f000130543a3a4163636f756e744964000e0460416e206163636f756e74207761732075706772616465642e18497373756564040118616d6f756e74180128543a3a42616c616e6365000f042d01546f74616c2069737375616e63652077617320696e637265617365642062792060616d6f756e74602c206372656174696e6720612063726564697420746f2062652062616c616e6365642e2452657363696e646564040118616d6f756e74180128543a3a42616c616e63650010042501546f74616c2069737375616e636520776173206465637265617365642062792060616d6f756e74602c206372656174696e672061206465627420746f2062652062616c616e6365642e184c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500110460536f6d652062616c616e636520776173206c6f636b65642e20556e6c6f636b656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500120468536f6d652062616c616e63652077617320756e6c6f636b65642e1846726f7a656e08010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500130460536f6d652062616c616e6365207761732066726f7a656e2e1854686177656408010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500140460536f6d652062616c616e636520776173207468617765642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65749814346672616d655f737570706f72741874726169747318746f6b656e73106d6973633442616c616e63655374617475730001081046726565000000205265736572766564000100009c0c6870616c6c65745f7472616e73616374696f6e5f7061796d656e741870616c6c6574144576656e74040454000104485472616e73616374696f6e466565506169640c010c77686f000130543a3a4163636f756e74496400012861637475616c5f66656518013042616c616e63654f663c543e00010c74697018013042616c616e63654f663c543e000008590141207472616e73616374696f6e20666565206061637475616c5f666565602c206f662077686963682060746970602077617320616464656420746f20746865206d696e696d756d20696e636c7573696f6e206665652c5c686173206265656e2070616964206279206077686f602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a00c3c70616c6c65745f74726561737572791870616c6c6574144576656e740804540004490001242050726f706f73656404013870726f706f73616c5f696e64657810013450726f706f73616c496e646578000004344e65772070726f706f73616c2e205370656e64696e670401406275646765745f72656d61696e696e6718013c42616c616e63654f663c542c20493e000104e45765206861766520656e6465642061207370656e6420706572696f6420616e642077696c6c206e6f7720616c6c6f636174652066756e64732e1c417761726465640c013870726f706f73616c5f696e64657810013450726f706f73616c496e646578000114617761726418013c42616c616e63654f663c542c20493e00011c6163636f756e74000130543a3a4163636f756e7449640002047c536f6d652066756e64732068617665206265656e20616c6c6f63617465642e2052656a656374656408013870726f706f73616c5f696e64657810013450726f706f73616c496e64657800011c736c617368656418013c42616c616e63654f663c542c20493e000304b0412070726f706f73616c207761732072656a65637465643b2066756e6473207765726520736c61736865642e144275726e7404012c6275726e745f66756e647318013c42616c616e63654f663c542c20493e00040488536f6d65206f66206f75722066756e64732068617665206265656e206275726e742e20526f6c6c6f766572040140726f6c6c6f7665725f62616c616e636518013c42616c616e63654f663c542c20493e0005042d015370656e64696e67206861732066696e69736865643b20746869732069732074686520616d6f756e74207468617420726f6c6c73206f76657220756e74696c206e657874207370656e642e1c4465706f73697404011476616c756518013c42616c616e63654f663c542c20493e0006047c536f6d652066756e64732068617665206265656e206465706f73697465642e345370656e64417070726f7665640c013870726f706f73616c5f696e64657810013450726f706f73616c496e646578000118616d6f756e7418013c42616c616e63654f663c542c20493e00012c62656e6566696369617279000130543a3a4163636f756e7449640007049c41206e6577207370656e642070726f706f73616c20686173206265656e20617070726f7665642e3c55706461746564496e61637469766508012c726561637469766174656418013c42616c616e63654f663c542c20493e00012c646561637469766174656418013c42616c616e63654f663c542c20493e000804cc54686520696e6163746976652066756e6473206f66207468652070616c6c65742068617665206265656e20757064617465642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a40c2c70616c6c65745f7375646f1870616c6c6574144576656e7404045400010c14537564696404012c7375646f5f726573756c74a801384469737061746368526573756c740000048841207375646f206a75737420746f6f6b20706c6163652e205c5b726573756c745c5d284b65794368616e6765640401286f6c645f7375646f6572b001504f7074696f6e3c543a3a4163636f756e7449643e0001043901546865205c5b7375646f65725c5d206a757374207377697463686564206964656e746974793b20746865206f6c64206b657920697320737570706c696564206966206f6e6520657869737465642e285375646f4173446f6e6504012c7375646f5f726573756c74a801384469737061746368526573756c740002048841207375646f206a75737420746f6f6b20706c6163652e205c5b726573756c745c5d047c54686520604576656e746020656e756d206f6620746869732070616c6c6574a80418526573756c7408045401ac044501640108084f6b0400ac000000000c4572720400640000010000ac0000040000b004184f7074696f6e04045401000108104e6f6e6500000010536f6d650400000000010000b40c306f726d6c5f76657374696e67186d6f64756c65144576656e7404045400010c5056657374696e675363686564756c6541646465640c011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e74496400014076657374696e675f7363686564756c65b8015056657374696e675363686564756c654f663c543e0000046c4164646564206e65772076657374696e67207363686564756c652e1c436c61696d656408010c77686f000130543a3a4163636f756e744964000118616d6f756e7418013042616c616e63654f663c543e00010440436c61696d65642076657374696e672e5c56657374696e675363686564756c65735570646174656404010c77686f000130543a3a4163636f756e74496400020468557064617465642076657374696e67207363686564756c65732e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574b808306f726d6c5f76657374696e673c56657374696e675363686564756c65082c426c6f636b4e756d62657201101c42616c616e6365011800100114737461727410012c426c6f636b4e756d626572000118706572696f6410012c426c6f636b4e756d626572000130706572696f645f636f756e7410010c7533320001287065725f706572696f64bc011c42616c616e63650000bc0000061800c00c306f726d6c5f78746f6b656e73186d6f64756c65144576656e74040454000104585472616e736665727265644d756c746941737365747310011873656e646572000130543a3a4163636f756e744964000118617373657473c4012c4d756c746941737365747300010c666565cc01284d756c7469417373657400011064657374d401344d756c74694c6f636174696f6e000004885472616e7366657272656420604d756c74694173736574602077697468206665652e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574c4102c73746167696e675f78636d087633286d756c746961737365742c4d756c746941737365747300000400c8013c5665633c4d756c746941737365743e0000c8000002cc00cc102c73746167696e675f78636d087633286d756c74696173736574284d756c7469417373657400000801086964d0011c4173736574496400010c66756ef8012c46756e676962696c6974790000d0102c73746167696e675f78636d087633286d756c746961737365741c4173736574496400010820436f6e63726574650400d401344d756c74694c6f636174696f6e00000020416273747261637404000401205b75383b2033325d00010000d4102c73746167696e675f78636d087633346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72d801244a756e6374696f6e730000d8102c73746167696e675f78636d087633246a756e6374696f6e73244a756e6374696f6e7300012410486572650000000858310400dc01204a756e6374696f6e0001000858320800dc01204a756e6374696f6e0000dc01204a756e6374696f6e0002000858330c00dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0003000858341000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0004000858351400dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0005000858361800dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0006000858371c00dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0007000858382000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e0000dc01204a756e6374696f6e00080000dc102c73746167696e675f78636d087633206a756e6374696f6e204a756e6374696f6e0001282450617261636861696e0400e0010c7533320000002c4163636f756e744964333208011c6e6574776f726be401444f7074696f6e3c4e6574776f726b49643e00010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726be401444f7074696f6e3c4e6574776f726b49643e000114696e64657828010c753634000200304163636f756e744b6579323008011c6e6574776f726be401444f7074696f6e3c4e6574776f726b49643e00010c6b6579ec01205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400bc0110753132380005002847656e6572616c4b65790801186c656e6774680801087538000110646174610401205b75383b2033325d000600244f6e6c794368696c6400070024506c7572616c6974790801086964f00118426f6479496400011070617274f40120426f6479506172740008003c476c6f62616c436f6e73656e7375730400e801244e6574776f726b496400090000e00000061000e404184f7074696f6e04045401e80108104e6f6e6500000010536f6d650400e80000010000e8102c73746167696e675f78636d087633206a756e6374696f6e244e6574776f726b496400012824427947656e6573697304000401205b75383b2033325d000000184279466f726b080130626c6f636b5f6e756d6265722c010c753634000128626c6f636b5f686173680401205b75383b2033325d00010020506f6c6b61646f74000200184b7573616d610003001c57657374656e6400040018526f636f636f00050018576f636f636f00060020457468657265756d040120636861696e5f696428010c7536340007002c426974636f696e436f72650008002c426974636f696e4361736800090000ec000003140000000800f0102c73746167696e675f78636d087633206a756e6374696f6e18426f6479496400012810556e69740000001c4d6f6e696b6572040044011c5b75383b20345d00010014496e6465780400e0010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e00080020547265617375727900090000f4102c73746167696e675f78636d087633206a756e6374696f6e20426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74e0010c753332000100204672616374696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c75333200040000f8102c73746167696e675f78636d087633286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400bc0110753132380000002c4e6f6e46756e6769626c650400fc01344173736574496e7374616e636500010000fc102c73746167696e675f78636d087633286d756c74696173736574344173736574496e7374616e636500011824556e646566696e656400000014496e6465780400bc01107531323800010018417272617934040044011c5b75383b20345d0002001841727261793804000101011c5b75383b20385d0003001c417272617931360400050101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d000500000101000003080000000800050100000310000000080009010c2c6f726d6c5f746f6b656e73186d6f64756c65144576656e740404540001441c456e646f7765640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000004b8416e206163636f756e74207761732063726561746564207769746820736f6d6520667265652062616c616e63652e20447573744c6f73740c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000108ec416e206163636f756e74207761732072656d6f7665642077686f73652062616c616e636520776173206e6f6e2d7a65726f206275742062656c6f77c84578697374656e7469616c4465706f7369742c20726573756c74696e6720696e20616e206f75747269676874206c6f73732e205472616e7366657210012c63757272656e63795f69640d010134543a3a43757272656e6379496400011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e63650002044c5472616e73666572207375636365656465642e2052657365727665640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000304e0536f6d652062616c616e63652077617320726573657276656420286d6f7665642066726f6d206672656520746f207265736572766564292e28556e72657365727665640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000404e8536f6d652062616c616e63652077617320756e726573657276656420286d6f7665642066726f6d20726573657276656420746f2066726565292e4852657365727665526570617472696174656414012c63757272656e63795f69640d010134543a3a43757272656e6379496400011066726f6d000130543a3a4163636f756e744964000108746f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e636500011873746174757398013442616c616e6365537461747573000508f4536f6d652072657365727665642062616c616e63652077617320726570617472696174656420286d6f7665642066726f6d20726573657276656420746f44616e6f74686572206163636f756e74292e2842616c616e636553657410012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e74496400011066726565180128543a3a42616c616e63650001207265736572766564180128543a3a42616c616e636500060468412062616c616e6365207761732073657420627920726f6f742e40546f74616c49737375616e636553657408012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365000704b854686520746f74616c2069737375616e6365206f6620616e2063757272656e637920686173206265656e207365742457697468647261776e0c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000804ec536f6d652062616c616e63657320776572652077697468647261776e2028652e672e2070617920666f72207472616e73616374696f6e20666565291c536c617368656410012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e74496400012c667265655f616d6f756e74180128543a3a42616c616e636500013c72657365727665645f616d6f756e74180128543a3a42616c616e6365000904d4536f6d652062616c616e636573207765726520736c61736865642028652e672e2064756520746f206d69732d6265686176696f7229244465706f73697465640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000a04984465706f736974656420736f6d652062616c616e636520696e746f20616e206163636f756e741c4c6f636b53657410011c6c6f636b5f6964010101384c6f636b4964656e74696669657200012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000b0454536f6d652066756e647320617265206c6f636b65642c4c6f636b52656d6f7665640c011c6c6f636b5f6964010101384c6f636b4964656e74696669657200012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000c047c536f6d65206c6f636b65642066756e6473207765726520756e6c6f636b6564184c6f636b65640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000d0474536f6d6520667265652062616c616e636520776173206c6f636b65642e20556e6c6f636b65640c012c63757272656e63795f69640d010134543a3a43757272656e6379496400010c77686f000130543a3a4163636f756e744964000118616d6f756e74180128543a3a42616c616e6365000e0478536f6d65206c6f636b65642062616c616e6365207761732066726565642e1849737375656408012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365000f002452657363696e64656408012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365001000047c54686520604576656e746020656e756d206f6620746869732070616c6c65740d01085470616c6c65745f666f726569676e5f6173736574731c4173736574496400010838466f726569676e417373657449640400100138466f726569676e41737365744964000000344e6174697665417373657449640400110101384e617469766543757272656e6379000100001101085470616c6c65745f666f726569676e5f617373657473384e617469766543757272656e6379000108104865726500000018506172656e740001000015010c3c70616c6c65745f6964656e746974791870616c6c6574144576656e740404540001342c4964656e7469747953657404010c77686f000130543a3a4163636f756e744964000004ec41206e616d652077617320736574206f72207265736574202877686963682077696c6c2072656d6f766520616c6c206a756467656d656e7473292e3c4964656e74697479436c656172656408010c77686f000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000104cc41206e616d652077617320636c65617265642c20616e642074686520676976656e2062616c616e63652072657475726e65642e384964656e746974794b696c6c656408010c77686f000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000204c441206e616d65207761732072656d6f76656420616e642074686520676976656e2062616c616e636520736c61736865642e484964656e746974696573496e736572746564040118616d6f756e7410010c753332000304090141206e756d626572206f66206964656e74697469657320616e64206173736f63696174656420696e666f207765726520666f726369626c7920696e7365727465642e444964656e74697469657352656d6f766564040118616d6f756e7410010c753332000404150141206e756d626572206f66206964656e74697469657320616e6420616c6c206173736f63696174656420696e666f207765726520666f726369626c792072656d6f7665642e484a756467656d656e7452657175657374656408010c77686f000130543a3a4163636f756e74496400013c7265676973747261725f696e646578100138526567697374726172496e6465780005049c41206a756467656d656e74207761732061736b65642066726f6d2061207265676973747261722e504a756467656d656e74556e72657175657374656408010c77686f000130543a3a4163636f756e74496400013c7265676973747261725f696e646578100138526567697374726172496e6465780006048841206a756467656d656e74207265717565737420776173207265747261637465642e384a756467656d656e74476976656e080118746172676574000130543a3a4163636f756e74496400013c7265676973747261725f696e646578100138526567697374726172496e6465780007049441206a756467656d656e742077617320676976656e2062792061207265676973747261722e38526567697374726172416464656404013c7265676973747261725f696e646578100138526567697374726172496e646578000804584120726567697374726172207761732061646465642e405375624964656e7469747941646465640c010c737562000130543a3a4163636f756e7449640001106d61696e000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000904f441207375622d6964656e746974792077617320616464656420746f20616e206964656e7469747920616e6420746865206465706f73697420706169642e485375624964656e7469747952656d6f7665640c010c737562000130543a3a4163636f756e7449640001106d61696e000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000a04090141207375622d6964656e74697479207761732072656d6f7665642066726f6d20616e206964656e7469747920616e6420746865206465706f7369742066726565642e485375624964656e746974795265766f6b65640c010c737562000130543a3a4163636f756e7449640001106d61696e000130543a3a4163636f756e74496400011c6465706f73697418013042616c616e63654f663c543e000b08190141207375622d6964656e746974792077617320636c65617265642c20616e642074686520676976656e206465706f7369742072657061747269617465642066726f6d20746865c86d61696e206964656e74697479206163636f756e7420746f20746865207375622d6964656e74697479206163636f756e742e545375624964656e746974696573496e736572746564040118616d6f756e7410010c753332000c04150141206e756d626572206f66206964656e746974696573207765726520666f726369626c7920757064617465642077697468206e6577207375622d6964656e7469746965732e047c54686520604576656e746020656e756d206f6620746869732070616c6c657419010c3c70616c6c65745f707265696d6167651870616c6c6574144576656e7404045400010c144e6f7465640401106861736830011c543a3a48617368000004684120707265696d61676520686173206265656e206e6f7465642e245265717565737465640401106861736830011c543a3a48617368000104784120707265696d61676520686173206265656e207265717565737465642e1c436c65617265640401106861736830011c543a3a486173680002046c4120707265696d616765206861732062656e20636c65617265642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65741d010c4070616c6c65745f64656d6f63726163791870616c6c6574144576656e740404540001442050726f706f73656408013870726f706f73616c5f696e64657810012450726f70496e64657800011c6465706f73697418013042616c616e63654f663c543e000004bc41206d6f74696f6e20686173206265656e2070726f706f7365642062792061207075626c6963206163636f756e742e185461626c656408013870726f706f73616c5f696e64657810012450726f70496e64657800011c6465706f73697418013042616c616e63654f663c543e000104d841207075626c69632070726f706f73616c20686173206265656e207461626c656420666f72207265666572656e64756d20766f74652e3845787465726e616c5461626c656400020494416e2065787465726e616c2070726f706f73616c20686173206265656e207461626c65642e1c537461727465640801247265665f696e64657810013c5265666572656e64756d496e6465780001247468726573686f6c6421010134566f74655468726573686f6c640003045c41207265666572656e64756d2068617320626567756e2e185061737365640401247265665f696e64657810013c5265666572656e64756d496e646578000404ac412070726f706f73616c20686173206265656e20617070726f766564206279207265666572656e64756d2e244e6f745061737365640401247265665f696e64657810013c5265666572656e64756d496e646578000504ac412070726f706f73616c20686173206265656e2072656a6563746564206279207265666572656e64756d2e2443616e63656c6c65640401247265665f696e64657810013c5265666572656e64756d496e6465780006048041207265666572656e64756d20686173206265656e2063616e63656c6c65642e2444656c65676174656408010c77686f000130543a3a4163636f756e744964000118746172676574000130543a3a4163636f756e744964000704dc416e206163636f756e74206861732064656c65676174656420746865697220766f746520746f20616e6f74686572206163636f756e742e2c556e64656c65676174656404011c6163636f756e74000130543a3a4163636f756e744964000804e4416e206163636f756e74206861732063616e63656c6c656420612070726576696f75732064656c65676174696f6e206f7065726174696f6e2e185665746f65640c010c77686f000130543a3a4163636f756e74496400013470726f706f73616c5f6861736830011048323536000114756e74696c100144426c6f636b4e756d626572466f723c543e00090494416e2065787465726e616c2070726f706f73616c20686173206265656e207665746f65642e2c426c61636b6c697374656404013470726f706f73616c5f6861736830011048323536000a04c4412070726f706f73616c5f6861736820686173206265656e20626c61636b6c6973746564207065726d616e656e746c792e14566f7465640c0114766f746572000130543a3a4163636f756e7449640001247265665f696e64657810013c5265666572656e64756d496e646578000110766f7465250101644163636f756e74566f74653c42616c616e63654f663c543e3e000b0490416e206163636f756e742068617320766f74656420696e2061207265666572656e64756d205365636f6e6465640801207365636f6e646572000130543a3a4163636f756e74496400012870726f705f696e64657810012450726f70496e646578000c048c416e206163636f756e742068617320736563636f6e64656420612070726f706f73616c4050726f706f73616c43616e63656c656404012870726f705f696e64657810012450726f70496e646578000d0460412070726f706f73616c20676f742063616e63656c65642e2c4d657461646174615365740801146f776e65722d0101344d657461646174614f776e6572043c4d65746164617461206f776e65722e011068617368300130507265696d616765486173680438507265696d61676520686173682e0e04d44d6574616461746120666f7220612070726f706f73616c206f722061207265666572656e64756d20686173206265656e207365742e3c4d65746164617461436c65617265640801146f776e65722d0101344d657461646174614f776e6572043c4d65746164617461206f776e65722e011068617368300130507265696d616765486173680438507265696d61676520686173682e0f04e44d6574616461746120666f7220612070726f706f73616c206f722061207265666572656e64756d20686173206265656e20636c65617265642e4c4d657461646174615472616e736665727265640c0128707265765f6f776e65722d0101344d657461646174614f776e6572046050726576696f7573206d65746164617461206f776e65722e01146f776e65722d0101344d657461646174614f776e6572044c4e6577206d65746164617461206f776e65722e011068617368300130507265696d616765486173680438507265696d61676520686173682e1004ac4d6574616461746120686173206265656e207472616e7366657272656420746f206e6577206f776e65722e047c54686520604576656e746020656e756d206f6620746869732070616c6c657421010c4070616c6c65745f64656d6f637261637938766f74655f7468726573686f6c6434566f74655468726573686f6c6400010c5053757065724d616a6f72697479417070726f76650000005053757065724d616a6f72697479416761696e73740001003853696d706c654d616a6f726974790002000025010c4070616c6c65745f64656d6f637261637910766f74652c4163636f756e74566f7465041c42616c616e636501180108205374616e64617264080110766f746529010110566f746500011c62616c616e636518011c42616c616e63650000001453706c697408010c61796518011c42616c616e636500010c6e617918011c42616c616e63650001000029010c4070616c6c65745f64656d6f637261637910766f746510566f746500000400080000002d010c4070616c6c65745f64656d6f6372616379147479706573344d657461646174614f776e657200010c2045787465726e616c0000002050726f706f73616c040010012450726f70496e646578000100285265666572656e64756d040010013c5265666572656e64756d496e6465780002000031010c4470616c6c65745f636f6c6c6563746976651870616c6c6574144576656e7408045400044900011c2050726f706f73656410011c6163636f756e74000130543a3a4163636f756e74496400013870726f706f73616c5f696e64657810013450726f706f73616c496e64657800013470726f706f73616c5f6861736830011c543a3a486173680001247468726573686f6c6410012c4d656d626572436f756e74000008490141206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e3c604d656d626572436f756e7460292e14566f74656414011c6163636f756e74000130543a3a4163636f756e74496400013470726f706f73616c5f6861736830011c543a3a48617368000114766f74656435010110626f6f6c00010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e74000108050141206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e671501612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e20726573706563746976656c7920617320604d656d626572436f756e7460292e20417070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000204c041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000304d041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c74000404210141206d6f74696f6e207761732065786563757465643b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e384d656d626572457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c740005044901412073696e676c65206d656d6265722064696420736f6d6520616374696f6e3b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e18436c6f7365640c013470726f706f73616c5f6861736830011c543a3a4861736800010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e740006045501412070726f706f73616c2077617320636c6f736564206265636175736520697473207468726573686f6c64207761732072656163686564206f7220616674657220697473206475726174696f6e207761732075702e047c54686520604576656e746020656e756d206f6620746869732070616c6c65743501000005000039010c4470616c6c65745f636f6c6c6563746976651870616c6c6574144576656e7408045400044900011c2050726f706f73656410011c6163636f756e74000130543a3a4163636f756e74496400013870726f706f73616c5f696e64657810013450726f706f73616c496e64657800013470726f706f73616c5f6861736830011c543a3a486173680001247468726573686f6c6410012c4d656d626572436f756e74000008490141206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e3c604d656d626572436f756e7460292e14566f74656414011c6163636f756e74000130543a3a4163636f756e74496400013470726f706f73616c5f6861736830011c543a3a48617368000114766f74656435010110626f6f6c00010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e74000108050141206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e671501612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e20726573706563746976656c7920617320604d656d626572436f756e7460292e20417070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000204c041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404013470726f706f73616c5f6861736830011c543a3a48617368000304d041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c74000404210141206d6f74696f6e207761732065786563757465643b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e384d656d626572457865637574656408013470726f706f73616c5f6861736830011c543a3a48617368000118726573756c74a801384469737061746368526573756c740005044901412073696e676c65206d656d6265722064696420736f6d6520616374696f6e3b20726573756c742077696c6c20626520604f6b602069662069742072657475726e656420776974686f7574206572726f722e18436c6f7365640c013470726f706f73616c5f6861736830011c543a3a4861736800010c79657310012c4d656d626572436f756e740001086e6f10012c4d656d626572436f756e740006045501412070726f706f73616c2077617320636c6f736564206265636175736520697473207468726573686f6c64207761732072656163686564206f7220616674657220697473206475726174696f6e207761732075702e047c54686520604576656e746020656e756d206f6620746869732070616c6c65743d010c4470616c6c65745f6d656d626572736869701870616c6c6574144576656e740804540004490001182c4d656d6265724164646564000004e054686520676976656e206d656d626572207761732061646465643b2073656520746865207472616e73616374696f6e20666f722077686f2e344d656d62657252656d6f766564000104e854686520676976656e206d656d626572207761732072656d6f7665643b2073656520746865207472616e73616374696f6e20666f722077686f2e384d656d6265727353776170706564000204d854776f206d656d62657273207765726520737761707065643b2073656520746865207472616e73616374696f6e20666f722077686f2e304d656d6265727352657365740003041501546865206d656d62657273686970207761732072657365743b2073656520746865207472616e73616374696f6e20666f722077686f20746865206e6577207365742069732e284b65794368616e676564000404844f6e65206f6620746865206d656d6265727327206b657973206368616e6765642e1444756d6d790005046c5068616e746f6d206d656d6265722c206e6576657220757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657441010c4470616c6c65745f6d656d626572736869701870616c6c6574144576656e740804540004490001182c4d656d6265724164646564000004e054686520676976656e206d656d626572207761732061646465643b2073656520746865207472616e73616374696f6e20666f722077686f2e344d656d62657252656d6f766564000104e854686520676976656e206d656d626572207761732072656d6f7665643b2073656520746865207472616e73616374696f6e20666f722077686f2e384d656d6265727353776170706564000204d854776f206d656d62657273207765726520737761707065643b2073656520746865207472616e73616374696f6e20666f722077686f2e304d656d6265727352657365740003041501546865206d656d62657273686970207761732072657365743b2073656520746865207472616e73616374696f6e20666f722077686f20746865206e6577207365742069732e284b65794368616e676564000404844f6e65206f6620746865206d656d6265727327206b657973206368616e6765642e1444756d6d790005046c5068616e746f6d206d656d6265722c206e6576657220757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657445010c6070616c6c65745f72616e6b65645f636f6c6c6563746976651870616c6c6574144576656e740804540004490001102c4d656d626572416464656404010c77686f000130543a3a4163636f756e7449640000047841206d656d626572206077686f6020686173206265656e2061646465642e2c52616e6b4368616e67656408010c77686f000130543a3a4163636f756e74496400011072616e6b4901011052616e6b000104f4546865206d656d626572206077686f6073652072616e6b20686173206265656e206368616e67656420746f2074686520676976656e206072616e6b602e344d656d62657252656d6f76656408010c77686f000130543a3a4163636f756e74496400011072616e6b4901011052616e6b0002041901546865206d656d626572206077686f60206f6620676976656e206072616e6b6020686173206265656e2072656d6f7665642066726f6d2074686520636f6c6c6563746976652e14566f74656410010c77686f000130543a3a4163636f756e744964000110706f6c6c100144506f6c6c496e6465784f663c542c20493e000110766f74654d010128566f74655265636f726400011474616c6c795101013454616c6c794f663c542c20493e0003085501546865206d656d626572206077686f602068617320766f74656420666f72207468652060706f6c6c6020776974682074686520676976656e2060766f746560206c656164696e6720746f20616e2075706461746564206074616c6c79602e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574490100000504004d01086070616c6c65745f72616e6b65645f636f6c6c65637469766528566f74655265636f72640001080c4179650400100114566f7465730000000c4e61790400100114566f746573000100005101086070616c6c65745f72616e6b65645f636f6c6c6563746976651454616c6c790c045400044900044d00000c0124626172655f6179657310012c4d656d626572496e64657800011061796573100114566f7465730001106e617973100114566f746573000055010c4070616c6c65745f7265666572656e64611870616c6c6574144576656e74080454000449000140245375626d69747465640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e0114747261636b4901013c547261636b49644f663c542c20493e04250154686520747261636b2028616e6420627920657874656e73696f6e2070726f706f73616c206469737061746368206f726967696e29206f662074686973207265666572656e64756d2e012070726f706f73616c5901014c426f756e64656443616c6c4f663c542c20493e04805468652070726f706f73616c20666f7220746865207265666572656e64756d2e00048041207265666572656e64756d20686173206265656e207375626d69747465642e544465636973696f6e4465706f736974506c616365640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e010494546865206465636973696f6e206465706f73697420686173206265656e20706c616365642e5c4465636973696f6e4465706f736974526566756e6465640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e02049c546865206465636973696f6e206465706f73697420686173206265656e20726566756e6465642e384465706f736974536c617368656408010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e03047041206465706f73697420686173206265656e20736c6173686165642e3c4465636973696f6e53746172746564100114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e0114747261636b4901013c547261636b49644f663c542c20493e04250154686520747261636b2028616e6420627920657874656e73696f6e2070726f706f73616c206469737061746368206f726967696e29206f662074686973207265666572656e64756d2e012070726f706f73616c5901014c426f756e64656443616c6c4f663c542c20493e04805468652070726f706f73616c20666f7220746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b85468652063757272656e742074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0404bc41207265666572656e64756d20686173206d6f76656420696e746f20746865206465636964696e672070686173652e38436f6e6669726d53746172746564040114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e050038436f6e6669726d41626f72746564040114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e060024436f6e6669726d6564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0704210141207265666572656e64756d2068617320656e6465642069747320636f6e6669726d6174696f6e20706861736520616e6420697320726561647920666f7220617070726f76616c2e20417070726f766564040114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e08040d0141207265666572656e64756d20686173206265656e20617070726f76656420616e64206974732070726f706f73616c20686173206265656e207363686564756c65642e2052656a6563746564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0904ac412070726f706f73616c20686173206265656e2072656a6563746564206279207265666572656e64756d2e2054696d65644f7574080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0a04d841207265666572656e64756d20686173206265656e2074696d6564206f757420776974686f7574206265696e6720646563696465642e2443616e63656c6c6564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0b048041207265666572656e64756d20686173206265656e2063616e63656c6c65642e184b696c6c6564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011474616c6c7951010120543a3a54616c6c7904b05468652066696e616c2074616c6c79206f6620766f74657320696e2074686973207265666572656e64756d2e0c047441207265666572656e64756d20686173206265656e206b696c6c65642e645375626d697373696f6e4465706f736974526566756e6465640c0114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e010c77686f000130543a3a4163636f756e744964048c546865206163636f756e742077686f20706c6163656420746865206465706f7369742e0118616d6f756e7418013c42616c616e63654f663c542c20493e048454686520616d6f756e7420706c6163656420627920746865206163636f756e742e0d04a4546865207375626d697373696f6e206465706f73697420686173206265656e20726566756e6465642e2c4d65746164617461536574080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011068617368300130507265696d616765486173680438507265696d61676520686173682e0e049c4d6574616461746120666f722061207265666572656e64756d20686173206265656e207365742e3c4d65746164617461436c6561726564080114696e64657810013c5265666572656e64756d496e6465780460496e646578206f6620746865207265666572656e64756d2e011068617368300130507265696d616765486173680438507265696d61676520686173682e0f04ac4d6574616461746120666f722061207265666572656e64756d20686173206265656e20636c65617265642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574590110346672616d655f737570706f72741874726169747324707265696d616765731c426f756e646564040454015d01010c184c6567616379040110686173683001104861736800000018496e6c696e650400b5050134426f756e646564496e6c696e65000100184c6f6f6b7570080110686173683001104861736800010c6c656e10010c753332000200005d0108306f70616c5f72756e74696d652c52756e74696d6543616c6c0001a01853797374656d0400610101ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53797374656d2c2052756e74696d653e000000485374617465547269654d6967726174696f6e0400710101dd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5374617465547269654d6967726174696f6e2c2052756e74696d653e0001003c50617261636861696e53797374656d0400890101d10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e53797374656d2c2052756e74696d653e0014003450617261636861696e496e666f0400c10101c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c50617261636861696e496e666f2c2052756e74696d653e00150044436f6c6c61746f7253656c656374696f6e0400c50101d90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f6c6c61746f7253656c656374696f6e2c2052756e74696d653e0017001c53657373696f6e0400c90101b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c53657373696f6e2c2052756e74696d653e0018002042616c616e6365730400d90101b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c42616c616e6365732c2052756e74696d653e001e002454696d657374616d700400e90101b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c54696d657374616d702c2052756e74696d653e0020002054726561737572790400ed0101b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c54726561737572792c2052756e74696d653e002200105375646f0400f10101a50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5375646f2c2052756e74696d653e0023001c56657374696e670400f50101b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c56657374696e672c2052756e74696d653e0025001c58546f6b656e730400fd0101b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c58546f6b656e732c2052756e74696d653e00260018546f6b656e7304004d0201ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546f6b656e732c2052756e74696d653e002700204964656e746974790400510201b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4964656e746974792c2052756e74696d653e00280020507265696d6167650400190301b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c507265696d6167652c2052756e74696d653e0029002444656d6f637261637904001d0301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c44656d6f63726163792c2052756e74696d653e002a001c436f756e63696c04002d0301b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f756e63696c2c2052756e74696d653e002b0048546563686e6963616c436f6d6d69747465650400310301dd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546563686e6963616c436f6d6d69747465652c2052756e74696d653e002c0044436f756e63696c4d656d626572736869700400350301d90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f756e63696c4d656d626572736869702c2052756e74696d653e002d0070546563686e6963616c436f6d6d69747465654d656d626572736869700400390301050273656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546563686e6963616c436f6d6d69747465654d656d626572736869702c2052756e74696d653e002e005046656c6c6f7773686970436f6c6c65637469766504003d0301e50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c46656c6c6f7773686970436f6c6c6563746976652c2052756e74696d653e002f004c46656c6c6f77736869705265666572656e64610400410301e10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c46656c6c6f77736869705265666572656e64612c2052756e74696d653e003000245363686564756c65720400710301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5363686564756c65722c2052756e74696d653e0031002458636d70517565756504007d0301b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c58636d7051756575652c2052756e74696d653e0032002c506f6c6b61646f7458636d0400810301c10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c506f6c6b61646f7458636d2c2052756e74696d653e0033002843756d756c757358636d0400250401bd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c43756d756c757358636d2c2052756e74696d653e00340020446d7051756575650400290401b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c446d7051756575652c2052756e74696d653e00350024496e666c6174696f6e04002d0401b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c496e666c6174696f6e2c2052756e74696d653e003c0018556e697175650400310401ad0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c556e697175652c2052756e74696d653e003d0034436f6e66696775726174696f6e04000d0501c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c436f6e66696775726174696f6e2c2052756e74696d653e003f00245374727563747572650400290501b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5374727563747572652c2052756e74696d653e0046003041707050726f6d6f74696f6e04002d0501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c41707050726f6d6f74696f6e2c2052756e74696d653e00490034466f726569676e4173736574730400350501c90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c466f726569676e4173736574732c2052756e74696d653e0050000c45564d0400450501a10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c45564d2c2052756e74696d653e00640020457468657265756d0400610501b50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c457468657265756d2c2052756e74696d653e0065004845766d436f6e747261637448656c706572730400890501dd0173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c45766d436f6e747261637448656c706572732c2052756e74696d653e0097003045766d4d6967726174696f6e0400910501c50173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c45766d4d6967726174696f6e2c2052756e74696d653e0099002c4d61696e74656e616e63650400a50501c10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c4d61696e74656e616e63652c2052756e74696d653e009a001c5574696c6974790400a90501b10173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c5574696c6974792c2052756e74696d653e009c0024546573745574696c730400b10501b90173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a64697370617463680a3a3a43616c6c61626c6543616c6c466f723c546573745574696c732c2052756e74696d653e00ff000061010c306672616d655f73797374656d1870616c6c65741043616c6c0404540001201872656d61726b04011872656d61726b34011c5665633c75383e0000045c536565205b6050616c6c65743a3a72656d61726b605d2e387365745f686561705f706167657304011470616765732c010c7536340001047c536565205b6050616c6c65743a3a7365745f686561705f7061676573605d2e207365745f636f6465040110636f646534011c5665633c75383e00020464536565205b6050616c6c65743a3a7365745f636f6465605d2e5c7365745f636f64655f776974686f75745f636865636b73040110636f646534011c5665633c75383e000304a0536565205b6050616c6c65743a3a7365745f636f64655f776974686f75745f636865636b73605d2e2c7365745f73746f726167650401146974656d73650101345665633c4b657956616c75653e00040470536565205b6050616c6c65743a3a7365745f73746f72616765605d2e306b696c6c5f73746f726167650401106b6579736d0101205665633c4b65793e00050474536565205b6050616c6c65743a3a6b696c6c5f73746f72616765605d2e2c6b696c6c5f70726566697808011870726566697834010c4b657900011c7375626b65797310010c75333200060470536565205b6050616c6c65743a3a6b696c6c5f707265666978605d2e4472656d61726b5f776974685f6576656e7404011872656d61726b34011c5665633c75383e00070488536565205b6050616c6c65743a3a72656d61726b5f776974685f6576656e74605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e65010000026901006901000004083434006d01000002340071010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c65741043616c6c04045400011858636f6e74726f6c5f6175746f5f6d6967726174696f6e0401306d617962655f636f6e6669677501015c4f7074696f6e3c4d6967726174696f6e4c696d6974733e0000049c536565205b6050616c6c65743a3a636f6e74726f6c5f6175746f5f6d6967726174696f6e605d2e40636f6e74696e75655f6d6967726174650c01186c696d6974737901013c4d6967726174696f6e4c696d69747300013c7265616c5f73697a655f757070657210010c7533320001307769746e6573735f7461736b7d0101404d6967726174696f6e5461736b3c543e00010484536565205b6050616c6c65743a3a636f6e74696e75655f6d696772617465605d2e486d6967726174655f637573746f6d5f746f700801106b6579736d0101305665633c5665633c75383e3e0001307769746e6573735f73697a6510010c7533320002048c536565205b6050616c6c65743a3a6d6967726174655f637573746f6d5f746f70605d2e506d6967726174655f637573746f6d5f6368696c640c0110726f6f7434011c5665633c75383e0001286368696c645f6b6579736d0101305665633c5665633c75383e3e000128746f74616c5f73697a6510010c75333200030494536565205b6050616c6c65743a3a6d6967726174655f637573746f6d5f6368696c64605d2e547365745f7369676e65645f6d61785f6c696d6974730401186c696d6974737901013c4d6967726174696f6e4c696d69747300040498536565205b6050616c6c65743a3a7365745f7369676e65645f6d61785f6c696d697473605d2e48666f7263655f7365745f70726f677265737308013070726f67726573735f746f708101013450726f67726573734f663c543e00013870726f67726573735f6368696c648101013450726f67726573734f663c543e0005048c536565205b6050616c6c65743a3a666f7263655f7365745f70726f6772657373605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e750104184f7074696f6e0404540179010108104e6f6e6500000010536f6d6504007901000001000079010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c65743c4d6967726174696f6e4c696d697473000008011073697a6510010c7533320001106974656d10010c75333200007d010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c6574344d6967726174696f6e5461736b040454000014013070726f67726573735f746f708101013450726f67726573734f663c543e00013870726f67726573735f6368696c648101013450726f67726573734f663c543e00011073697a6510010c753332000124746f705f6974656d7310010c75333200012c6368696c645f6974656d7310010c753332000081010c6c70616c6c65745f73746174655f747269655f6d6967726174696f6e1870616c6c65742050726f677265737304244d61784b65794c656e00010c1c546f53746172740000001c4c6173744b6579040085010164426f756e6465645665633c75382c204d61784b65794c656e3e00010020436f6d706c6574650002000085010c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000089010c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c65741043616c6c0404540001104c7365745f76616c69646174696f6e5f64617461040110646174618d01015450617261636861696e496e686572656e744461746100000490536565205b6050616c6c65743a3a7365745f76616c69646174696f6e5f64617461605d2e607375646f5f73656e645f7570776172645f6d65737361676504011c6d6573736167653401345570776172644d657373616765000104a4536565205b6050616c6c65743a3a7375646f5f73656e645f7570776172645f6d657373616765605d2e44617574686f72697a655f75706772616465080124636f64655f6861736830011c543a3a48617368000134636865636b5f76657273696f6e35010110626f6f6c00020488536565205b6050616c6c65743a3a617574686f72697a655f75706772616465605d2e60656e6163745f617574686f72697a65645f75706772616465040110636f646534011c5665633c75383e000304a4536565205b6050616c6c65743a3a656e6163745f617574686f72697a65645f75706772616465605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e8d01089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e745450617261636861696e496e686572656e7444617461000010013c76616c69646174696f6e5f646174619101015c50657273697374656456616c69646174696f6e4461746100014472656c61795f636861696e5f73746174659901015473705f747269653a3a53746f7261676550726f6f66000144646f776e776172645f6d65737361676573a101016c5665633c496e626f756e64446f776e776172644d6573736167653e00014c686f72697a6f6e74616c5f6d65737361676573a90101a442547265654d61703c5061726149642c205665633c496e626f756e6448726d704d6573736167653e3e000091010c4c706f6c6b61646f745f7072696d6974697665730876355c50657273697374656456616c69646174696f6e446174610804480130044e01100010012c706172656e745f6865616495010120486561644461746100014c72656c61795f706172656e745f6e756d6265721001044e00016472656c61795f706172656e745f73746f726167655f726f6f74300104480001306d61785f706f765f73697a6510010c753332000095010c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665732048656164446174610000040034011c5665633c75383e000099010c1c73705f747269653473746f726167655f70726f6f663053746f7261676550726f6f660000040128747269655f6e6f6465739d01014442547265655365743c5665633c75383e3e00009d010420425472656553657404045401340004006d01000000a101000002a50100a5010860706f6c6b61646f745f636f72655f7072696d69746976657358496e626f756e64446f776e776172644d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d62657200010c6d736734013c446f776e776172644d6573736167650000a901042042547265654d617008044b01ad01045601b101000400b901000000ad010c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665730849640000040010010c7533320000b101000002b50100b5010860706f6c6b61646f745f636f72655f7072696d69746976657348496e626f756e6448726d704d657373616765042c426c6f636b4e756d62657201100008011c73656e745f617410012c426c6f636b4e756d6265720001106461746134015073705f7374643a3a7665633a3a5665633c75383e0000b901000002bd0100bd0100000408ad01b10100c1010c3870617261636861696e5f696e666f1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ec5010c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c65741043616c6c04045400011c406164645f696e76756c6e657261626c6504010c6e6577000130543a3a4163636f756e74496400000484536565205b6050616c6c65743a3a6164645f696e76756c6e657261626c65605d2e4c72656d6f76655f696e76756c6e657261626c6504010c77686f000130543a3a4163636f756e74496400010490536565205b6050616c6c65743a3a72656d6f76655f696e76756c6e657261626c65605d2e2c6765745f6c6963656e736500020470536565205b6050616c6c65743a3a6765745f6c6963656e7365605d2e1c6f6e626f61726400030460536565205b6050616c6c65743a3a6f6e626f617264605d2e206f6666626f61726400040464536565205b6050616c6c65743a3a6f6666626f617264605d2e3c72656c656173655f6c6963656e736500050480536565205b6050616c6c65743a3a72656c656173655f6c6963656e7365605d2e54666f7263655f72656c656173655f6c6963656e736504010c77686f000130543a3a4163636f756e74496400060498536565205b6050616c6c65743a3a666f7263655f72656c656173655f6c6963656e7365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ec9010c3870616c6c65745f73657373696f6e1870616c6c65741043616c6c040454000108207365745f6b6579730801106b657973cd01011c543a3a4b65797300011470726f6f6634011c5665633c75383e00000464536565205b6050616c6c65743a3a7365745f6b657973605d2e2870757267655f6b6579730001046c536565205b6050616c6c65743a3a70757267655f6b657973605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ecd010c306f70616c5f72756e74696d653872756e74696d655f636f6d6d6f6e2c53657373696f6e4b657973000004011061757261d10101c43c41757261206173202463726174653a3a426f756e64546f52756e74696d654170705075626c69633e3a3a5075626c69630000d101104473705f636f6e73656e7375735f617572611c737232353531392c6170705f73723235353139185075626c696300000400d501013c737232353531393a3a5075626c69630000d5010c1c73705f636f72651c73723235353139185075626c6963000004000401205b75383b2033325d0000d9010c3c70616c6c65745f62616c616e6365731870616c6c65741043616c6c080454000449000124507472616e736665725f616c6c6f775f646561746808011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e636500000494536565205b6050616c6c65743a3a7472616e736665725f616c6c6f775f6465617468605d2e587365745f62616c616e63655f646570726563617465640c010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001206e65775f66726565bc0128543a3a42616c616e63650001306f6c645f7265736572766564bc0128543a3a42616c616e63650001049c536565205b6050616c6c65743a3a7365745f62616c616e63655f64657072656361746564605d2e38666f7263655f7472616e736665720c0118736f75726365dd0101504163636f756e7449644c6f6f6b75704f663c543e00011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e63650002047c536565205b6050616c6c65743a3a666f7263655f7472616e73666572605d2e4c7472616e736665725f6b6565705f616c69766508011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e636500030490536565205b6050616c6c65743a3a7472616e736665725f6b6565705f616c697665605d2e307472616e736665725f616c6c08011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e0001286b6565705f616c69766535010110626f6f6c00040474536565205b6050616c6c65743a3a7472616e736665725f616c6c605d2e3c666f7263655f756e7265736572766508010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e000118616d6f756e74180128543a3a42616c616e636500050480536565205b6050616c6c65743a3a666f7263655f756e72657365727665605d2e40757067726164655f6163636f756e747304010c77686fe50101445665633c543a3a4163636f756e7449643e00060484536565205b6050616c6c65743a3a757067726164655f6163636f756e7473605d2e207472616e7366657208011064657374dd0101504163636f756e7449644c6f6f6b75704f663c543e00011476616c7565bc0128543a3a42616c616e636500070464536565205b6050616c6c65743a3a7472616e73666572605d2e44666f7263655f7365745f62616c616e636508010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001206e65775f66726565bc0128543a3a42616c616e636500080488536565205b6050616c6c65743a3a666f7263655f7365745f62616c616e6365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732edd010c2873705f72756e74696d65306d756c746961646472657373304d756c74694164647265737308244163636f756e7449640100304163636f756e74496e64657801ac011408496404000001244163636f756e74496400000014496e6465780400e10101304163636f756e74496e6465780001000c526177040034011c5665633c75383e0002002441646472657373333204000401205b75383b2033325d000300244164647265737332300400ec01205b75383b2032305d00040000e101000006ac00e5010000020000e9010c4070616c6c65745f74696d657374616d701870616c6c65741043616c6c0404540001040c73657404010c6e6f77280124543a3a4d6f6d656e7400000450536565205b6050616c6c65743a3a736574605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732eed010c3c70616c6c65745f74726561737572791870616c6c65741043616c6c0804540004490001143470726f706f73655f7370656e6408011476616c7565bc013c42616c616e63654f663c542c20493e00012c62656e6566696369617279dd0101504163636f756e7449644c6f6f6b75704f663c543e00000478536565205b6050616c6c65743a3a70726f706f73655f7370656e64605d2e3c72656a6563745f70726f706f73616c04012c70726f706f73616c5f6964e0013450726f706f73616c496e64657800010480536565205b6050616c6c65743a3a72656a6563745f70726f706f73616c605d2e40617070726f76655f70726f706f73616c04012c70726f706f73616c5f6964e0013450726f706f73616c496e64657800020484536565205b6050616c6c65743a3a617070726f76655f70726f706f73616c605d2e147370656e64080118616d6f756e74bc013c42616c616e63654f663c542c20493e00012c62656e6566696369617279dd0101504163636f756e7449644c6f6f6b75704f663c543e00030458536565205b6050616c6c65743a3a7370656e64605d2e3c72656d6f76655f617070726f76616c04012c70726f706f73616c5f6964e0013450726f706f73616c496e64657800040480536565205b6050616c6c65743a3a72656d6f76655f617070726f76616c605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ef1010c2c70616c6c65745f7375646f1870616c6c65741043616c6c040454000110107375646f04011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00000454536565205b6050616c6c65743a3a7375646f605d2e547375646f5f756e636865636b65645f77656967687408011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00011877656967687424011857656967687400010498536565205b6050616c6c65743a3a7375646f5f756e636865636b65645f776569676874605d2e1c7365745f6b657904010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e00020460536565205b6050616c6c65743a3a7365745f6b6579605d2e1c7375646f5f617308010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00030460536565205b6050616c6c65743a3a7375646f5f6173605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ef5010c306f726d6c5f76657374696e67186d6f64756c651043616c6c04045400011014636c61696d00000458536565205b6050616c6c65743a3a636c61696d605d2e3c7665737465645f7472616e7366657208011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650001207363686564756c65b8015056657374696e675363686564756c654f663c543e00010480536565205b6050616c6c65743a3a7665737465645f7472616e73666572605d2e607570646174655f76657374696e675f7363686564756c657308010c77686fdd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500014476657374696e675f7363686564756c6573f90101645665633c56657374696e675363686564756c654f663c543e3e000204a4536565205b6050616c6c65743a3a7570646174655f76657374696e675f7363686564756c6573605d2e24636c61696d5f666f7204011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500030468536565205b6050616c6c65743a3a636c61696d5f666f72605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ef901000002b800fd010c306f726d6c5f78746f6b656e73186d6f64756c651043616c6c040454000118207472616e7366657210012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e6365000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400000464536565205b6050616c6c65743a3a7472616e73666572605d2e4c7472616e736665725f6d756c746961737365740c0114617373657425020160426f783c56657273696f6e65644d756c746941737365743e000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400010490536565205b6050616c6c65743a3a7472616e736665725f6d756c74696173736574605d2e447472616e736665725f776974685f66656514012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74180128543a3a42616c616e636500010c666565180128543a3a42616c616e6365000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400020488536565205b6050616c6c65743a3a7472616e736665725f776974685f666565605d2e707472616e736665725f6d756c746961737365745f776974685f666565100114617373657425020160426f783c56657273696f6e65644d756c746941737365743e00010c66656525020160426f783c56657273696f6e65644d756c746941737365743e000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d6974000304b4536565205b6050616c6c65743a3a7472616e736665725f6d756c746961737365745f776974685f666565605d2e607472616e736665725f6d756c746963757272656e6369657310012863757272656e63696573390201805665633c28543a3a43757272656e637949642c20543a3a42616c616e6365293e0001206665655f6974656d10010c753332000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d6974000404a4536565205b6050616c6c65743a3a7472616e736665725f6d756c746963757272656e63696573605d2e507472616e736665725f6d756c746961737365747310011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001206665655f6974656d10010c753332000110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000144646573745f7765696768745f6c696d69742102012c5765696768744c696d697400050494536565205b6050616c6c65743a3a7472616e736665725f6d756c7469617373657473605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e0102082c73746167696e675f78636d5856657273696f6e65644d756c74694c6f636174696f6e00010808563204000502014476323a3a4d756c74694c6f636174696f6e0001000856330400d4014476333a3a4d756c74694c6f636174696f6e000300000502102c73746167696e675f78636d087632346d756c74696c6f636174696f6e344d756c74694c6f636174696f6e000008011c706172656e74730801087538000120696e746572696f72090201244a756e6374696f6e7300000902102c73746167696e675f78636d087632346d756c74696c6f636174696f6e244a756e6374696f6e73000124104865726500000008583104000d0201204a756e6374696f6e00010008583208000d0201204a756e6374696f6e00000d0201204a756e6374696f6e0002000858330c000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00030008583410000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00040008583514000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00050008583618000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e0006000858371c000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00070008583820000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e00000d0201204a756e6374696f6e000800000d02102c73746167696e675f78636d087632206a756e6374696f6e204a756e6374696f6e0001242450617261636861696e0400e0010c7533320000002c4163636f756e744964333208011c6e6574776f726b110201244e6574776f726b496400010869640401205b75383b2033325d000100384163636f756e74496e646578363408011c6e6574776f726b110201244e6574776f726b4964000114696e64657828010c753634000200304163636f756e744b6579323008011c6e6574776f726b110201244e6574776f726b496400010c6b6579ec01205b75383b2032305d0003003850616c6c6574496e7374616e6365040008010875380004003047656e6572616c496e6465780400bc0110753132380005002847656e6572616c4b65790400150201805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e000600244f6e6c794368696c6400070024506c7572616c697479080108696419020118426f64794964000110706172741d020120426f6479506172740008000011020c2c73746167696e675f78636d087632244e6574776f726b49640001100c416e79000000144e616d65640400150201805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010020506f6c6b61646f74000200184b7573616d610003000015020c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e64656456656308045401080453000004003401185665633c543e000019020c2c73746167696e675f78636d08763218426f6479496400012810556e6974000000144e616d65640400150201805765616b426f756e6465645665633c75382c20436f6e73745533323c33323e3e00010014496e6465780400e0010c7533320002002445786563757469766500030024546563686e6963616c0004002c4c656769736c6174697665000500204a7564696369616c0006001c446566656e73650007003841646d696e697374726174696f6e000800205472656173757279000900001d020c2c73746167696e675f78636d08763220426f64795061727400011414566f6963650000001c4d656d62657273040114636f756e74e0010c753332000100204672616374696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c7533320002004441744c6561737450726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c753332000300484d6f72655468616e50726f706f7274696f6e08010c6e6f6de0010c75333200011464656e6f6de0010c7533320004000021020c2c73746167696e675f78636d0876332c5765696768744c696d697400010824556e6c696d697465640000001c4c696d697465640400240118576569676874000100002502082c73746167696e675f78636d4c56657273696f6e65644d756c7469417373657400010808563204002902013876323a3a4d756c746941737365740001000856330400cc013876333a3a4d756c74694173736574000300002902102c73746167696e675f78636d087632286d756c74696173736574284d756c74694173736574000008010869642d02011c4173736574496400010c66756e3102012c46756e676962696c69747900002d02102c73746167696e675f78636d087632286d756c746961737365741c4173736574496400010820436f6e63726574650400050201344d756c74694c6f636174696f6e000000204162737472616374040034011c5665633c75383e000100003102102c73746167696e675f78636d087632286d756c746961737365742c46756e676962696c6974790001082046756e6769626c650400bc0110753132380000002c4e6f6e46756e6769626c650400350201344173736574496e7374616e6365000100003502102c73746167696e675f78636d087632286d756c74696173736574344173736574496e7374616e636500011c24556e646566696e656400000014496e6465780400bc01107531323800010018417272617934040044011c5b75383b20345d0002001841727261793804000101011c5b75383b20385d0003001c417272617931360400050101205b75383b2031365d0004001c4172726179333204000401205b75383b2033325d00050010426c6f62040034011c5665633c75383e0006000039020000023d02003d02000004080d0118004102082c73746167696e675f78636d5056657273696f6e65644d756c746941737365747300010808563204004502013c76323a3a4d756c74694173736574730001000856330400c4013c76333a3a4d756c7469417373657473000300004502102c73746167696e675f78636d087632286d756c746961737365742c4d756c7469417373657473000004004902013c5665633c4d756c746941737365743e000049020000022902004d020c2c6f726d6c5f746f6b656e73186d6f64756c651043616c6c040454000114207472616e736665720c011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74bc0128543a3a42616c616e636500000464536565205b6050616c6c65743a3a7472616e73666572605d2e307472616e736665725f616c6c0c011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e637949640001286b6565705f616c69766535010110626f6f6c00010474536565205b6050616c6c65743a3a7472616e736665725f616c6c605d2e4c7472616e736665725f6b6565705f616c6976650c011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74bc0128543a3a42616c616e636500020490536565205b6050616c6c65743a3a7472616e736665725f6b6565705f616c697665605d2e38666f7263655f7472616e73666572100118736f75726365dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500011064657374dd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e63794964000118616d6f756e74bc0128543a3a42616c616e63650003047c536565205b6050616c6c65743a3a666f7263655f7472616e73666572605d2e2c7365745f62616c616e636510010c77686fdd01018c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500012c63757272656e63795f69640d010134543a3a43757272656e637949640001206e65775f66726565bc0128543a3a42616c616e63650001306e65775f7265736572766564bc0128543a3a42616c616e636500040470536565205b6050616c6c65743a3a7365745f62616c616e6365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e51020c3c70616c6c65745f6964656e746974791870616c6c65741043616c6c040454000148346164645f72656769737472617204011c6163636f756e74dd0101504163636f756e7449644c6f6f6b75704f663c543e00000478536565205b6050616c6c65743a3a6164645f726567697374726172605d2e307365745f6964656e74697479040110696e666f550201a4426f783c4964656e74697479496e666f3c543a3a4d61784164646974696f6e616c4669656c64733e3e00010474536565205b6050616c6c65743a3a7365745f6964656e74697479605d2e207365745f7375627304011073756273dd0201645665633c28543a3a4163636f756e7449642c2044617461293e00020464536565205b6050616c6c65743a3a7365745f73756273605d2e38636c6561725f6964656e746974790003047c536565205b6050616c6c65743a3a636c6561725f6964656e74697479605d2e44726571756573745f6a756467656d656e740801247265675f696e646578e00138526567697374726172496e64657800011c6d61785f666565bc013042616c616e63654f663c543e00040488536565205b6050616c6c65743a3a726571756573745f6a756467656d656e74605d2e3863616e63656c5f726571756573740401247265675f696e646578100138526567697374726172496e6465780005047c536565205b6050616c6c65743a3a63616e63656c5f72657175657374605d2e1c7365745f666565080114696e646578e00138526567697374726172496e64657800010c666565bc013042616c616e63654f663c543e00060460536565205b6050616c6c65743a3a7365745f666565605d2e387365745f6163636f756e745f6964080114696e646578e00138526567697374726172496e64657800010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e0007047c536565205b6050616c6c65743a3a7365745f6163636f756e745f6964605d2e287365745f6669656c6473080114696e646578e00138526567697374726172496e6465780001186669656c6473e50201384964656e746974794669656c64730008046c536565205b6050616c6c65743a3a7365745f6669656c6473605d2e4470726f766964655f6a756467656d656e741001247265675f696e646578e00138526567697374726172496e646578000118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e0001246a756467656d656e74ed02015c4a756467656d656e743c42616c616e63654f663c543e3e0001206964656e7469747930011c543a3a4861736800090488536565205b6050616c6c65743a3a70726f766964655f6a756467656d656e74605d2e346b696c6c5f6964656e74697479040118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e000a0478536565205b6050616c6c65743a3a6b696c6c5f6964656e74697479605d2e1c6164645f73756208010c737562dd0101504163636f756e7449644c6f6f6b75704f663c543e000110646174616102011044617461000b0460536565205b6050616c6c65743a3a6164645f737562605d2e2872656e616d655f73756208010c737562dd0101504163636f756e7449644c6f6f6b75704f663c543e000110646174616102011044617461000c046c536565205b6050616c6c65743a3a72656e616d655f737562605d2e2872656d6f76655f73756204010c737562dd0101504163636f756e7449644c6f6f6b75704f663c543e000d046c536565205b6050616c6c65743a3a72656d6f76655f737562605d2e20717569745f737562000e0464536565205b6050616c6c65743a3a717569745f737562605d2e5c666f7263655f696e736572745f6964656e7469746965730401286964656e746974696573f10201985665633c28543a3a4163636f756e7449642c20526567697374726174696f6e4f663c543e293e000f04a0536565205b6050616c6c65743a3a666f7263655f696e736572745f6964656e746974696573605d2e5c666f7263655f72656d6f76655f6964656e7469746965730401286964656e746974696573e50101445665633c543a3a4163636f756e7449643e001004a0536565205b6050616c6c65743a3a666f7263655f72656d6f76655f6964656e746974696573605d2e38666f7263655f7365745f7375627304011073756273090301785665633c5375624163636f756e747342794163636f756e7449643c543e3e0011047c536565205b6050616c6c65743a3a666f7263655f7365745f73756273605d2e04704964656e746974792070616c6c6574206465636c61726174696f6e2e55020c3c70616c6c65745f6964656e74697479147479706573304964656e74697479496e666f04284669656c644c696d697400002401286164646974696f6e616c59020190426f756e6465645665633c28446174612c2044617461292c204669656c644c696d69743e00011c646973706c617961020110446174610001146c6567616c610201104461746100010c776562610201104461746100011072696f746102011044617461000114656d61696c610201104461746100013c7067705f66696e6765727072696e74d90201404f7074696f6e3c5b75383b2032305d3e000114696d616765610201104461746100011c747769747465726102011044617461000059020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454015d02045300000400d50201185665633c543e00005d0200000408610261020061020c3c70616c6c65745f6964656e746974791474797065731044617461000198104e6f6e650000001052617730040065020000010010526177310400690200000200105261773204006d0200000300105261773304007102000004001052617734040044000005001052617735040075020000060010526177360400790200000700105261773704007d02000008001052617738040001010000090010526177390400810200000a001452617731300400850200000b001452617731310400890200000c0014526177313204008d0200000d001452617731330400910200000e001452617731340400950200000f001452617731350400990200001000145261773136040005010000110014526177313704009d02000012001452617731380400a102000013001452617731390400a502000014001452617732300400ec000015001452617732310400a902000016001452617732320400ad02000017001452617732330400b102000018001452617732340400b502000019001452617732350400b90200001a001452617732360400bd0200001b001452617732370400c10200001c001452617732380400c50200001d001452617732390400c90200001e001452617733300400cd0200001f001452617733310400d10200002000145261773332040004000021002c426c616b6554776f323536040004000022001853686132353604000400002300244b656363616b323536040004000024002c53686154687265653235360400040000250000650200000300000000080069020000030100000008006d020000030200000008007102000003030000000800750200000305000000080079020000030600000008007d02000003070000000800810200000309000000080085020000030a000000080089020000030b00000008008d020000030c000000080091020000030d000000080095020000030e000000080099020000030f00000008009d02000003110000000800a102000003120000000800a502000003130000000800a902000003150000000800ad02000003160000000800b102000003170000000800b502000003180000000800b902000003190000000800bd020000031a0000000800c1020000031b0000000800c5020000031c0000000800c9020000031d0000000800cd020000031e0000000800d1020000031f0000000800d5020000025d0200d90204184f7074696f6e04045401ec0108104e6f6e6500000010536f6d650400ec0000010000dd02000002e10200e1020000040800610200e5020c3c70616c6c65745f6964656e7469747914747970657320426974466c61677304045401e9020004002c01344964656e746974794669656c640000e9020c3c70616c6c65745f6964656e74697479147479706573344964656e746974794669656c640001201c446973706c6179000100144c6567616c0002000c5765620004001052696f7400080014456d61696c0010003850677046696e6765727072696e7400200014496d6167650040001c5477697474657200800000ed020c3c70616c6c65745f6964656e74697479147479706573244a756467656d656e74041c42616c616e63650118011c1c556e6b6e6f776e0000001c46656550616964040018011c42616c616e636500010028526561736f6e61626c65000200244b6e6f776e476f6f64000300244f75744f6644617465000400284c6f775175616c697479000500244572726f6e656f757300060000f102000002f50200f5020000040800f90200f9020c3c70616c6c65745f6964656e7469747914747970657330526567697374726174696f6e0c1c42616c616e63650118344d61784a756467656d656e7473004c4d61784164646974696f6e616c4669656c647300000c01286a756467656d656e7473fd0201fc426f756e6465645665633c28526567697374726172496e6465782c204a756467656d656e743c42616c616e63653e292c204d61784a756467656d656e74733e00011c6465706f73697418011c42616c616e6365000110696e666f550201844964656e74697479496e666f3c4d61784164646974696f6e616c4669656c64733e0000fd020c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454010103045300000400050301185665633c543e000001030000040810ed0200050300000201030009030000020d03000d0300000408001103001103000004081815030015030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e102045300000400dd0201185665633c543e000019030c3c70616c6c65745f707265696d6167651870616c6c65741043616c6c040454000110346e6f74655f707265696d616765040114627974657334011c5665633c75383e00000478536565205b6050616c6c65743a3a6e6f74655f707265696d616765605d2e3c756e6e6f74655f707265696d6167650401106861736830011c543a3a4861736800010480536565205b6050616c6c65743a3a756e6e6f74655f707265696d616765605d2e40726571756573745f707265696d6167650401106861736830011c543a3a4861736800020484536565205b6050616c6c65743a3a726571756573745f707265696d616765605d2e48756e726571756573745f707265696d6167650401106861736830011c543a3a486173680003048c536565205b6050616c6c65743a3a756e726571756573745f707265696d616765605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e1d030c4070616c6c65745f64656d6f63726163791870616c6c65741043616c6c04045400014c1c70726f706f736508012070726f706f73616c59010140426f756e64656443616c6c4f663c543e00011476616c7565bc013042616c616e63654f663c543e00000460536565205b6050616c6c65743a3a70726f706f7365605d2e187365636f6e6404012070726f706f73616ce0012450726f70496e6465780001045c536565205b6050616c6c65743a3a7365636f6e64605d2e10766f74650801247265665f696e646578e0013c5265666572656e64756d496e646578000110766f7465250101644163636f756e74566f74653c42616c616e63654f663c543e3e00020454536565205b6050616c6c65743a3a766f7465605d2e40656d657267656e63795f63616e63656c0401247265665f696e64657810013c5265666572656e64756d496e64657800030484536565205b6050616c6c65743a3a656d657267656e63795f63616e63656c605d2e4065787465726e616c5f70726f706f736504012070726f706f73616c59010140426f756e64656443616c6c4f663c543e00040484536565205b6050616c6c65743a3a65787465726e616c5f70726f706f7365605d2e6465787465726e616c5f70726f706f73655f6d616a6f7269747904012070726f706f73616c59010140426f756e64656443616c6c4f663c543e000504a8536565205b6050616c6c65743a3a65787465726e616c5f70726f706f73655f6d616a6f72697479605d2e6065787465726e616c5f70726f706f73655f64656661756c7404012070726f706f73616c59010140426f756e64656443616c6c4f663c543e000604a4536565205b6050616c6c65743a3a65787465726e616c5f70726f706f73655f64656661756c74605d2e28666173745f747261636b0c013470726f706f73616c5f6861736830011048323536000134766f74696e675f706572696f64100144426c6f636b4e756d626572466f723c543e00011464656c6179100144426c6f636b4e756d626572466f723c543e0007046c536565205b6050616c6c65743a3a666173745f747261636b605d2e347665746f5f65787465726e616c04013470726f706f73616c5f686173683001104832353600080478536565205b6050616c6c65743a3a7665746f5f65787465726e616c605d2e4463616e63656c5f7265666572656e64756d0401247265665f696e646578e0013c5265666572656e64756d496e64657800090488536565205b6050616c6c65743a3a63616e63656c5f7265666572656e64756d605d2e2064656c65676174650c0108746fdd0101504163636f756e7449644c6f6f6b75704f663c543e000128636f6e76696374696f6e21030128436f6e76696374696f6e00011c62616c616e636518013042616c616e63654f663c543e000a0464536565205b6050616c6c65743a3a64656c6567617465605d2e28756e64656c6567617465000b046c536565205b6050616c6c65743a3a756e64656c6567617465605d2e58636c6561725f7075626c69635f70726f706f73616c73000c049c536565205b6050616c6c65743a3a636c6561725f7075626c69635f70726f706f73616c73605d2e18756e6c6f636b040118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e000d045c536565205b6050616c6c65743a3a756e6c6f636b605d2e2c72656d6f76655f766f7465040114696e64657810013c5265666572656e64756d496e646578000e0470536565205b6050616c6c65743a3a72656d6f76655f766f7465605d2e4472656d6f76655f6f746865725f766f7465080118746172676574dd0101504163636f756e7449644c6f6f6b75704f663c543e000114696e64657810013c5265666572656e64756d496e646578000f0488536565205b6050616c6c65743a3a72656d6f76655f6f746865725f766f7465605d2e24626c61636b6c69737408013470726f706f73616c5f686173683001104832353600013c6d617962655f7265665f696e6465782503015c4f7074696f6e3c5265666572656e64756d496e6465783e00100468536565205b6050616c6c65743a3a626c61636b6c697374605d2e3c63616e63656c5f70726f706f73616c04012870726f705f696e646578e0012450726f70496e64657800110480536565205b6050616c6c65743a3a63616e63656c5f70726f706f73616c605d2e307365745f6d657461646174610801146f776e65722d0101344d657461646174614f776e65720001286d617962655f68617368290301504f7074696f6e3c507265696d616765486173683e00120474536565205b6050616c6c65743a3a7365745f6d65746164617461605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e21030c4070616c6c65745f64656d6f637261637928636f6e76696374696f6e28436f6e76696374696f6e00011c104e6f6e65000000204c6f636b65643178000100204c6f636b65643278000200204c6f636b65643378000300204c6f636b65643478000400204c6f636b65643578000500204c6f636b6564367800060000250304184f7074696f6e04045401100108104e6f6e6500000010536f6d650400100000010000290304184f7074696f6e04045401300108104e6f6e6500000010536f6d6504003000000100002d030c4470616c6c65745f636f6c6c6563746976651870616c6c65741043616c6c0804540004490001182c7365745f6d656d626572730c012c6e65775f6d656d62657273e50101445665633c543a3a4163636f756e7449643e0001147072696d65b001504f7074696f6e3c543a3a4163636f756e7449643e0001246f6c645f636f756e7410012c4d656d626572436f756e7400000470536565205b6050616c6c65743a3a7365745f6d656d62657273605d2e1c6578656375746508012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200010460536565205b6050616c6c65743a3a65786563757465605d2e1c70726f706f73650c01247468726573686f6c64e0012c4d656d626572436f756e7400012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200020460536565205b6050616c6c65743a3a70726f706f7365605d2e10766f74650c012070726f706f73616c30011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800011c617070726f766535010110626f6f6c00030454536565205b6050616c6c65743a3a766f7465605d2e4c646973617070726f76655f70726f706f73616c04013470726f706f73616c5f6861736830011c543a3a4861736800050490536565205b6050616c6c65743a3a646973617070726f76655f70726f706f73616c605d2e14636c6f736510013470726f706f73616c5f6861736830011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800015470726f706f73616c5f7765696768745f626f756e642401185765696768740001306c656e6774685f626f756e64e0010c75333200060458536565205b6050616c6c65743a3a636c6f7365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e31030c4470616c6c65745f636f6c6c6563746976651870616c6c65741043616c6c0804540004490001182c7365745f6d656d626572730c012c6e65775f6d656d62657273e50101445665633c543a3a4163636f756e7449643e0001147072696d65b001504f7074696f6e3c543a3a4163636f756e7449643e0001246f6c645f636f756e7410012c4d656d626572436f756e7400000470536565205b6050616c6c65743a3a7365745f6d656d62657273605d2e1c6578656375746508012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200010460536565205b6050616c6c65743a3a65786563757465605d2e1c70726f706f73650c01247468726573686f6c64e0012c4d656d626572436f756e7400012070726f706f73616c5d01017c426f783c3c5420617320436f6e6669673c493e3e3a3a50726f706f73616c3e0001306c656e6774685f626f756e64e0010c75333200020460536565205b6050616c6c65743a3a70726f706f7365605d2e10766f74650c012070726f706f73616c30011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800011c617070726f766535010110626f6f6c00030454536565205b6050616c6c65743a3a766f7465605d2e4c646973617070726f76655f70726f706f73616c04013470726f706f73616c5f6861736830011c543a3a4861736800050490536565205b6050616c6c65743a3a646973617070726f76655f70726f706f73616c605d2e14636c6f736510013470726f706f73616c5f6861736830011c543a3a48617368000114696e646578e0013450726f706f73616c496e64657800015470726f706f73616c5f7765696768745f626f756e642401185765696768740001306c656e6774685f626f756e64e0010c75333200060458536565205b6050616c6c65743a3a636c6f7365605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e35030c4470616c6c65745f6d656d626572736869701870616c6c65741043616c6c08045400044900011c286164645f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0000046c536565205b6050616c6c65743a3a6164645f6d656d626572605d2e3472656d6f76655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00010478536565205b6050616c6c65743a3a72656d6f76655f6d656d626572605d2e2c737761705f6d656d62657208011872656d6f7665dd0101504163636f756e7449644c6f6f6b75704f663c543e00010c616464dd0101504163636f756e7449644c6f6f6b75704f663c543e00020470536565205b6050616c6c65743a3a737761705f6d656d626572605d2e3472657365745f6d656d6265727304011c6d656d62657273e50101445665633c543a3a4163636f756e7449643e00030478536565205b6050616c6c65743a3a72657365745f6d656d62657273605d2e286368616e67655f6b657904010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e0004046c536565205b6050616c6c65743a3a6368616e67655f6b6579605d2e247365745f7072696d6504010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00050468536565205b6050616c6c65743a3a7365745f7072696d65605d2e2c636c6561725f7072696d6500060470536565205b6050616c6c65743a3a636c6561725f7072696d65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e39030c4470616c6c65745f6d656d626572736869701870616c6c65741043616c6c08045400044900011c286164645f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0000046c536565205b6050616c6c65743a3a6164645f6d656d626572605d2e3472656d6f76655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00010478536565205b6050616c6c65743a3a72656d6f76655f6d656d626572605d2e2c737761705f6d656d62657208011872656d6f7665dd0101504163636f756e7449644c6f6f6b75704f663c543e00010c616464dd0101504163636f756e7449644c6f6f6b75704f663c543e00020470536565205b6050616c6c65743a3a737761705f6d656d626572605d2e3472657365745f6d656d6265727304011c6d656d62657273e50101445665633c543a3a4163636f756e7449643e00030478536565205b6050616c6c65743a3a72657365745f6d656d62657273605d2e286368616e67655f6b657904010c6e6577dd0101504163636f756e7449644c6f6f6b75704f663c543e0004046c536565205b6050616c6c65743a3a6368616e67655f6b6579605d2e247365745f7072696d6504010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00050468536565205b6050616c6c65743a3a7365745f7072696d65605d2e2c636c6561725f7072696d6500060470536565205b6050616c6c65743a3a636c6561725f7072696d65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e3d030c6070616c6c65745f72616e6b65645f636f6c6c6563746976651870616c6c65741043616c6c080454000449000118286164645f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0000046c536565205b6050616c6c65743a3a6164645f6d656d626572605d2e3870726f6d6f74655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001047c536565205b6050616c6c65743a3a70726f6d6f74655f6d656d626572605d2e3464656d6f74655f6d656d62657204010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e00020478536565205b6050616c6c65743a3a64656d6f74655f6d656d626572605d2e3472656d6f76655f6d656d62657208010c77686fdd0101504163636f756e7449644c6f6f6b75704f663c543e0001206d696e5f72616e6b4901011052616e6b00030478536565205b6050616c6c65743a3a72656d6f76655f6d656d626572605d2e10766f7465080110706f6c6c100144506f6c6c496e6465784f663c542c20493e00010c61796535010110626f6f6c00040454536565205b6050616c6c65743a3a766f7465605d2e30636c65616e75705f706f6c6c080128706f6c6c5f696e646578100144506f6c6c496e6465784f663c542c20493e00010c6d617810010c75333200050474536565205b6050616c6c65743a3a636c65616e75705f706f6c6c605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e41030c4070616c6c65745f7265666572656e64611870616c6c65741043616c6c080454000449000124187375626d69740c013c70726f706f73616c5f6f726967696e4503015c426f783c50616c6c6574734f726967696e4f663c543e3e00012070726f706f73616c5901014c426f756e64656443616c6c4f663c542c20493e000140656e6163746d656e745f6d6f6d656e746d03017c446973706174636854696d653c426c6f636b4e756d626572466f723c543e3e0000045c536565205b6050616c6c65743a3a7375626d6974605d2e58706c6163655f6465636973696f6e5f6465706f736974040114696e64657810013c5265666572656e64756d496e6465780001049c536565205b6050616c6c65743a3a706c6163655f6465636973696f6e5f6465706f736974605d2e5c726566756e645f6465636973696f6e5f6465706f736974040114696e64657810013c5265666572656e64756d496e646578000204a0536565205b6050616c6c65743a3a726566756e645f6465636973696f6e5f6465706f736974605d2e1863616e63656c040114696e64657810013c5265666572656e64756d496e6465780003045c536565205b6050616c6c65743a3a63616e63656c605d2e106b696c6c040114696e64657810013c5265666572656e64756d496e64657800040454536565205b6050616c6c65743a3a6b696c6c605d2e406e756467655f7265666572656e64756d040114696e64657810013c5265666572656e64756d496e64657800050484536565205b6050616c6c65743a3a6e756467655f7265666572656e64756d605d2e486f6e655f66657765725f6465636964696e67040114747261636b4901013c547261636b49644f663c542c20493e0006048c536565205b6050616c6c65743a3a6f6e655f66657765725f6465636964696e67605d2e64726566756e645f7375626d697373696f6e5f6465706f736974040114696e64657810013c5265666572656e64756d496e646578000704a8536565205b6050616c6c65743a3a726566756e645f7375626d697373696f6e5f6465706f736974605d2e307365745f6d65746164617461080114696e64657810013c5265666572656e64756d496e6465780001286d617962655f68617368290301504f7074696f6e3c507265696d616765486173683e00080474536565205b6050616c6c65743a3a7365745f6d65746164617461605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e450308306f70616c5f72756e74696d65304f726967696e43616c6c65720001201873797374656d0400490301746672616d655f73797374656d3a3a4f726967696e3c52756e74696d653e0000001c436f756e63696c04004d0301010170616c6c65745f636f6c6c6563746976653a3a4f726967696e3c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365313e002b0048546563686e6963616c436f6d6d69747465650400510301010170616c6c65745f636f6c6c6563746976653a3a4f726967696e3c52756e74696d652c2070616c6c65745f636f6c6c6563746976653a3a496e7374616e6365323e002c001c4f726967696e7304005503016870616c6c65745f676f765f6f726967696e733a3a4f726967696e0063002c506f6c6b61646f7458636d04005903014870616c6c65745f78636d3a3a4f726967696e0033002843756d756c757358636d04005d03016863756d756c75735f70616c6c65745f78636d3a3a4f726967696e00340020457468657265756d04006103015c70616c6c65745f657468657265756d3a3a4f726967696e00650010566f69640400690301410173656c663a3a73705f6170695f68696464656e5f696e636c756465735f636f6e7374727563745f72756e74696d653a3a68696464656e5f696e636c7564653a3a0a5f5f707269766174653a3a566f69640007000049030c346672616d655f737570706f7274206469737061746368245261774f726967696e04244163636f756e7449640100010c10526f6f74000000185369676e656404000001244163636f756e744964000100104e6f6e65000200004d03084470616c6c65745f636f6c6c656374697665245261774f726967696e08244163636f756e7449640100044900010c1c4d656d62657273080010012c4d656d626572436f756e74000010012c4d656d626572436f756e74000000184d656d62657204000001244163636f756e744964000100205f5068616e746f6d000200005103084470616c6c65745f636f6c6c656374697665245261774f726967696e08244163636f756e7449640100044900010c1c4d656d62657273080010012c4d656d626572436f756e74000010012c4d656d626572436f756e74000000184d656d62657204000001244163636f756e744964000100205f5068616e746f6d0002000055030c4870616c6c65745f676f765f6f726967696e731870616c6c6574184f726967696e0001045446656c6c6f777368697050726f706f736974696f6e0000000059030c2870616c6c65745f78636d1870616c6c6574184f726967696e0001080c58636d0400d401344d756c74694c6f636174696f6e00000020526573706f6e73650400d401344d756c74694c6f636174696f6e000100005d030c4863756d756c75735f70616c6c65745f78636d1870616c6c6574184f726967696e0001081452656c6179000000405369626c696e6750617261636861696e0400ad010118506172614964000100006103083c70616c6c65745f657468657265756d245261774f726967696e0001044c457468657265756d5472616e73616374696f6e04006503011048313630000000006503083c7072696d69746976655f7479706573104831363000000400ec01205b75383b2032305d00006903081c73705f636f726510566f6964000100006d0310346672616d655f737570706f727418747261697473207363686564756c6530446973706174636854696d65042c426c6f636b4e756d62657201100108084174040010012c426c6f636b4e756d626572000000144166746572040010012c426c6f636b4e756d6265720001000071030c4070616c6c65745f7363686564756c65721870616c6c65741043616c6c040454000118207363686564756c651001107768656e100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00000464536565205b6050616c6c65743a3a7363686564756c65605d2e1863616e63656c0801107768656e100144426c6f636b4e756d626572466f723c543e000114696e64657810010c7533320001045c536565205b6050616c6c65743a3a63616e63656c605d2e387363686564756c655f6e616d656414010869640401205461736b4e616d650001107768656e100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e0002047c536565205b6050616c6c65743a3a7363686564756c655f6e616d6564605d2e3063616e63656c5f6e616d656404010869640401205461736b4e616d6500030474536565205b6050616c6c65743a3a63616e63656c5f6e616d6564605d2e387363686564756c655f61667465721001146166746572100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e0004047c536565205b6050616c6c65743a3a7363686564756c655f6166746572605d2e507363686564756c655f6e616d65645f616674657214010869640401205461736b4e616d650001146166746572100144426c6f636b4e756d626572466f723c543e0001386d617962655f706572696f646963750301ac4f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d626572466f723c543e3e3e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00050494536565205b6050616c6c65743a3a7363686564756c655f6e616d65645f6166746572605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e750304184f7074696f6e0404540179030108104e6f6e6500000010536f6d650400790300000100007903000004081010007d030c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c65741043616c6c04045400012448736572766963655f6f766572776569676874080114696e6465782c013c4f766572776569676874496e6465780001307765696768745f6c696d69742401185765696768740000048c536565205b6050616c6c65743a3a736572766963655f6f766572776569676874605d2e5473757370656e645f78636d5f657865637574696f6e00010498536565205b6050616c6c65743a3a73757370656e645f78636d5f657865637574696f6e605d2e50726573756d655f78636d5f657865637574696f6e00020494536565205b6050616c6c65743a3a726573756d655f78636d5f657865637574696f6e605d2e607570646174655f73757370656e645f7468726573686f6c6404010c6e657710010c753332000304a4536565205b6050616c6c65743a3a7570646174655f73757370656e645f7468726573686f6c64605d2e547570646174655f64726f705f7468726573686f6c6404010c6e657710010c75333200040498536565205b6050616c6c65743a3a7570646174655f64726f705f7468726573686f6c64605d2e5c7570646174655f726573756d655f7468726573686f6c6404010c6e657710010c753332000504a0536565205b6050616c6c65743a3a7570646174655f726573756d655f7468726573686f6c64605d2e5c7570646174655f7468726573686f6c645f77656967687404010c6e6577240118576569676874000604a0536565205b6050616c6c65743a3a7570646174655f7468726573686f6c645f776569676874605d2e707570646174655f7765696768745f72657374726963745f646563617904010c6e6577240118576569676874000704b4536565205b6050616c6c65743a3a7570646174655f7765696768745f72657374726963745f6465636179605d2e847570646174655f78636d705f6d61785f696e646976696475616c5f77656967687404010c6e6577240118576569676874000804c8536565205b6050616c6c65743a3a7570646174655f78636d705f6d61785f696e646976696475616c5f776569676874605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e81030c2870616c6c65745f78636d1870616c6c65741043616c6c04045400012c1073656e64080110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011c6d65737361676585030154426f783c56657273696f6e656458636d3c28293e3e00000454536565205b6050616c6c65743a3a73656e64605d2e3c74656c65706f72745f617373657473100110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c75333200010480536565205b6050616c6c65743a3a74656c65706f72745f617373657473605d2e5c726573657276655f7472616e736665725f617373657473100110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c753332000204a0536565205b6050616c6c65743a3a726573657276655f7472616e736665725f617373657473605d2e1c6578656375746508011c6d657373616765050401c0426f783c56657273696f6e656458636d3c3c5420617320537973436f6e6669673e3a3a52756e74696d6543616c6c3e3e0001286d61785f77656967687424011857656967687400030460536565205b6050616c6c65743a3a65786563757465605d2e44666f7263655f78636d5f76657273696f6e0801206c6f636174696f6ed40148426f783c4d756c74694c6f636174696f6e3e00011c76657273696f6e10012858636d56657273696f6e00040488536565205b6050616c6c65743a3a666f7263655f78636d5f76657273696f6e605d2e64666f7263655f64656661756c745f78636d5f76657273696f6e0401446d617962655f78636d5f76657273696f6e250301484f7074696f6e3c58636d56657273696f6e3e000504a8536565205b6050616c6c65743a3a666f7263655f64656661756c745f78636d5f76657273696f6e605d2e78666f7263655f7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000604bc536565205b6050616c6c65743a3a666f7263655f7375627363726962655f76657273696f6e5f6e6f74696679605d2e80666f7263655f756e7375627363726962655f76657273696f6e5f6e6f746966790401206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e000704c4536565205b6050616c6c65743a3a666f7263655f756e7375627363726962655f76657273696f6e5f6e6f74696679605d2e7c6c696d697465645f726573657276655f7472616e736665725f617373657473140110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69742102012c5765696768744c696d6974000804c0536565205b6050616c6c65743a3a6c696d697465645f726573657276655f7472616e736665725f617373657473605d2e5c6c696d697465645f74656c65706f72745f617373657473140110646573740102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00012c62656e65666963696172790102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e00011861737365747341020164426f783c56657273696f6e65644d756c74694173736574733e0001386665655f61737365745f6974656d10010c7533320001307765696768745f6c696d69742102012c5765696768744c696d6974000904a0536565205b6050616c6c65743a3a6c696d697465645f74656c65706f72745f617373657473605d2e40666f7263655f73757370656e73696f6e04012473757370656e64656435010110626f6f6c000a0484536565205b6050616c6c65743a3a666f7263655f73757370656e73696f6e605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e8503082c73746167696e675f78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010808563204008903015076323a3a58636d3c52756e74696d6543616c6c3e0002000856330400bd03015076333a3a58636d3c52756e74696d6543616c6c3e0003000089030c2c73746167696e675f78636d0876320c58636d042c52756e74696d6543616c6c000004008d0301745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e00008d0300000291030091030c2c73746167696e675f78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404004502012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404004502012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404004502012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f696428011c51756572794964000120726573706f6e736595030120526573706f6e73650001286d61785f77656967687428010c753634000300345472616e7366657241737365740801186173736574734502012c4d756c746941737365747300012c62656e6566696369617279050201344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574734502012c4d756c746941737365747300011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428010c75363400011063616c6ca9030168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e040009020154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f77656967687428010c753634000c00304465706f73697441737365740c0118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200012c62656e6566696369617279050201344d756c74694c6f636174696f6e000d004c4465706f736974526573657276654173736574100118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000e003445786368616e6765417373657408011067697665ad0301404d756c7469417373657446696c74657200011c726563656976654502012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c0118617373657473ad0301404d756c7469417373657446696c74657200011c72657365727665050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473ad0301404d756c7469417373657446696c74657200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e000118617373657473ad0301404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f77656967687428010c75363400120030427579457865637574696f6e08011066656573290201284d756c746941737365740001307765696768745f6c696d6974b903012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204008903014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e64697804008903014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574734502012c4d756c74694173736574730001187469636b6574050201344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f77656967687428010c753634001a0048556e73756273637269626556657273696f6e001b000095030c2c73746167696e675f78636d08763220526573706f6e7365000110104e756c6c0000001841737365747304004502012c4d756c74694173736574730001003c457865637574696f6e526573756c740400990301504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e00030000990304184f7074696f6e040454019d030108104e6f6e6500000010536f6d6504009d0300000100009d030000040810a10300a103102c73746167696e675f78636d08763218747261697473144572726f72000168204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300444d756c74694c6f636174696f6e46756c6c000400684d756c74694c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e73697665001400105472617004002c010c7536340015004c556e68616e646c656458636d56657273696f6e001600485765696768744c696d69745265616368656404002c01185765696768740017001c426172726965720018004c5765696768744e6f74436f6d70757461626c6500190000a5030c2c73746167696e675f78636d087632284f726967696e4b696e64000110184e617469766500000040536f7665726569676e4163636f756e74000100245375706572757365720002000c58636d00030000a9030c2c73746167696e675f78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656434011c5665633c75383e0000ad03102c73746167696e675f78636d087632286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e69746504004502012c4d756c74694173736574730000001057696c640400b103013857696c644d756c7469417373657400010000b103102c73746167696e675f78636d087632286d756c746961737365743857696c644d756c746941737365740001080c416c6c00000014416c6c4f6608010869642d02011c4173736574496400010c66756eb503013c57696c6446756e676962696c69747900010000b503102c73746167696e675f78636d087632286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c6500010000b9030c2c73746167696e675f78636d0876322c5765696768744c696d697400010824556e6c696d697465640000001c4c696d69746564040028010c75363400010000bd030c2c73746167696e675f78636d0876330c58636d041043616c6c00000400c10301585665633c496e737472756374696f6e3c43616c6c3e3e0000c103000002c50300c5030c2c73746167696e675f78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400c4012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400c4012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400c4012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f696428011c51756572794964000120726573706f6e7365c9030120526573706f6e73650001286d61785f77656967687424011857656967687400011c71756572696572f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473c4012c4d756c746941737365747300012c62656e6566696369617279d401344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473c4012c4d756c746941737365747300011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737424011857656967687400011063616c6ca903014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400d80154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400f50301445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473f90301404d756c7469417373657446696c74657200012c62656e6566696369617279d401344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000e003445786368616e676541737365740c011067697665f90301404d756c7469417373657446696c74657200011077616e74c4012c4d756c746941737365747300011c6d6178696d616c35010110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473f90301404d756c7469417373657446696c74657200011c72657365727665d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f000118617373657473f90301404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573cc01284d756c746941737365740001307765696768745f6c696d69742102012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c65720400bd03012458636d3c43616c6c3e0015002c536574417070656e6469780400bd03012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473c4012c4d756c74694173736574730001187469636b6574d401344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f776569676874240118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400c4012c4d756c7469417373657473001c002c45787065637441737365740400c4012c4d756c7469417373657473001d00304578706563744f726967696e0400f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400cd0301504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e736163745374617475730400e90301384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6534011c5665633c75383e000134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578e0010c7533320001106e616d6534011c5665633c75383e00012c6d6f64756c655f6e616d6534011c5665633c75383e00012c63726174655f6d616a6f72e0010c75333200013c6d696e5f63726174655f6d696e6f72e0010c753332002200505265706f72745472616e736163745374617475730400f50301445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400dc01204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726be801244e6574776f726b496400012c64657374696e6174696f6ed80154496e746572696f724d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e002600244c6f636b41737365740801146173736574cc01284d756c74694173736574000120756e6c6f636b6572d401344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574cc01284d756c74694173736574000118746172676574d401344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574cc01284d756c746941737365740001146f776e6572d401344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574cc01284d756c746941737365740001186c6f636b6572d401344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f776974686472617735010110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400d401344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69742102012c5765696768744c696d6974000130636865636b5f6f726967696ef10301544f7074696f6e3c4d756c74694c6f636174696f6e3e002f0000c9030c2c73746167696e675f78636d08763320526573706f6e7365000118104e756c6c000000184173736574730400c4012c4d756c74694173736574730001003c457865637574696f6e526573756c740400cd0301504f7074696f6e3c287533322c204572726f72293e0002001c56657273696f6e040010013873757065723a3a56657273696f6e0003002c50616c6c657473496e666f0400d9030198426f756e6465645665633c50616c6c6574496e666f2c204d617850616c6c657473496e666f3e000400384469737061746368526573756c740400e90301384d617962654572726f72436f646500050000cd0304184f7074696f6e04045401d1030108104e6f6e6500000010536f6d650400d1030000010000d1030000040810d50300d503102c73746167696e675f78636d08763318747261697473144572726f720001a0204f766572666c6f7700000034556e696d706c656d656e74656400010060556e74727573746564526573657276654c6f636174696f6e00020064556e7472757374656454656c65706f72744c6f636174696f6e000300304c6f636174696f6e46756c6c000400544c6f636174696f6e4e6f74496e7665727469626c65000500244261644f726967696e0006003c496e76616c69644c6f636174696f6e0007003441737365744e6f74466f756e64000800544661696c6564546f5472616e7361637441737365740009003c4e6f74576974686472617761626c65000a00484c6f636174696f6e43616e6e6f74486f6c64000b0054457863656564734d61784d65737361676553697a65000c005844657374696e6174696f6e556e737570706f72746564000d00245472616e73706f7274000e0028556e726f757461626c65000f0030556e6b6e6f776e436c61696d001000384661696c6564546f4465636f6465001100404d6178576569676874496e76616c6964001200384e6f74486f6c64696e674665657300130030546f6f457870656e73697665001400105472617004002c010c753634001500404578706563746174696f6e46616c73650016003850616c6c65744e6f74466f756e64001700304e616d654d69736d617463680018004c56657273696f6e496e636f6d70617469626c6500190050486f6c64696e67576f756c644f766572666c6f77001a002c4578706f72744572726f72001b00385265616e63686f724661696c6564001c00184e6f4465616c001d0028466565734e6f744d6574001e00244c6f636b4572726f72001f00304e6f5065726d697373696f6e00200028556e616e63686f726564002100384e6f744465706f73697461626c650022004c556e68616e646c656458636d56657273696f6e002300485765696768744c696d69745265616368656404002401185765696768740024001c426172726965720025004c5765696768744e6f74436f6d70757461626c650026004445786365656473537461636b4c696d697400270000d9030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401dd03045300000400e50301185665633c543e0000dd030c2c73746167696e675f78636d0876332850616c6c6574496e666f0000180114696e646578e0010c7533320001106e616d65e1030180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e00012c6d6f64756c655f6e616d65e1030180426f756e6465645665633c75382c204d617850616c6c65744e616d654c656e3e0001146d616a6f72e0010c7533320001146d696e6f72e0010c7533320001147061746368e0010c7533320000e1030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000e503000002dd0300e9030c2c73746167696e675f78636d087633384d617962654572726f72436f646500010c1c53756363657373000000144572726f720400ed03018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e000100385472756e63617465644572726f720400ed03018c426f756e6465645665633c75382c204d617844697370617463684572726f724c656e3e00020000ed030c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000f10304184f7074696f6e04045401d40108104e6f6e6500000010536f6d650400d40000010000f5030c2c73746167696e675f78636d087633445175657279526573706f6e7365496e666f00000c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e00012071756572795f696428011c517565727949640001286d61785f7765696768742401185765696768740000f903102c73746167696e675f78636d087633286d756c74696173736574404d756c7469417373657446696c74657200010820446566696e6974650400c4012c4d756c74694173736574730000001057696c640400fd03013857696c644d756c7469417373657400010000fd03102c73746167696e675f78636d087633286d756c746961737365743857696c644d756c746941737365740001100c416c6c00000014416c6c4f660801086964d0011c4173736574496400010c66756e0104013c57696c6446756e676962696c69747900010028416c6c436f756e7465640400e0010c75333200020030416c6c4f66436f756e7465640c01086964d0011c4173736574496400010c66756e0104013c57696c6446756e676962696c697479000114636f756e74e0010c753332000300000104102c73746167696e675f78636d087633286d756c746961737365743c57696c6446756e676962696c6974790001082046756e6769626c650000002c4e6f6e46756e6769626c65000100000504082c73746167696e675f78636d3056657273696f6e656458636d042c52756e74696d6543616c6c00010808563204000904015076323a3a58636d3c52756e74696d6543616c6c3e00020008563304001904015076333a3a58636d3c52756e74696d6543616c6c3e0003000009040c2c73746167696e675f78636d0876320c58636d042c52756e74696d6543616c6c000004000d0401745665633c496e737472756374696f6e3c52756e74696d6543616c6c3e3e00000d0400000211040011040c2c73746167696e675f78636d0876322c496e737472756374696f6e042c52756e74696d6543616c6c000170345769746864726177417373657404004502012c4d756c7469417373657473000000545265736572766541737365744465706f736974656404004502012c4d756c7469417373657473000100585265636569766554656c65706f72746564417373657404004502012c4d756c7469417373657473000200345175657279526573706f6e73650c012071756572795f696428011c51756572794964000120726573706f6e736595030120526573706f6e73650001286d61785f77656967687428010c753634000300345472616e7366657241737365740801186173736574734502012c4d756c746941737365747300012c62656e6566696369617279050201344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c01186173736574734502012c4d756c746941737365747300011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f74797065a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737428010c75363400011063616c6c15040168446f75626c65456e636f6465643c52756e74696d6543616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e040009020154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720c012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e00014c6d61785f726573706f6e73655f77656967687428010c753634000c00304465706f73697441737365740c0118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200012c62656e6566696369617279050201344d756c74694c6f636174696f6e000d004c4465706f736974526573657276654173736574100118617373657473ad0301404d756c7469417373657446696c7465720001286d61785f617373657473e0010c75333200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e000e003445786368616e6765417373657408011067697665ad0301404d756c7469417373657446696c74657200011c726563656976654502012c4d756c7469417373657473000f005c496e6974696174655265736572766557697468647261770c0118617373657473ad0301404d756c7469417373657446696c74657200011c72657365727665050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473ad0301404d756c7469417373657446696c74657200011064657374050201344d756c74694c6f636174696f6e00010c78636d8903011c58636d3c28293e001100305175657279486f6c64696e6710012071756572795f696428011c5175657279496400011064657374050201344d756c74694c6f636174696f6e000118617373657473ad0301404d756c7469417373657446696c74657200014c6d61785f726573706f6e73655f77656967687428010c75363400120030427579457865637574696f6e08011066656573290201284d756c746941737365740001307765696768745f6c696d6974b903012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204000904014058636d3c52756e74696d6543616c6c3e0015002c536574417070656e64697804000904014058636d3c52756e74696d6543616c6c3e00160028436c6561724572726f7200170028436c61696d41737365740801186173736574734502012c4d756c74694173736574730001187469636b6574050201344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f77656967687428010c753634001a0048556e73756273637269626556657273696f6e001b000015040c2c73746167696e675f78636d38646f75626c655f656e636f64656434446f75626c65456e636f646564040454000004011c656e636f64656434011c5665633c75383e000019040c2c73746167696e675f78636d0876330c58636d041043616c6c000004001d0401585665633c496e737472756374696f6e3c43616c6c3e3e00001d0400000221040021040c2c73746167696e675f78636d0876332c496e737472756374696f6e041043616c6c0001c034576974686472617741737365740400c4012c4d756c7469417373657473000000545265736572766541737365744465706f73697465640400c4012c4d756c7469417373657473000100585265636569766554656c65706f7274656441737365740400c4012c4d756c7469417373657473000200345175657279526573706f6e736510012071756572795f696428011c51756572794964000120726573706f6e7365c9030120526573706f6e73650001286d61785f77656967687424011857656967687400011c71756572696572f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e000300345472616e736665724173736574080118617373657473c4012c4d756c746941737365747300012c62656e6566696369617279d401344d756c74694c6f636174696f6e000400505472616e736665725265736572766541737365740c0118617373657473c4012c4d756c746941737365747300011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000500205472616e736163740c012c6f726967696e5f6b696e64a50301284f726967696e4b696e64000158726571756972655f7765696768745f61745f6d6f737424011857656967687400011063616c6c1504014c446f75626c65456e636f6465643c43616c6c3e0006006448726d704e65774368616e6e656c4f70656e526571756573740c011873656e646572e0010c7533320001406d61785f6d6573736167655f73697a65e0010c7533320001306d61785f6361706163697479e0010c7533320007004c48726d704368616e6e656c4163636570746564040124726563697069656e74e0010c7533320008004848726d704368616e6e656c436c6f73696e670c0124696e69746961746f72e0010c75333200011873656e646572e0010c753332000124726563697069656e74e0010c7533320009002c436c6561724f726967696e000a003444657363656e644f726967696e0400d80154496e746572696f724d756c74694c6f636174696f6e000b002c5265706f72744572726f720400f50301445175657279526573706f6e7365496e666f000c00304465706f7369744173736574080118617373657473f90301404d756c7469417373657446696c74657200012c62656e6566696369617279d401344d756c74694c6f636174696f6e000d004c4465706f7369745265736572766541737365740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e000e003445786368616e676541737365740c011067697665f90301404d756c7469417373657446696c74657200011077616e74c4012c4d756c746941737365747300011c6d6178696d616c35010110626f6f6c000f005c496e6974696174655265736572766557697468647261770c0118617373657473f90301404d756c7469417373657446696c74657200011c72657365727665d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e00100040496e69746961746554656c65706f72740c0118617373657473f90301404d756c7469417373657446696c74657200011064657374d401344d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e001100345265706f7274486f6c64696e67080134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f000118617373657473f90301404d756c7469417373657446696c74657200120030427579457865637574696f6e08011066656573cc01284d756c746941737365740001307765696768745f6c696d69742102012c5765696768744c696d697400130034526566756e64537572706c75730014003c5365744572726f7248616e646c657204001904012458636d3c43616c6c3e0015002c536574417070656e64697804001904012458636d3c43616c6c3e00160028436c6561724572726f7200170028436c61696d4173736574080118617373657473c4012c4d756c74694173736574730001187469636b6574d401344d756c74694c6f636174696f6e0018001054726170040028010c7536340019004053756273637269626556657273696f6e08012071756572795f696428011c5175657279496400014c6d61785f726573706f6e73655f776569676874240118576569676874001a0048556e73756273637269626556657273696f6e001b00244275726e41737365740400c4012c4d756c7469417373657473001c002c45787065637441737365740400c4012c4d756c7469417373657473001d00304578706563744f726967696e0400f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e001e002c4578706563744572726f720400cd0301504f7074696f6e3c287533322c204572726f72293e001f00504578706563745472616e736163745374617475730400e90301384d617962654572726f72436f64650020002c517565727950616c6c657408012c6d6f64756c655f6e616d6534011c5665633c75383e000134726573706f6e73655f696e666ff50301445175657279526573706f6e7365496e666f0021003045787065637450616c6c6574140114696e646578e0010c7533320001106e616d6534011c5665633c75383e00012c6d6f64756c655f6e616d6534011c5665633c75383e00012c63726174655f6d616a6f72e0010c75333200013c6d696e5f63726174655f6d696e6f72e0010c753332002200505265706f72745472616e736163745374617475730400f50301445175657279526573706f6e7365496e666f0023004c436c6561725472616e736163745374617475730024003c556e6976657273616c4f726967696e0400dc01204a756e6374696f6e002500344578706f72744d6573736167650c011c6e6574776f726be801244e6574776f726b496400012c64657374696e6174696f6ed80154496e746572696f724d756c74694c6f636174696f6e00010c78636dbd03011c58636d3c28293e002600244c6f636b41737365740801146173736574cc01284d756c74694173736574000120756e6c6f636b6572d401344d756c74694c6f636174696f6e0027002c556e6c6f636b41737365740801146173736574cc01284d756c74694173736574000118746172676574d401344d756c74694c6f636174696f6e002800384e6f7465556e6c6f636b61626c650801146173736574cc01284d756c746941737365740001146f776e6572d401344d756c74694c6f636174696f6e0029003452657175657374556e6c6f636b0801146173736574cc01284d756c746941737365740001186c6f636b6572d401344d756c74694c6f636174696f6e002a002c536574466565734d6f64650401306a69745f776974686472617735010110626f6f6c002b0020536574546f70696304000401205b75383b2033325d002c0028436c656172546f706963002d002c416c6961734f726967696e0400d401344d756c74694c6f636174696f6e002e003c556e70616964457865637574696f6e0801307765696768745f6c696d69742102012c5765696768744c696d6974000130636865636b5f6f726967696ef10301544f7074696f6e3c4d756c74694c6f636174696f6e3e002f000025040c4863756d756c75735f70616c6c65745f78636d1870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e29040c6063756d756c75735f70616c6c65745f646d705f71756575651870616c6c65741043616c6c04045400010448736572766963655f6f766572776569676874080114696e6465782c013c4f766572776569676874496e6465780001307765696768745f6c696d69742401185765696768740000048c536565205b6050616c6c65743a3a736572766963655f6f766572776569676874605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e2d040c4070616c6c65745f696e666c6174696f6e1870616c6c65741043616c6c0404540001043c73746172745f696e666c6174696f6e04016c696e666c6174696f6e5f73746172745f72656c61795f626c6f636b100144426c6f636b4e756d626572466f723c543e00000480536565205b6050616c6c65743a3a73746172745f696e666c6174696f6e605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e31040c3470616c6c65745f756e697175651870616c6c65741043616c6c040454000180446372656174655f636f6c6c656374696f6e10013c636f6c6c656374696f6e5f6e616d65350401d4426f756e6465645665633c7531362c20436f6e73745533323c4d41585f434f4c4c454354494f4e5f4e414d455f4c454e4754483e3e000158636f6c6c656374696f6e5f6465736372697074696f6e3d0401f0426f756e6465645665633c7531362c20436f6e73745533323c4d41585f434f4c4c454354494f4e5f4445534352495054494f4e5f4c454e4754483e3e000130746f6b656e5f707265666978410401c4426f756e6465645665633c75382c20436f6e73745533323c4d41585f544f4b454e5f5052454649585f4c454e4754483e3e0001106d6f646545040138436f6c6c656374696f6e4d6f646500000488536565205b6050616c6c65743a3a6372656174655f636f6c6c656374696f6e605d2e506372656174655f636f6c6c656374696f6e5f6578040110646174614904019c437265617465436f6c6c656374696f6e446174613c543a3a43726f73734163636f756e7449643e00010494536565205b6050616c6c65743a3a6372656174655f636f6c6c656374696f6e5f6578605d2e4864657374726f795f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640002048c536565205b6050616c6c65743a3a64657374726f795f636f6c6c656374696f6e605d2e446164645f746f5f616c6c6f775f6c697374080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c616464726573734d040144543a3a43726f73734163636f756e74496400030488536565205b6050616c6c65743a3a6164645f746f5f616c6c6f775f6c697374605d2e5872656d6f76655f66726f6d5f616c6c6f775f6c697374080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c616464726573734d040144543a3a43726f73734163636f756e7449640004049c536565205b6050616c6c65743a3a72656d6f76655f66726f6d5f616c6c6f775f6c697374605d2e5c6368616e67655f636f6c6c656374696f6e5f6f776e6572080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001246e65775f6f776e6572000130543a3a4163636f756e744964000504a0536565205b6050616c6c65743a3a6368616e67655f636f6c6c656374696f6e5f6f776e6572605d2e506164645f636f6c6c656374696f6e5f61646d696e080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001306e65775f61646d696e5f69644d040144543a3a43726f73734163636f756e74496400060494536565205b6050616c6c65743a3a6164645f636f6c6c656374696f6e5f61646d696e605d2e5c72656d6f76655f636f6c6c656374696f6e5f61646d696e080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001286163636f756e745f69644d040144543a3a43726f73734163636f756e744964000704a0536565205b6050616c6c65743a3a72656d6f76655f636f6c6c656374696f6e5f61646d696e605d2e587365745f636f6c6c656374696f6e5f73706f6e736f72080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400012c6e65775f73706f6e736f72000130543a3a4163636f756e7449640008049c536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f73706f6e736f72605d2e4c636f6e6669726d5f73706f6e736f7273686970040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400090490536565205b6050616c6c65743a3a636f6e6669726d5f73706f6e736f7273686970605d2e6472656d6f76655f636f6c6c656374696f6e5f73706f6e736f72040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000a04a8536565205b6050616c6c65743a3a72656d6f76655f636f6c6c656374696f6e5f73706f6e736f72605d2e2c6372656174655f6974656d0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001146f776e65724d040144543a3a43726f73734163636f756e74496400011064617461c10401384372656174654974656d44617461000b0470536565205b6050616c6c65743a3a6372656174655f6974656d605d2e546372656174655f6d756c7469706c655f6974656d730c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001146f776e65724d040144543a3a43726f73734163636f756e7449640001286974656d735f64617461d104014c5665633c4372656174654974656d446174613e000c0498536565205b6050616c6c65743a3a6372656174655f6d756c7469706c655f6974656d73605d2e647365745f636f6c6c656374696f6e5f70726f70657274696573080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400012870726f70657274696573b50401345665633c50726f70657274793e000d04a8536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f70726f70657274696573605d2e7064656c6574655f636f6c6c656374696f6e5f70726f70657274696573080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400013470726f70657274795f6b657973d50401405665633c50726f70657274794b65793e000e04b4536565205b6050616c6c65743a3a64656c6574655f636f6c6c656374696f6e5f70726f70657274696573605d2e507365745f746f6b656e5f70726f706572746965730c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000120746f6b656e5f6964d904011c546f6b656e496400012870726f70657274696573b50401345665633c50726f70657274793e000f0494536565205b6050616c6c65743a3a7365745f746f6b656e5f70726f70657274696573605d2e5c64656c6574655f746f6b656e5f70726f706572746965730c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000120746f6b656e5f6964d904011c546f6b656e496400013470726f70657274795f6b657973d50401405665633c50726f70657274794b65793e001004a0536565205b6050616c6c65743a3a64656c6574655f746f6b656e5f70726f70657274696573605d2e787365745f746f6b656e5f70726f70657274795f7065726d697373696f6e73080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400015070726f70657274795f7065726d697373696f6e73a50401685665633c50726f70657274794b65795065726d697373696f6e3e001104bc536565205b6050616c6c65743a3a7365745f746f6b656e5f70726f70657274795f7065726d697373696f6e73605d2e606372656174655f6d756c7469706c655f6974656d735f6578080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011064617461dd04018c4372656174654974656d4578446174613c543a3a43726f73734163636f756e7449643e001204a4536565205b6050616c6c65743a3a6372656174655f6d756c7469706c655f6974656d735f6578605d2e687365745f7472616e73666572735f656e61626c65645f666c6167080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011476616c756535010110626f6f6c001304ac536565205b6050616c6c65743a3a7365745f7472616e73666572735f656e61626c65645f666c6167605d2e246275726e5f6974656d0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800140468536565205b6050616c6c65743a3a6275726e5f6974656d605d2e246275726e5f66726f6d100134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011066726f6d4d040144543a3a43726f73734163636f756e74496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800150468536565205b6050616c6c65743a3a6275726e5f66726f6d605d2e207472616e73666572100124726563697069656e744d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800160464536565205b6050616c6c65743a3a7472616e73666572605d2e1c617070726f766510011c7370656e6465724d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e4964000118616d6f756e741801107531323800170460536565205b6050616c6c65743a3a617070726f7665605d2e30617070726f76655f66726f6d14011066726f6d4d040144543a3a43726f73734163636f756e744964000108746f4d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e4964000118616d6f756e741801107531323800180474536565205b6050616c6c65743a3a617070726f76655f66726f6d605d2e347472616e736665725f66726f6d14011066726f6d4d040144543a3a43726f73734163636f756e744964000124726563697069656e744d040144543a3a43726f73734163636f756e744964000134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e496400011476616c75651801107531323800190478536565205b6050616c6c65743a3a7472616e736665725f66726f6d605d2e547365745f636f6c6c656374696f6e5f6c696d697473080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001246e65775f6c696d69745d040140436f6c6c656374696f6e4c696d697473001a0498536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f6c696d697473605d2e687365745f636f6c6c656374696f6e5f7065726d697373696f6e73080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001386e65775f7065726d697373696f6e71040154436f6c6c656374696f6e5065726d697373696f6e73001b04ac536565205b6050616c6c65743a3a7365745f636f6c6c656374696f6e5f7065726d697373696f6e73605d2e2c7265706172746974696f6e0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000120746f6b656e5f6964d904011c546f6b656e4964000118616d6f756e7418011075313238001c0470536565205b6050616c6c65743a3a7265706172746974696f6e605d2e547365745f616c6c6f77616e63655f666f725f616c6c0c0134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640001206f70657261746f724d040144543a3a43726f73734163636f756e74496400011c617070726f766535010110626f6f6c001d0498536565205b6050616c6c65743a3a7365745f616c6c6f77616e63655f666f725f616c6c605d2e5c666f7263655f7265706169725f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964001e04a0536565205b6050616c6c65743a3a666f7263655f7265706169725f636f6c6c656374696f6e605d2e44666f7263655f7265706169725f6974656d080134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e496400011c6974656d5f6964d904011c546f6b656e4964001f0488536565205b6050616c6c65743a3a666f7263655f7265706169725f6974656d605d2e04d85479706520616c69617320746f2050616c6c65742c20746f206265207573656420627920636f6e7374727563745f72756e74696d652e35040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014901045300000400390401185665633c543e000039040000024901003d040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454014901045300000400390401185665633c543e000041040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e00004504083c75705f646174615f7374727563747338436f6c6c656374696f6e4d6f646500010c0c4e46540000002046756e6769626c650400080134446563696d616c506f696e747300010028526546756e6769626c65000200004904083c75705f646174615f7374727563747350437265617465436f6c6c656374696f6e44617461043843726f73734163636f756e744964014d04003001106d6f646545040138436f6c6c656374696f6e4d6f6465000118616363657373510401484f7074696f6e3c4163636573734d6f64653e0001106e616d6535040138436f6c6c656374696f6e4e616d6500012c6465736372697074696f6e3d040154436f6c6c656374696f6e4465736372697074696f6e000130746f6b656e5f70726566697841040154436f6c6c656374696f6e546f6b656e5072656669780001186c696d697473590401604f7074696f6e3c436f6c6c656374696f6e4c696d6974733e00012c7065726d697373696f6e736d0401744f7074696f6e3c436f6c6c656374696f6e5065726d697373696f6e733e000168746f6b656e5f70726f70657274795f7065726d697373696f6e7395040188436f6c6c656374696f6e50726f706572746965735065726d697373696f6e7356656300012870726f70657274696573a904015c436f6c6c656374696f6e50726f7065727469657356656300012861646d696e5f6c697374b904014c5665633c43726f73734163636f756e7449643e00013c70656e64696e675f73706f6e736f72bd0401584f7074696f6e3c43726f73734163636f756e7449643e000114666c6167736902013c436f6c6c656374696f6e466c61677300004d040c2870616c6c65745f65766d1c6163636f756e745c426173696343726f73734163636f756e7449645265707204244163636f756e744964010001082453756273747261746504000001244163636f756e74496400000020457468657265756d0400650301104831363000010000510404184f7074696f6e0404540155040108104e6f6e6500000010536f6d650400550400000100005504083c75705f646174615f73747275637473284163636573734d6f6465000108184e6f726d616c00000024416c6c6f774c69737400010000590404184f7074696f6e040454015d040108104e6f6e6500000010536f6d6504005d0400000100005d04083c75705f646174615f7374727563747340436f6c6c656374696f6e4c696d69747300002401746163636f756e745f746f6b656e5f6f776e6572736869705f6c696d69742503012c4f7074696f6e3c7533323e00014c73706f6e736f7265645f646174615f73697a652503012c4f7074696f6e3c7533323e00016473706f6e736f7265645f646174615f726174655f6c696d69746104016c4f7074696f6e3c53706f6e736f72696e67526174654c696d69743e00012c746f6b656e5f6c696d69742503012c4f7074696f6e3c7533323e00016073706f6e736f725f7472616e736665725f74696d656f75742503012c4f7074696f6e3c7533323e00015c73706f6e736f725f617070726f76655f74696d656f75742503012c4f7074696f6e3c7533323e0001486f776e65725f63616e5f7472616e73666572690401304f7074696f6e3c626f6f6c3e0001446f776e65725f63616e5f64657374726f79690401304f7074696f6e3c626f6f6c3e0001447472616e73666572735f656e61626c6564690401304f7074696f6e3c626f6f6c3e0000610404184f7074696f6e0404540165040108104e6f6e6500000010536f6d650400650400000100006504083c75705f646174615f737472756374734c53706f6e736f72696e67526174654c696d69740001084853706f6e736f72696e6744697361626c656400000018426c6f636b73040010010c75333200010000690404184f7074696f6e0404540135010108104e6f6e6500000010536f6d650400350100000100006d0404184f7074696f6e0404540171040108104e6f6e6500000010536f6d650400710400000100007104083c75705f646174615f7374727563747354436f6c6c656374696f6e5065726d697373696f6e7300000c0118616363657373510401484f7074696f6e3c4163636573734d6f64653e0001246d696e745f6d6f6465690401304f7074696f6e3c626f6f6c3e00011c6e657374696e67750401684f7074696f6e3c4e657374696e675065726d697373696f6e733e0000750404184f7074696f6e0404540179040108104e6f6e6500000010536f6d650400790400000100007904083c75705f646174615f73747275637473484e657374696e675065726d697373696f6e7300000c012c746f6b656e5f6f776e657235010110626f6f6c000140636f6c6c656374696f6e5f61646d696e35010110626f6f6c000128726573747269637465647d0401684f7074696f6e3c4f776e6572526573747269637465645365743e00007d0404184f7074696f6e0404540181040108104e6f6e6500000010536f6d650400810400000100008104083c75705f646174615f73747275637473484f776e657252657374726963746564536574000004008504015c4f776e657252657374726963746564536574496e6e6572000085040c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f7365743c426f756e64656442547265655365740804540189040453000004008d04012c42547265655365743c543e00008904083c75705f646174615f7374727563747330436f6c6c656374696f6e49640000040010010c75333200008d04042042547265655365740404540189040004009104000000910400000289040095040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454019904045300000400a50401185665633c543e00009904083c75705f646174615f737472756374735450726f70657274794b65795065726d697373696f6e000008010c6b65799d04012c50726f70657274794b65790001287065726d697373696f6ea104014850726f70657274795065726d697373696f6e00009d040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000a104083c75705f646174615f737472756374734850726f70657274795065726d697373696f6e00000c011c6d757461626c6535010110626f6f6c000140636f6c6c656374696f6e5f61646d696e35010110626f6f6c00012c746f6b656e5f6f776e657235010110626f6f6c0000a504000002990400a9040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401ad04045300000400b50401185665633c543e0000ad04083c75705f646174615f737472756374732050726f7065727479000008010c6b65799d04012c50726f70657274794b657900011476616c7565b104013450726f706572747956616c75650000b1040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000b504000002ad0400b9040000024d0400bd0404184f7074696f6e040454014d040108104e6f6e6500000010536f6d6504004d040000010000c104083c75705f646174615f73747275637473384372656174654974656d4461746100010c0c4e46540400c50401344372656174654e6674446174610000002046756e6769626c650400c904014843726561746546756e6769626c654461746100010028526546756e6769626c650400cd040150437265617465526546756e6769626c654461746100020000c504083c75705f646174615f73747275637473344372656174654e667444617461000004012870726f70657274696573a904015c436f6c6c656374696f6e50726f706572746965735665630000c904083c75705f646174615f737472756374734843726561746546756e6769626c6544617461000004011476616c7565180110753132380000cd04083c75705f646174615f7374727563747350437265617465526546756e6769626c654461746100000801187069656365731801107531323800012870726f70657274696573a904015c436f6c6c656374696f6e50726f706572746965735665630000d104000002c10400d5040000029d0400d904083c75705f646174615f737472756374731c546f6b656e49640000040010010c7533320000dd04083c75705f646174615f73747275637473404372656174654974656d457844617461043843726f73734163636f756e744964014d0401100c4e46540400e104012d01426f756e6465645665633c4372656174654e66744578446174613c43726f73734163636f756e7449643e2c20436f6e73745533323c0a4d41585f4954454d535f5045525f42415443483e3e0000002046756e6769626c650400ed04011101426f756e64656442547265654d61703c43726f73734163636f756e7449642c20753132382c20436f6e73745533323c4d41585f4954454d535f5045525f42415443483e3e0001005c526566756e6769626c654d756c7469706c654974656d730400fd04016501426f756e6465645665633c437265617465526566756e6769626c65457853696e676c654f776e65723c43726f73734163636f756e7449643e2c20436f6e73745533323c0a4d41585f4954454d535f5045525f42415443483e3e00020060526566756e6769626c654d756c7469706c654f776e6572730400090501c0437265617465526566756e6769626c6545784d756c7469706c654f776e6572733c43726f73734163636f756e7449643e00030000e1040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e504045300000400e90401185665633c543e0000e504083c75705f646174615f737472756374733c4372656174654e6674457844617461043843726f73734163636f756e744964014d040008012870726f70657274696573a904015c436f6c6c656374696f6e50726f706572746965735665630001146f776e65724d04013843726f73734163636f756e7449640000e904000002e50400ed040c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b014d0404560118045300000400f104013842547265654d61703c4b2c20563e0000f104042042547265654d617008044b014d0404560118000400f504000000f504000002f90400f904000004084d041800fd040c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454010105045300000400050501185665633c543e00000105083c75705f646174615f7374727563747374437265617465526566756e6769626c65457853696e676c654f776e6572043843726f73734163636f756e744964014d04000c0110757365724d04013843726f73734163636f756e7449640001187069656365731801107531323800012870726f70657274696573a904015c436f6c6c656374696f6e50726f70657274696573566563000005050000020105000905083c75705f646174615f7374727563747380437265617465526566756e6769626c6545784d756c7469706c654f776e657273043843726f73734163636f756e744964014d04000801147573657273ed04011101426f756e64656442547265654d61703c43726f73734163636f756e7449642c20753132382c20436f6e73745533323c4d41585f4954454d535f5045525f42415443483e3e00012870726f70657274696573a904015c436f6c6c656374696f6e50726f7065727469657356656300000d050c5070616c6c65745f636f6e66696775726174696f6e1870616c6c65741043616c6c040454000118987365745f7765696768745f746f5f6665655f636f656666696369656e745f6f76657272696465040114636f6566661105012c4f7074696f6e3c7536343e000004dc536565205b6050616c6c65743a3a7365745f7765696768745f746f5f6665655f636f656666696369656e745f6f76657272696465605d2e687365745f6d696e5f6761735f70726963655f6f76657272696465040114636f6566661105012c4f7074696f6e3c7536343e000104ac536565205b6050616c6c65743a3a7365745f6d696e5f6761735f70726963655f6f76657272696465605d2ea07365745f6170705f70726f6d6f74696f6e5f636f6e66696775726174696f6e5f6f76657272696465040134636f6e66696775726174696f6e150501b041707050726f6d6f74696f6e436f6e66696775726174696f6e3c426c6f636b4e756d626572466f723c543e3e000304e4536565205b6050616c6c65743a3a7365745f6170705f70726f6d6f74696f6e5f636f6e66696775726174696f6e5f6f76657272696465605d2ea07365745f636f6c6c61746f725f73656c656374696f6e5f646573697265645f636f6c6c61746f727304010c6d61782503012c4f7074696f6e3c7533323e000404e4536565205b6050616c6c65743a3a7365745f636f6c6c61746f725f73656c656374696f6e5f646573697265645f636f6c6c61746f7273605d2e8c7365745f636f6c6c61746f725f73656c656374696f6e5f6c6963656e73655f626f6e64040118616d6f756e74250501784f7074696f6e3c3c5420617320436f6e6669673e3a3a42616c616e63653e000504d0536565205b6050616c6c65743a3a7365745f636f6c6c61746f725f73656c656374696f6e5f6c6963656e73655f626f6e64605d2e947365745f636f6c6c61746f725f73656c656374696f6e5f6b69636b5f7468726573686f6c640401247468726573686f6c64250301644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000604d8536565205b6050616c6c65743a3a7365745f636f6c6c61746f725f73656c656374696f6e5f6b69636b5f7468726573686f6c64605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e110504184f7074696f6e040454012c0108104e6f6e6500000010536f6d6504002c00000100001505085070616c6c65745f636f6e66696775726174696f6e6441707050726f6d6f74696f6e436f6e66696775726174696f6e042c426c6f636b4e756d626572011000100158726563616c63756c6174696f6e5f696e74657276616c2503014c4f7074696f6e3c426c6f636b4e756d6265723e00014070656e64696e675f696e74657276616c2503014c4f7074696f6e3c426c6f636b4e756d6265723e00013c696e74657276616c5f696e636f6d651905013c4f7074696f6e3c50657262696c6c3e00016c6d61785f7374616b6572735f7065725f63616c63756c6174696f6e210501284f7074696f6e3c75383e0000190504184f7074696f6e040454011d050108104e6f6e6500000010536f6d6504001d0500000100001d050c3473705f61726974686d65746963287065725f7468696e67731c50657262696c6c0000040010010c7533320000210504184f7074696f6e04045401080108104e6f6e6500000010536f6d650400080000010000250504184f7074696f6e04045401180108104e6f6e6500000010536f6d65040018000001000029050c4070616c6c65745f7374727563747572651870616c6c65741043616c6c040454000100040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e2d050c5070616c6c65745f6170705f70726f6d6f74696f6e1870616c6c65741043616c6c040454000128447365745f61646d696e5f6164647265737304011461646d696e4d040144543a3a43726f73734163636f756e74496400000488536565205b6050616c6c65743a3a7365745f61646d696e5f61646472657373605d2e147374616b65040118616d6f756e7418013042616c616e63654f663c543e00010458536565205b6050616c6c65743a3a7374616b65605d2e2c756e7374616b655f616c6c00020470536565205b6050616c6c65743a3a756e7374616b655f616c6c605d2e3c756e7374616b655f7061727469616c040118616d6f756e7418013042616c616e63654f663c543e00080480536565205b6050616c6c65743a3a756e7374616b655f7061727469616c605d2e4873706f6e736f725f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e49640003048c536565205b6050616c6c65743a3a73706f6e736f725f636f6c6c656374696f6e605d2e6873746f705f73706f6e736f72696e675f636f6c6c656374696f6e040134636f6c6c656374696f6e5f696489040130436f6c6c656374696f6e4964000404ac536565205b6050616c6c65743a3a73746f705f73706f6e736f72696e675f636f6c6c656374696f6e605d2e4073706f6e736f725f636f6e747261637404012c636f6e74726163745f6964650301104831363000050484536565205b6050616c6c65743a3a73706f6e736f725f636f6e7472616374605d2e6073746f705f73706f6e736f72696e675f636f6e747261637404012c636f6e74726163745f69646503011048313630000604a4536565205b6050616c6c65743a3a73746f705f73706f6e736f72696e675f636f6e7472616374605d2e387061796f75745f7374616b6572730401387374616b6572735f6e756d626572210501284f7074696f6e3c75383e0007047c536565205b6050616c6c65743a3a7061796f75745f7374616b657273605d2e34666f7263655f756e7374616b6504013870656e64696e675f626c6f636b73310501585665633c426c6f636b4e756d626572466f723c543e3e00090478536565205b6050616c6c65743a3a666f7263655f756e7374616b65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e3105000002100035050c5470616c6c65745f666f726569676e5f617373657473186d6f64756c651043616c6c0404540001085872656769737465725f666f726569676e5f61737365740c01146f776e6572000130543a3a4163636f756e7449640001206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e0001206d6574616461746139050180426f783c41737365744d657461646174613c42616c616e63654f663c543e3e3e0000049c536565205b6050616c6c65743a3a72656769737465725f666f726569676e5f6173736574605d2e507570646174655f666f726569676e5f61737365740c0140666f726569676e5f61737365745f6964100138466f726569676e417373657449640001206c6f636174696f6e0102016c426f783c56657273696f6e65644d756c74694c6f636174696f6e3e0001206d6574616461746139050180426f783c41737365744d657461646174613c42616c616e63654f663c543e3e3e00010494536565205b6050616c6c65743a3a7570646174655f666f726569676e5f6173736574605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e39050c5470616c6c65745f666f726569676e5f617373657473186d6f64756c653441737365744d65746164617461041c42616c616e63650118001001106e616d653d05012441737365744e616d6500011873796d626f6c4105012c417373657453796d626f6c000120646563696d616c73080108753800013c6d696e696d616c5f62616c616e636518011c42616c616e636500003d050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000041050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000045050c2870616c6c65745f65766d1870616c6c65741043616c6c04045400011020776974686472617708011c61646472657373650301104831363000011476616c756518013042616c616e63654f663c543e00000464536565205b6050616c6c65743a3a7769746864726177605d2e1063616c6c240118736f7572636565030110483136300001187461726765746503011048313630000114696e70757434011c5665633c75383e00011476616c756549050110553235360001246761735f6c696d69742c010c75363400013c6d61785f6665655f7065725f67617349050110553235360001606d61785f7072696f726974795f6665655f7065725f676173510501304f7074696f6e3c553235363e0001146e6f6e6365510501304f7074696f6e3c553235363e00012c6163636573735f6c697374550501585665633c28483136302c205665633c483235363e293e00010454536565205b6050616c6c65743a3a63616c6c605d2e18637265617465200118736f757263656503011048313630000110696e697434011c5665633c75383e00011476616c756549050110553235360001246761735f6c696d69742c010c75363400013c6d61785f6665655f7065725f67617349050110553235360001606d61785f7072696f726974795f6665655f7065725f676173510501304f7074696f6e3c553235363e0001146e6f6e6365510501304f7074696f6e3c553235363e00012c6163636573735f6c697374550501585665633c28483136302c205665633c483235363e293e0002045c536565205b6050616c6c65743a3a637265617465605d2e1c63726561746532240118736f757263656503011048313630000110696e697434011c5665633c75383e00011073616c743001104832353600011476616c756549050110553235360001246761735f6c696d69742c010c75363400013c6d61785f6665655f7065725f67617349050110553235360001606d61785f7072696f726974795f6665655f7065725f676173510501304f7074696f6e3c553235363e0001146e6f6e6365510501304f7074696f6e3c553235363e00012c6163636573735f6c697374550501585665633c28483136302c205665633c483235363e293e00030460536565205b6050616c6c65743a3a63726561746532605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e4905083c7072696d69746976655f74797065731055323536000004004d0501205b7536343b20345d00004d05000003040000002c00510504184f7074696f6e0404540149050108104e6f6e6500000010536f6d65040049050000010000550500000259050059050000040865035d05005d05000002300061050c3c70616c6c65745f657468657265756d1870616c6c65741043616c6c040454000104207472616e7361637404012c7472616e73616374696f6e6505012c5472616e73616374696f6e00000464536565205b6050616c6c65743a3a7472616e73616374605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e65050c20657468657265756d2c7472616e73616374696f6e345472616e73616374696f6e563200010c184c65676163790400690501444c65676163795472616e73616374696f6e0000001c45495032393330040079050148454950323933305472616e73616374696f6e0001001c45495031353539040085050148454950313535395472616e73616374696f6e0002000069050c20657468657265756d2c7472616e73616374696f6e444c65676163795472616e73616374696f6e00001c01146e6f6e636549050110553235360001246761735f707269636549050110553235360001246761735f6c696d69744905011055323536000118616374696f6e6d0501445472616e73616374696f6e416374696f6e00011476616c75654905011055323536000114696e70757434011442797465730001247369676e6174757265710501505472616e73616374696f6e5369676e617475726500006d050c20657468657265756d2c7472616e73616374696f6e445472616e73616374696f6e416374696f6e0001081043616c6c04006503011048313630000000184372656174650001000071050c20657468657265756d2c7472616e73616374696f6e505472616e73616374696f6e5369676e617475726500000c010476750501545472616e73616374696f6e5265636f76657279496400010472300110483235360001047330011048323536000075050c20657468657265756d2c7472616e73616374696f6e545472616e73616374696f6e5265636f766572794964000004002c010c753634000079050c20657468657265756d2c7472616e73616374696f6e48454950323933305472616e73616374696f6e00002c0120636861696e5f69642c010c7536340001146e6f6e636549050110553235360001246761735f707269636549050110553235360001246761735f6c696d69744905011055323536000118616374696f6e6d0501445472616e73616374696f6e416374696f6e00011476616c75654905011055323536000114696e707574340114427974657300012c6163636573735f6c6973747d0501284163636573734c6973740001306f64645f795f70617269747935010110626f6f6c0001047230011048323536000104733001104832353600007d0500000281050081050c20657468657265756d2c7472616e73616374696f6e384163636573734c6973744974656d000008011c616464726573736503011c4164647265737300013073746f726167655f6b6579735d0501245665633c483235363e000085050c20657468657265756d2c7472616e73616374696f6e48454950313535395472616e73616374696f6e0000300120636861696e5f69642c010c7536340001146e6f6e636549050110553235360001606d61785f7072696f726974795f6665655f7065725f676173490501105532353600013c6d61785f6665655f7065725f67617349050110553235360001246761735f6c696d69744905011055323536000118616374696f6e6d0501445472616e73616374696f6e416374696f6e00011476616c75654905011055323536000114696e707574340114427974657300012c6163636573735f6c6973747d0501284163636573734c6973740001306f64645f795f70617269747935010110626f6f6c00010472300110483235360001047330011048323536000089050c6c70616c6c65745f65766d5f636f6e74726163745f68656c706572731870616c6c65741043616c6c040454000104706d6967726174655f66726f6d5f73656c665f73706f6e736f72696e670401246164647265737365738d0501245665633c483136303e000004b4536565205b6050616c6c65743a3a6d6967726174655f66726f6d5f73656c665f73706f6e736f72696e67605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e8d0500000265030091050c5070616c6c65745f65766d5f6d6967726174696f6e1870616c6c65741043616c6c04045400011814626567696e04011c61646472657373650301104831363000000458536565205b6050616c6c65743a3a626567696e605d2e207365745f6461746108011c61646472657373650301104831363000011064617461950501445665633c28483235362c2048323536293e00010464536565205b6050616c6c65743a3a7365745f64617461605d2e1866696e69736808011c616464726573736503011048313630000110636f646534011c5665633c75383e0002045c536565205b6050616c6c65743a3a66696e697368605d2e3c696e736572745f6574685f6c6f67730401106c6f67739d0501485665633c657468657265756d3a3a4c6f673e00030480536565205b6050616c6c65743a3a696e736572745f6574685f6c6f6773605d2e34696e736572745f6576656e74730401186576656e74736d0101305665633c5665633c75383e3e00040478536565205b6050616c6c65743a3a696e736572745f6576656e7473605d2e4072656d6f76655f726d726b5f6461746100050484536565205b6050616c6c65743a3a72656d6f76655f726d726b5f64617461605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732e95050000029905009905000004083030009d05000002a10500a1050c20657468657265756d0c6c6f670c4c6f6700000c011c616464726573736503011048313630000118746f706963735d0501245665633c483235363e0001106461746134011442797465730000a5050c4870616c6c65745f6d61696e74656e616e63651870616c6c65741043616c6c04045400010818656e61626c650000045c536565205b6050616c6c65743a3a656e61626c65605d2e1c64697361626c6500010460536565205b6050616c6c65743a3a64697361626c65605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ea9050c3870616c6c65745f7574696c6974791870616c6c65741043616c6c04045400011814626174636804011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00000458536565205b6050616c6c65743a3a6261746368605d2e3461735f64657269766174697665080114696e6465784901010c75313600011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00010478536565205b6050616c6c65743a3a61735f64657269766174697665605d2e2462617463685f616c6c04011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00020468536565205b6050616c6c65743a3a62617463685f616c6c605d2e2c64697370617463685f617308012461735f6f726967696e45030154426f783c543a3a50616c6c6574734f726967696e3e00011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00030470536565205b6050616c6c65743a3a64697370617463685f6173605d2e2c666f7263655f626174636804011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00040470536565205b6050616c6c65743a3a666f7263655f6261746368605d2e2c776974685f77656967687408011063616c6c5d01017c426f783c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00011877656967687424011857656967687400050470536565205b6050616c6c65743a3a776974685f776569676874605d2e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732ead050000025d0100b1050c4470616c6c65745f746573745f7574696c731870616c6c65741043616c6c04045400011818656e61626c6500000454536565206050616c6c65743a3a656e61626c65602e387365745f746573745f76616c756504011476616c756510010c75333200010474536565206050616c6c65743a3a7365745f746573745f76616c7565602e6c7365745f746573745f76616c75655f616e645f726f6c6c6261636b04011476616c756510010c753332000204a8536565206050616c6c65743a3a7365745f746573745f76616c75655f616e645f726f6c6c6261636b602e38696e635f746573745f76616c756500030474536565206050616c6c65743a3a696e635f746573745f76616c7565602e346a7573745f74616b655f66656500040470536565206050616c6c65743a3a6a7573745f74616b655f666565602e2462617463685f616c6c04011463616c6c73ad05017c5665633c3c5420617320436f6e6669673e3a3a52756e74696d6543616c6c3e00050460536565206050616c6c65743a3a62617463685f616c6c602e040d01436f6e7461696e7320612076617269616e742070657220646973706174636861626c652065787472696e736963207468617420746869732070616c6c6574206861732eb5050c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000b9050c4070616c6c65745f7363686564756c65721870616c6c6574144576656e74040454000118245363686564756c65640801107768656e100144426c6f636b4e756d626572466f723c543e000114696e64657810010c753332000004505363686564756c656420736f6d65207461736b2e2043616e63656c65640801107768656e100144426c6f636b4e756d626572466f723c543e000114696e64657810010c7533320001044c43616e63656c656420736f6d65207461736b2e28446973706174636865640c01107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e000118726573756c74a801384469737061746368526573756c74000204544469737061746368656420736f6d65207461736b2e3c43616c6c556e617661696c61626c650801107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e00030429015468652063616c6c20666f72207468652070726f7669646564206861736820776173206e6f7420666f756e6420736f20746865207461736b20686173206265656e2061626f727465642e38506572696f6469634661696c65640801107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e0004043d0154686520676976656e207461736b2077617320756e61626c6520746f2062652072656e657765642073696e636520746865206167656e64612069732066756c6c206174207468617420626c6f636b2e545065726d616e656e746c794f7665727765696768740801107461736b790301785461736b416464726573733c426c6f636b4e756d626572466f723c543e3e00010869648801404f7074696f6e3c5461736b4e616d653e000504f054686520676976656e207461736b2063616e206e657665722062652065786563757465642073696e6365206974206973206f7665727765696768742e04304576656e747320747970652ebd050c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144576656e7404045400011c1c537563636573730c01306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d4861736800011877656967687424011857656967687400000464536f6d652058434d20776173206578656375746564206f6b2e104661696c1001306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d486173680001146572726f72d503012058636d4572726f7200011877656967687424011857656967687400010440536f6d652058434d206661696c65642e2842616456657273696f6e0401306d6573736167655f6861736804011c58636d48617368000204544261642058434d2076657273696f6e20757365642e24426164466f726d61740401306d6573736167655f6861736804011c58636d48617368000304504261642058434d20666f726d617420757365642e3c58636d704d65737361676553656e740401306d6573736167655f6861736804011c58636d48617368000404c0416e2048524d50206d657373616765207761732073656e7420746f2061207369626c696e672070617261636861696e2e484f766572776569676874456e71756575656410011873656e646572ad01011850617261496400011c73656e745f617410014052656c6179426c6f636b4e756d626572000114696e6465782c013c4f766572776569676874496e6465780001207265717569726564240118576569676874000504d4416e2058434d2065786365656465642074686520696e646976696475616c206d65737361676520776569676874206275646765742e484f7665727765696768745365727669636564080114696e6465782c013c4f766572776569676874496e646578000110757365642401185765696768740006044101416e2058434d2066726f6d20746865206f7665727765696768742071756575652077617320657865637574656420776974682074686520676976656e2061637475616c2077656967687420757365642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574c1050c2870616c6c65745f78636d1870616c6c6574144576656e7404045400015c24417474656d7074656404011c6f7574636f6d65c505015078636d3a3a6c61746573743a3a4f7574636f6d65000004a8457865637574696f6e206f6620616e2058434d206d6573736167652077617320617474656d707465642e1053656e741001186f726967696ed401344d756c74694c6f636174696f6e00012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e00011c6d657373616765bd03011c58636d3c28293e0001286d6573736167655f696404011c58636d486173680001045c412058434d206d657373616765207761732073656e742e48556e6578706563746564526573706f6e73650801186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400020c5901517565727920726573706f6e736520726563656976656420776869636820646f6573206e6f74206d61746368206120726567697374657265642071756572792e2054686973206d61792062652062656361757365206155016d61746368696e6720717565727920776173206e6576657220726567697374657265642c206974206d617920626520626563617573652069742069732061206475706c696361746520726573706f6e73652c206f727062656361757365207468652071756572792074696d6564206f75742e34526573706f6e7365526561647908012071756572795f69642c011c51756572794964000120726573706f6e7365c9030120526573706f6e73650003085d01517565727920726573706f6e736520686173206265656e20726563656976656420616e6420697320726561647920666f722074616b696e672077697468206074616b655f726573706f6e7365602e205468657265206973806e6f2072656769737465726564206e6f74696669636174696f6e2063616c6c2e204e6f7469666965640c012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380004085901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e20686173a86265656e206469737061746368656420616e64206578656375746564207375636365737366756c6c792e404e6f746966794f76657277656967687414012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800013461637475616c5f77656967687424011857656967687400014c6d61785f62756467657465645f77656967687424011857656967687400050c4901517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652072656769737465726564206e6f74696669636174696f6e5901636f756c64206e6f742062652064697370617463686564206265636175736520746865206469737061746368207765696768742069732067726561746572207468616e20746865206d6178696d756d20776569676874e46f726967696e616c6c7920627564676574656420627920746869732072756e74696d6520666f722074686520717565727920726573756c742e4c4e6f7469667944697370617463684572726f720c012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e64657808010875380006085501517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e2054686572652077617320612067656e6572616c206572726f722077697468886469737061746368696e6720746865206e6f74696669636174696f6e2063616c6c2e484e6f746966794465636f64654661696c65640c012071756572795f69642c011c5175657279496400013070616c6c65745f696e646578080108753800012863616c6c5f696e646578080108753800070c5101517565727920726573706f6e736520686173206265656e20726563656976656420616e642071756572792069732072656d6f7665642e205468652064697370617463682077617320756e61626c6520746f20626559016465636f64656420696e746f2061206043616c6c603b2074686973206d696768742062652064756520746f2064697370617463682066756e6374696f6e20686176696e672061207369676e6174757265207768696368946973206e6f742060286f726967696e2c20517565727949642c20526573706f6e736529602e40496e76616c6964526573706f6e6465720c01186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400014465787065637465645f6c6f636174696f6ef10301544f7074696f6e3c4d756c74694c6f636174696f6e3e00080c5901457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206f726967696e206c6f636174696f6e206f662074686520726573706f6e736520646f657355016e6f74206d6174636820746861742065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5c496e76616c6964526573706f6e64657256657273696f6e0801186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400091c5101457870656374656420717565727920726573706f6e736520686173206265656e2072656365697665642062757420746865206578706563746564206f726967696e206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e34526573706f6e736554616b656e04012071756572795f69642c011c51756572794964000a04c8526563656976656420717565727920726573706f6e736520686173206265656e207265616420616e642072656d6f7665642e34417373657473547261707065640c011068617368300110483235360001186f726967696ed401344d756c74694c6f636174696f6e0001186173736574734102015056657273696f6e65644d756c7469417373657473000b04b8536f6d65206173736574732068617665206265656e20706c6163656420696e20616e20617373657420747261702e5456657273696f6e4368616e67654e6f74696669656410012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000118726573756c7410012858636d56657273696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d48617368000c0c2501416e2058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e206d65737361676520686173206265656e20617474656d7074656420746f2062652073656e742e00e054686520636f7374206f662073656e64696e672069742028626f726e652062792074686520636861696e2920697320696e636c756465642e5c537570706f7274656456657273696f6e4368616e6765640801206c6f636174696f6ed401344d756c74694c6f636174696f6e00011c76657273696f6e10012858636d56657273696f6e000d08390154686520737570706f727465642076657273696f6e206f662061206c6f636174696f6e20686173206265656e206368616e6765642e2054686973206d69676874206265207468726f75676820616ec06175746f6d61746963206e6f74696669636174696f6e206f722061206d616e75616c20696e74657276656e74696f6e2e504e6f7469667954617267657453656e644661696c0c01206c6f636174696f6ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c517565727949640001146572726f72d503012058636d4572726f72000e0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f727c73656e64696e6720746865206e6f74696669636174696f6e20746f2069742e644e6f746966795461726765744d6967726174696f6e4661696c0801206c6f636174696f6e0102015856657273696f6e65644d756c74694c6f636174696f6e00012071756572795f69642c011c51756572794964000f0859014120676976656e206c6f636174696f6e2077686963682068616420612076657273696f6e206368616e676520737562736372697074696f6e207761732064726f70706564206f77696e6720746f20616e206572726f72b46d6967726174696e6720746865206c6f636174696f6e20746f206f7572206e65772058434d20666f726d61742e54496e76616c69645175657269657256657273696f6e0801186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400101c5501457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652065787065637465642071756572696572206c6f636174696f6e20706c6163656420696e4d0173746f7261676520627920746869732072756e74696d652070726576696f75736c792063616e6e6f74206265206465636f6465642e205468652071756572792072656d61696e7320726567697374657265642e0041015468697320697320756e6578706563746564202873696e63652061206c6f636174696f6e20706c6163656420696e2073746f7261676520696e20612070726576696f75736c7920657865637574696e674d0172756e74696d652073686f756c64206265207265616461626c65207072696f7220746f2071756572792074696d656f75742920616e642064616e6765726f75732073696e63652074686520706f737369626c79590176616c696420726573706f6e73652077696c6c2062652064726f707065642e204d616e75616c20676f7665726e616e636520696e74657276656e74696f6e2069732070726f6261626c7920676f696e6720746f2062651c6e65656465642e38496e76616c6964517565726965721001186f726967696ed401344d756c74694c6f636174696f6e00012071756572795f69642c011c5175657279496400014065787065637465645f71756572696572d401344d756c74694c6f636174696f6e0001506d617962655f61637475616c5f71756572696572f10301544f7074696f6e3c4d756c74694c6f636174696f6e3e00110c5d01457870656374656420717565727920726573706f6e736520686173206265656e20726563656976656420627574207468652071756572696572206c6f636174696f6e206f662074686520726573706f6e736520646f657351016e6f74206d61746368207468652065787065637465642e205468652071756572792072656d61696e73207265676973746572656420666f722061206c617465722c2076616c69642c20726573706f6e736520746f6c626520726563656976656420616e642061637465642075706f6e2e5056657273696f6e4e6f74696679537461727465640c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d486173680012085901412072656d6f746520686173207265717565737465642058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e2066726f6d20757320616e64207765206861766520686f6e6f7265642069742e1d01412076657273696f6e20696e666f726d6174696f6e206d6573736167652069732073656e7420746f207468656d20616e642069747320636f737420697320696e636c756465642e5856657273696f6e4e6f746966795265717565737465640c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d486173680013043d015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073656e642075732058434d2076657273696f6e206368616e6765206e6f74696669636174696f6e732e6056657273696f6e4e6f74696679556e7265717565737465640c012c64657374696e6174696f6ed401344d756c74694c6f636174696f6e000110636f7374c4012c4d756c74694173736574730001286d6573736167655f696404011c58636d4861736800140825015765206861766520726571756573746564207468617420612072656d6f746520636861696e2073746f70732073656e64696e672075732058434d2076657273696f6e206368616e6765386e6f74696669636174696f6e732e204665657350616964080118706179696e67d401344d756c74694c6f636174696f6e00011066656573c4012c4d756c7469417373657473001504310146656573207765726520706169642066726f6d2061206c6f636174696f6e20666f7220616e206f7065726174696f6e20286f6674656e20666f72207573696e67206053656e6458636d60292e34417373657473436c61696d65640c011068617368300110483235360001186f726967696ed401344d756c74694c6f636174696f6e0001186173736574734102015056657273696f6e65644d756c7469417373657473001604c0536f6d65206173736574732068617665206265656e20636c61696d65642066726f6d20616e2061737365742074726170047c54686520604576656e746020656e756d206f6620746869732070616c6c6574c505102c73746167696e675f78636d087633187472616974731c4f7574636f6d6500010c20436f6d706c657465040024011857656967687400000028496e636f6d706c65746508002401185765696768740000d50301144572726f72000100144572726f720400d50301144572726f7200020000c9050c4863756d756c75735f70616c6c65745f78636d1870616c6c6574144576656e7404045400010c34496e76616c6964466f726d617404000401205b75383b2033325d00000880446f776e77617264206d65737361676520697320696e76616c69642058434d2e205c5b206964205c5d48556e737570706f7274656456657273696f6e04000401205b75383b2033325d000108bc446f776e77617264206d65737361676520697320756e737570706f727465642076657273696f6e206f662058434d2e205c5b206964205c5d404578656375746564446f776e7761726408000401205b75383b2033325d0000c505011c4f7574636f6d65000208c4446f776e77617264206d65737361676520657865637574656420776974682074686520676976656e206f7574636f6d652e445c5b2069642c206f7574636f6d65205c5d047c54686520604576656e746020656e756d206f6620746869732070616c6c6574cd050c6063756d756c75735f70616c6c65745f646d705f71756575651870616c6c6574144576656e7404045400011c34496e76616c6964466f726d61740401306d6573736167655f6861736804011c58636d4861736800000480446f776e77617264206d65737361676520697320696e76616c69642058434d2e48556e737570706f7274656456657273696f6e0401306d6573736167655f6861736804011c58636d48617368000104bc446f776e77617264206d65737361676520697320756e737570706f727465642076657273696f6e206f662058434d2e404578656375746564446f776e776172640c01306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d4861736800011c6f7574636f6d65c505011c4f7574636f6d65000204c4446f776e77617264206d65737361676520657865637574656420776974682074686520676976656e206f7574636f6d652e3c5765696768744578686175737465641001306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d4861736800014072656d61696e696e675f77656967687424011857656967687400013c72657175697265645f776569676874240118576569676874000304f054686520776569676874206c696d697420666f722068616e646c696e6720646f776e77617264206d657373616765732077617320726561636865642e484f766572776569676874456e7175657565641001306d6573736167655f6861736804011c58636d486173680001286d6573736167655f696404011c58636d486173680001406f7665727765696768745f696e6465782c013c4f766572776569676874496e64657800013c72657175697265645f7765696768742401185765696768740004041901446f776e77617264206d657373616765206973206f76657277656967687420616e642077617320706c6163656420696e20746865206f7665727765696768742071756575652e484f76657277656967687453657276696365640801406f7665727765696768745f696e6465782c013c4f766572776569676874496e64657800012c7765696768745f75736564240118576569676874000504e0446f776e77617264206d6573736167652066726f6d20746865206f766572776569676874207175657565207761732065786563757465642e504d61784d657373616765734578686175737465640401306d6573736167655f6861736804011c58636d48617368000604d0546865206d6178696d756d206e756d626572206f6620646f776e77617264206d657373616765732077617320726561636865642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d1050c5070616c6c65745f636f6e66696775726174696f6e1870616c6c6574144576656e7404045400010c4c4e657744657369726564436f6c6c61746f7273040144646573697265645f636f6c6c61746f72732503012c4f7074696f6e3c7533323e000000584e6577436f6c6c61746f724c6963656e7365426f6e64040124626f6e645f636f7374250501484f7074696f6e3c543a3a42616c616e63653e000100604e6577436f6c6c61746f724b69636b5468726573686f6c640401406c656e6774685f696e5f626c6f636b73250301644f7074696f6e3c426c6f636b4e756d626572466f723c543e3e000200047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d5050c3470616c6c65745f636f6d6d6f6e1870616c6c6574144576656e7404045400015844436f6c6c656374696f6e437265617465640c0089040130436f6c6c656374696f6e496404dc476c6f62616c6c7920756e69717565206964656e746966696572206f66206e65776c79206372656174656420636f6c6c656374696f6e2e000801087538049c5b60436f6c6c656374696f6e4d6f6465605d20636f6e76657274656420696e746f205f75385f2e00000130543a3a4163636f756e7449640444436f6c6c656374696f6e206f776e65722e0004684e657720636f6c6c656374696f6e2077617320637265617465644c436f6c6c656374696f6e44657374726f796564040089040130436f6c6c656374696f6e496404a4476c6f62616c6c7920756e69717565206964656e746966696572206f6620636f6c6c656374696f6e2e0104704e657720636f6c6c656374696f6e207761732064657374726f7965642c4974656d43726561746564100089040130436f6c6c656374696f6e496404b04964206f662074686520636f6c6c656374696f6e207768657265206974656d2077617320637265617465642e00d904011c546f6b656e496404b04964206f6620616e206974656d2e20556e697175652077697468696e2074686520636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964046c4f776e6572206f66206e65776c792063726561746564206974656d00180110753132380440416c77617973203120666f72204e46540204544e6577206974656d2077617320637265617465642e344974656d44657374726f796564100089040130436f6c6c656374696f6e496404b84964206f662074686520636f6c6c656374696f6e207768657265206974656d207761732064657374726f7965642e00d904011c546f6b656e496404644964656e746966696572206f66206275726e6564204e46542e004d040144543a3a43726f73734163636f756e744964049057686963682075736572206861732064657374726f7965642069747320746f6b656e732e001801107531323804c8416d6f756e74206f6620746f6b656e207069656365732064657374726f65642e20416c77617973203120666f72204e46542e03046c436f6c6c656374696f6e206974656d20776173206275726e65642e205472616e73666572140089040130436f6c6c656374696f6e496404a44964206f6620636f6c6c656374696f6e20746f207768696368206974656d2069732062656c6f6e672e00d904011c546f6b656e496404384964206f6620616e206974656d2e004d040144543a3a43726f73734163636f756e744964045c4f726967696e616c206f776e6572206f66206974656d2e004d040144543a3a43726f73734163636f756e74496404484e6577206f776e6572206f66206974656d2e001801107531323804d0416d6f756e74206f6620746f6b656e20706965636573207472616e7366657265642e20416c77617973203120666f72204e46542e0404504974656d20776173207472616e7366657272656420417070726f766564140089040130436f6c6c656374696f6e496404a44964206f6620636f6c6c656374696f6e20746f207768696368206974656d2069732062656c6f6e672e00d904011c546f6b656e496404384964206f6620616e206974656d2e004d040144543a3a43726f73734163636f756e744964045c4f726967696e616c206f776e6572206f66206974656d2e004d040144543a3a43726f73734163636f756e7449640498496420666f722077686963682074686520617070726f76616c20776173206772616e7465642e001801107531323804d0416d6f756e74206f6620746f6b656e20706965636573207472616e7366657265642e20416c77617973203120666f72204e46542e05041101416d6f756e7420706965636573206f6620746f6b656e206f776e6564206279206073656e646572602077617320617070726f76656420666f7220607370656e646572602e38417070726f766564466f72416c6c100089040130436f6c6c656374696f6e496404a44964206f6620636f6c6c656374696f6e20746f207768696368206974656d2069732062656c6f6e672e004d040144543a3a43726f73734163636f756e74496404484f776e6572206f6620612077616c6c65742e004d040144543a3a43726f73734163636f756e74496404d0496420666f72207768696368206f70657261746f722073746174757320776173206772616e746564206f72207265776f6b65642e0035010110626f6f6c04984973206f70657261746f7220737461747573206772616e746564206f72207265766f6b65643f0604050141206073656e6465726020617070726f766573206f7065726174696f6e73206f6e20616c6c206f776e656420746f6b656e7320666f7220607370656e646572602e54436f6c6c656374696f6e50726f7065727479536574080089040130436f6c6c656374696f6e496404c04964206f6620636f6c6c656374696f6e20746f2077686963682070726f706572747920686173206265656e207365742e009d04012c50726f70657274794b657904685468652070726f7065727479207468617420776173207365742e0704c054686520636f6c6c6574696f6e2070726f706572747920686173206265656e206164646564206f72206564697465642e64436f6c6c656374696f6e50726f706572747944656c65746564080089040130436f6c6c656374696f6e496404d04964206f6620636f6c6c656374696f6e20746f2077686963682070726f706572747920686173206265656e2064656c657465642e009d04012c50726f70657274794b657904785468652070726f70657274792074686174207761732064656c657465642e0804785468652070726f706572747920686173206265656e2064656c657465642e40546f6b656e50726f70657274795365740c0089040130436f6c6c656374696f6e496404f84964656e746966696572206f662074686520636f6c6c656374696f6e2077686f736520746f6b656e20686173207468652070726f7065727479207365742e00d904011c546f6b656e496404a454686520746f6b656e20666f72207768696368207468652070726f706572747920776173207365742e009d04012c50726f70657274794b657904685468652070726f7065727479207468617420776173207365742e0904b054686520746f6b656e2070726f706572747920686173206265656e206164646564206f72206564697465642e50546f6b656e50726f706572747944656c657465640c0089040130436f6c6c656374696f6e49640409014964656e746966696572206f662074686520636f6c6c656374696f6e2077686f736520746f6b656e20686173207468652070726f70657274792064656c657465642e00d904011c546f6b656e496404b454686520746f6b656e20666f72207768696368207468652070726f7065727479207761732064656c657465642e009d04012c50726f70657274794b657904785468652070726f70657274792074686174207761732064656c657465642e0a049054686520746f6b656e2070726f706572747920686173206265656e2064656c657465642e5450726f70657274795065726d697373696f6e536574080089040130436f6c6c656374696f6e496404ec4944206f6620636f6c6c656374696f6e20746f2077686963682070726f7065727479207065726d697373696f6e20686173206265656e207365742e009d04012c50726f70657274794b657904945468652070726f7065727479207065726d697373696f6e207468617420776173207365742e0b04ec54686520746f6b656e2070726f7065727479207065726d697373696f6e206f66206120636f6c6c656374696f6e20686173206265656e207365742e54416c6c6f774c697374416464726573734164646564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964047441646472657373206f6620746865206164646564206163636f756e742e0c0490416464726573732077617320616464656420746f2074686520616c6c6f77206c6973742e5c416c6c6f774c6973744164647265737352656d6f766564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964047c41646472657373206f66207468652072656d6f766564206163636f756e742e0d04a041646472657373207761732072656d6f7665642066726f6d2074686520616c6c6f77206c6973742e50436f6c6c656374696f6e41646d696e4164646564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964043841646d696e20616464726573732e0e046c436f6c6c656374696f6e2061646d696e207761732061646465642e58436f6c6c656374696f6e41646d696e52656d6f766564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e004d040144543a3a43726f73734163636f756e744964045852656d6f7665642061646d696e20616464726573732e0f0474436f6c6c656374696f6e2061646d696e207761732072656d6f7665642e48436f6c6c656374696f6e4c696d6974536574040089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e10046c436f6c6c656374696f6e206c696d6974732077657265207365742e58436f6c6c656374696f6e4f776e65724368616e676564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404484e6577206f776e657220616464726573732e110474436f6c6c656374696f6e206f776e656420776173206368616e6765642e5c436f6c6c656374696f6e5065726d697373696f6e536574040089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e120480436f6c6c656374696f6e207065726d697373696f6e732077657265207365742e50436f6c6c656374696f6e53706f6e736f72536574080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e13046c436f6c6c656374696f6e2073706f6e736f7220776173207365742e5053706f6e736f7273686970436f6e6669726d6564080089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e1404604e65772073706f6e736f722077617320636f6e6669726d2e60436f6c6c656374696f6e53706f6e736f7252656d6f766564040089040130436f6c6c656374696f6e496404784944206f662074686520616666656374656420636f6c6c656374696f6e2e15047c436f6c6c656374696f6e2073706f6e736f72207761732072656d6f7665642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574d9050c4070616c6c65745f7374727563747572651870616c6c6574144576656e740404540001042045786563757465640400a801384469737061746368526573756c740000049445786563757465642063616c6c206f6e20626568616c66206f662074686520746f6b656e2e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574dd050c5070616c6c65745f6170705f70726f6d6f74696f6e1870616c6c6574144576656e74040454000110505374616b696e67526563616c63756c6174696f6e0c00000130543a3a4163636f756e7449640458416e20726563616c63756c61746564207374616b65720018013042616c616e63654f663c543e049042617365206f6e20776869636820696e7465726573742069732063616c63756c617465640018013042616c616e63654f663c543e0468416d6f756e74206f66206163637275656420696e74657265737400188c5374616b696e6720726563616c63756c6174696f6e2077617320706572666f726d6564002c2320417267756d656e74738c2a204163636f756e7449643a206163636f756e74206f6620746865207374616b65722e782a2042616c616e6365203a20726563616c63756c6174696f6e2062617365602a2042616c616e6365203a20746f74616c20696e636f6d65145374616b650800000130543a3a4163636f756e744964000018013042616c616e63654f663c543e000114545374616b696e672077617320706572666f726d6564002c2320417267756d656e7473882a204163636f756e7449643a206163636f756e74206f6620746865207374616b6572682a2042616c616e6365203a207374616b696e6720616d6f756e741c556e7374616b650800000130543a3a4163636f756e744964000018013042616c616e63654f663c543e0002145c556e7374616b696e672077617320706572666f726d6564002c2320417267756d656e7473882a204163636f756e7449643a206163636f756e74206f6620746865207374616b6572702a2042616c616e6365203a20756e7374616b696e6720616d6f756e742053657441646d696e0400000130543a3a4163636f756e744964000310445468652061646d696e2077617320736574002c2320417267756d656e7473a42a204163636f756e7449643a206163636f756e742061646472657373206f66207468652061646d696e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e1050c5470616c6c65745f666f726569676e5f617373657473186d6f64756c65144576656e7404045400011058466f726569676e4173736574526567697374657265640c012061737365745f6964100138466f726569676e4173736574496400013461737365745f61646472657373d401344d756c74694c6f636174696f6e0001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0000047454686520666f726569676e20617373657420726567697374657265642e4c466f726569676e4173736574557064617465640c012061737365745f6964100138466f726569676e4173736574496400013461737365745f61646472657373d401344d756c74694c6f636174696f6e0001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0001046854686520666f726569676e20617373657420757064617465642e3c41737365745265676973746572656408012061737365745f69640d01011c417373657449640001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0002045454686520617373657420726567697374657265642e3041737365745570646174656408012061737365745f69640d01011c417373657449640001206d657461646174613905016c41737365744d657461646174613c42616c616e63654f663c543e3e0003044854686520617373657420757064617465642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e5050c2870616c6c65745f65766d1870616c6c6574144576656e740404540001140c4c6f6704010c6c6f67a105010c4c6f670000047c457468657265756d206576656e74732066726f6d20636f6e7472616374732e1c4372656174656404011c616464726573736503011048313630000104b44120636f6e747261637420686173206265656e206372656174656420617420676976656e20616464726573732e34437265617465644661696c656404011c61646472657373650301104831363000020405014120636f6e74726163742077617320617474656d7074656420746f20626520637265617465642c206275742074686520657865637574696f6e206661696c65642e20457865637574656404011c616464726573736503011048313630000304f84120636f6e747261637420686173206265656e206578656375746564207375636365737366756c6c79207769746820737461746573206170706c6965642e3845786563757465644661696c656404011c61646472657373650301104831363000040465014120636f6e747261637420686173206265656e2065786563757465642077697468206572726f72732e20537461746573206172652072657665727465642077697468206f6e6c79206761732066656573206170706c6965642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574e9050c3c70616c6c65745f657468657265756d1870616c6c6574144576656e7400010420457865637574656414011066726f6d6503011048313630000108746f65030110483136300001407472616e73616374696f6e5f686173683001104832353600012c657869745f726561736f6eed05012845786974526561736f6e00012865787472615f6461746134011c5665633c75383e000004c8416e20657468657265756d207472616e73616374696f6e20776173207375636365737366756c6c792065786563757465642e047c54686520604576656e746020656e756d206f6620746869732070616c6c6574ed050c2065766d5f636f7265146572726f722845786974526561736f6e0001101c537563636565640400f105012c4578697453756363656564000000144572726f720400f5050124457869744572726f72000100185265766572740400050601284578697452657665727400020014466174616c04000906012445786974466174616c00030000f1050c2065766d5f636f7265146572726f722c457869745375636365656400010c1c53746f707065640000002052657475726e656400010020537569636964656400020000f5050c2065766d5f636f7265146572726f7224457869744572726f7200014038537461636b556e646572666c6f7700000034537461636b4f766572666c6f770001002c496e76616c69644a756d7000020030496e76616c696452616e67650003004444657369676e61746564496e76616c69640004002c43616c6c546f6f446565700005003c437265617465436f6c6c6973696f6e0006004c437265617465436f6e74726163744c696d69740007002c496e76616c6964436f64650400f90501184f70636f6465000f002c4f75744f664f6666736574000800204f75744f66476173000900244f75744f6646756e64000a002c5043556e646572666c6f77000b002c437265617465456d707479000c00144f746865720400fd050144436f773c277374617469632c207374723e000d00204d61784e6f6e6365000e0000f9050c2065766d5f636f7265186f70636f6465184f70636f64650000040008010875380000fd05040c436f7704045401010600040001060000000106000005020005060c2065766d5f636f7265146572726f7228457869745265766572740001042052657665727465640000000009060c2065766d5f636f7265146572726f722445786974466174616c000110304e6f74537570706f7274656400000048556e68616e646c6564496e746572727570740001004043616c6c4572726f724173466174616c0400f5050124457869744572726f72000200144f746865720400fd050144436f773c277374617469632c207374723e000300000d060c6c70616c6c65745f65766d5f636f6e74726163745f68656c706572731870616c6c6574144576656e7404045400010c48436f6e747261637453706f6e736f725365740800650301104831363004b0436f6e74726163742061646472657373206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e000464436f6e74726163742073706f6e736f7220776173207365742e70436f6e747261637453706f6e736f7273686970436f6e6669726d65640800650301104831363004b0436f6e74726163742061646472657373206f662074686520616666656374656420636f6c6c656374696f6e2e00000130543a3a4163636f756e74496404504e65772073706f6e736f7220616464726573732e0104604e65772073706f6e736f722077617320636f6e6669726d2e58436f6e747261637453706f6e736f7252656d6f7665640400650301104831363004b0436f6e74726163742061646472657373206f662074686520616666656374656420636f6c6c656374696f6e2e02047c436f6c6c656374696f6e2073706f6e736f72207761732072656d6f7665642e047c54686520604576656e746020656e756d206f6620746869732070616c6c657411060c5070616c6c65745f65766d5f6d6967726174696f6e1870616c6c6574144576656e7404045400010424546573744576656e74000004f054686973206576656e74206973207573656420696e2062656e63686d61726b696e6720616e642063616e206265207573656420666f72207465737473047c54686520604576656e746020656e756d206f6620746869732070616c6c657415060c4870616c6c65745f6d61696e74656e616e63651870616c6c6574144576656e74040454000108484d61696e74656e616e6365456e61626c65640000004c4d61696e74656e616e636544697361626c6564000100047c54686520604576656e746020656e756d206f6620746869732070616c6c657419060c3870616c6c65745f7574696c6974791870616c6c6574144576656e74000118404261746368496e746572727570746564080114696e64657810010c7533320001146572726f7264013444697370617463684572726f7200000855014261746368206f66206469737061746368657320646964206e6f7420636f6d706c6574652066756c6c792e20496e646578206f66206669727374206661696c696e6720646973706174636820676976656e2c2061734877656c6c20617320746865206572726f722e384261746368436f6d706c65746564000104c84261746368206f66206469737061746368657320636f6d706c657465642066756c6c792077697468206e6f206572726f722e604261746368436f6d706c65746564576974684572726f7273000204b44261746368206f66206469737061746368657320636f6d706c657465642062757420686173206572726f72732e344974656d436f6d706c657465640003041d01412073696e676c65206974656d2077697468696e2061204261746368206f6620646973706174636865732068617320636f6d706c657465642077697468206e6f206572726f722e284974656d4661696c65640401146572726f7264013444697370617463684572726f720004041101412073696e676c65206974656d2077697468696e2061204261746368206f6620646973706174636865732068617320636f6d706c657465642077697468206572726f722e30446973706174636865644173040118726573756c74a801384469737061746368526573756c7400050458412063616c6c2077617320646973706174636865642e047c54686520604576656e746020656e756d206f6620746869732070616c6c65741d060c4470616c6c65745f746573745f7574696c731870616c6c6574144576656e7404045400010c2856616c756549735365740000003853686f756c64526f6c6c6261636b000100384261746368436f6d706c65746564000200047c54686520604576656e746020656e756d206f6620746869732070616c6c6574210608306672616d655f73797374656d14506861736500010c384170706c7945787472696e736963040010010c7533320000003046696e616c697a6174696f6e00010038496e697469616c697a6174696f6e000200002506000002790300290608306672616d655f73797374656d584c61737452756e74696d6555706772616465496e666f0000080130737065635f76657273696f6ee0014c636f6465633a3a436f6d706163743c7533323e000124737065635f6e616d650106016473705f72756e74696d653a3a52756e74696d65537472696e6700002d060c306672616d655f73797374656d186c696d69747330426c6f636b5765696768747300000c0128626173655f626c6f636b2401185765696768740001246d61785f626c6f636b2401185765696768740001247065725f636c617373310601845065724469737061746368436c6173733c57656967687473506572436c6173733e000031060c346672616d655f737570706f7274206469737061746368405065724469737061746368436c617373040454013506000c01186e6f726d616c350601045400012c6f7065726174696f6e616c35060104540001246d616e6461746f72793506010454000035060c306672616d655f73797374656d186c696d6974733c57656967687473506572436c6173730000100138626173655f65787472696e7369632401185765696768740001346d61785f65787472696e736963390601384f7074696f6e3c5765696768743e0001246d61785f746f74616c390601384f7074696f6e3c5765696768743e0001207265736572766564390601384f7074696f6e3c5765696768743e0000390604184f7074696f6e04045401240108104e6f6e6500000010536f6d6504002400000100003d060c306672616d655f73797374656d186c696d6974732c426c6f636b4c656e677468000004010c6d6178410601545065724469737061746368436c6173733c7533323e000041060c346672616d655f737570706f7274206469737061746368405065724469737061746368436c6173730404540110000c01186e6f726d616c1001045400012c6f7065726174696f6e616c100104540001246d616e6461746f72791001045400004506082873705f776569676874733c52756e74696d6544625765696768740000080110726561642c010c75363400011477726974652c010c75363400004906082873705f76657273696f6e3852756e74696d6556657273696f6e0000200124737065635f6e616d650106013452756e74696d65537472696e67000124696d706c5f6e616d650106013452756e74696d65537472696e67000144617574686f72696e675f76657273696f6e10010c753332000130737065635f76657273696f6e10010c753332000130696d706c5f76657273696f6e10010c753332000110617069734d06011c4170697356656300014c7472616e73616374696f6e5f76657273696f6e10010c75333200013473746174655f76657273696f6e080108753800004d06040c436f77040454015106000400510600000051060000025506005506000004080101100059060c306672616d655f73797374656d1870616c6c6574144572726f720404540001183c496e76616c6964537065634e616d650000081101546865206e616d65206f662073706563696669636174696f6e20646f6573206e6f74206d61746368206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e685370656356657273696f6e4e65656473546f496e63726561736500010841015468652073706563696669636174696f6e2076657273696f6e206973206e6f7420616c6c6f77656420746f206465637265617365206265747765656e207468652063757272656e742072756e74696d6550616e6420746865206e65772072756e74696d652e744661696c6564546f4578747261637452756e74696d6556657273696f6e00020cec4661696c656420746f2065787472616374207468652072756e74696d652076657273696f6e2066726f6d20746865206e65772072756e74696d652e0009014569746865722063616c6c696e672060436f72655f76657273696f6e60206f72206465636f64696e67206052756e74696d6556657273696f6e60206661696c65642e4c4e6f6e44656661756c74436f6d706f73697465000304fc537569636964652063616c6c6564207768656e20746865206163636f756e7420686173206e6f6e2d64656661756c7420636f6d706f7369746520646174612e3c4e6f6e5a65726f526566436f756e74000404350154686572652069732061206e6f6e2d7a65726f207265666572656e636520636f756e742070726576656e74696e6720746865206163636f756e742066726f6d206265696e67207075726765642e3043616c6c46696c7465726564000504d0546865206f726967696e2066696c7465722070726576656e74207468652063616c6c20746f20626520646973706174636865642e046c4572726f7220666f72207468652053797374656d2070616c6c65745d0600000261060061060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e7420416e636573746f720404480130000c0138757365645f62616e647769647468650601345573656442616e647769647468000138706172615f686561645f68617368290301244f7074696f6e3c483e000160636f6e73756d65645f676f5f61686561645f7369676e616c7906018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e000065060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74345573656442616e64776964746800000c0134756d705f6d73675f636f756e7410010c75333200013c756d705f746f74616c5f627974657310010c75333200013468726d705f6f7574676f696e676906018c42547265654d61703c5061726149642c2048726d704368616e6e656c5570646174653e00006906042042547265654d617008044b01ad010456016d0600040071060000006d060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e744448726d704368616e6e656c55706461746500000801246d73675f636f756e7410010c75333200012c746f74616c5f627974657310010c75333200007106000002750600750600000408ad016d0600790604184f7074696f6e040454017d060108104e6f6e6500000010536f6d6504007d0600000100007d060c4c706f6c6b61646f745f7072696d6974697665730876353855706772616465476f41686561640001081441626f72740000001c476f41686561640001000081060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d48756e696e636c756465645f7365676d656e74385365676d656e74547261636b65720404480130000c0138757365645f62616e647769647468650601345573656442616e64776964746800013868726d705f77617465726d61726b250301804f7074696f6e3c72656c61795f636861696e3a3a426c6f636b4e756d6265723e000160636f6e73756d65645f676f5f61686561645f7369676e616c7906018c4f7074696f6e3c72656c61795f636861696e3a3a55706772616465476f41686561643e0000850604184f7074696f6e0404540189060108104e6f6e6500000010536f6d6504008906000001000089060c4c706f6c6b61646f745f7072696d69746976657308763548557067726164655265737472696374696f6e0001041c50726573656e74000000008d060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f74584d6573736167696e675374617465536e617073686f740000100130646d715f6d71635f6865616430014472656c61795f636861696e3a3a4861736800019c72656c61795f64697370617463685f71756575655f72656d61696e696e675f63617061636974799106018c52656c61794469737061746368517565756552656d61696e696e674361706163697479000140696e67726573735f6368616e6e656c73950601885665633c285061726149642c20416272696467656448726d704368616e6e656c293e00013c6567726573735f6368616e6e656c73950601885665633c285061726149642c20416272696467656448726d704368616e6e656c293e000091060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d5072656c61795f73746174655f736e617073686f748c52656c61794469737061746368517565756552656d61696e696e674361706163697479000008013c72656d61696e696e675f636f756e7410010c75333200013872656d61696e696e675f73697a6510010c75333200009506000002990600990600000408ad019d06009d060c4c706f6c6b61646f745f7072696d6974697665730876354c416272696467656448726d704368616e6e656c00001801306d61785f636170616369747910010c7533320001386d61785f746f74616c5f73697a6510010c7533320001406d61785f6d6573736167655f73697a6510010c7533320001246d73675f636f756e7410010c753332000128746f74616c5f73697a6510010c7533320001206d71635f68656164290301304f7074696f6e3c486173683e0000a1060c4c706f6c6b61646f745f7072696d697469766573087635644162726964676564486f7374436f6e66696775726174696f6e00002801346d61785f636f64655f73697a6510010c7533320001486d61785f686561645f646174615f73697a6510010c7533320001586d61785f7570776172645f71756575655f636f756e7410010c7533320001546d61785f7570776172645f71756575655f73697a6510010c75333200015c6d61785f7570776172645f6d6573736167655f73697a6510010c7533320001906d61785f7570776172645f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200018868726d705f6d61785f6d6573736167655f6e756d5f7065725f63616e64696461746510010c75333200016c76616c69646174696f6e5f757067726164655f636f6f6c646f776e10012c426c6f636b4e756d62657200016076616c69646174696f6e5f757067726164655f64656c617910012c426c6f636b4e756d6265720001506173796e635f6261636b696e675f706172616d73a506018c73757065723a3a7673746167696e673a3a4173796e634261636b696e67506172616d730000a5060c4c706f6c6b61646f745f7072696d697469766573207673746167696e67484173796e634261636b696e67506172616d73000008014c6d61785f63616e6469646174655f646570746810010c753332000150616c6c6f7765645f616e6365737472795f6c656e10010c7533320000a906089463756d756c75735f7072696d6974697665735f70617261636861696e5f696e686572656e74444d6573736167655175657565436861696e0000040030012452656c6179486173680000ad06042042547265654d617008044b01ad01045601a906000400b106000000b106000002b50600b50600000408ad01a90600b906000002bd0600bd060860706f6c6b61646f745f636f72655f7072696d6974697665734c4f7574626f756e6448726d704d6573736167650408496401ad0100080124726563697069656e74ad01010849640001106461746134015073705f7374643a3a7665633a3a5665633c75383e0000c106087c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d60436f646555706772616465417574686f72697a6174696f6e0404540000080124636f64655f6861736830011c543a3a48617368000134636865636b5f76657273696f6e35010110626f6f6c0000c5060c7c63756d756c75735f70616c6c65745f70617261636861696e5f73797374656d1870616c6c6574144572726f720404540001204c4f7665726c617070696e6755706772616465730000041901417474656d707420746f20757067726164652076616c69646174696f6e2066756e6374696f6e207768696c65206578697374696e6720757067726164652070656e64696e672e5050726f686962697465644279506f6c6b61646f740001044d01506f6c6b61646f742063757272656e746c792070726f68696269747320746869732070617261636861696e2066726f6d20757067726164696e67206974732076616c69646174696f6e2066756e6374696f6e2e18546f6f426967000208450154686520737570706c6965642076616c69646174696f6e2066756e6374696f6e2068617320636f6d70696c656420696e746f206120626c6f62206c6172676572207468616e20506f6c6b61646f742069733c77696c6c696e6720746f2072756e2e6856616c69646174696f6e446174614e6f74417661696c61626c650003041d0154686520696e686572656e7420776869636820737570706c696573207468652076616c69646174696f6e206461746120646964206e6f742072756e207468697320626c6f636b2e74486f7374436f6e66696775726174696f6e4e6f74417661696c61626c65000404290154686520696e686572656e7420776869636820737570706c6965732074686520686f737420636f6e66696775726174696f6e20646964206e6f742072756e207468697320626c6f636b2e304e6f745363686564756c6564000504d84e6f2076616c69646174696f6e2066756e6374696f6e20757067726164652069732063757272656e746c79207363686564756c65642e444e6f7468696e67417574686f72697a6564000604904e6f20636f6465207570677261646520686173206265656e20617574686f72697a65642e30556e617574686f72697a6564000704bc54686520676976656e20636f6465207570677261646520686173206e6f74206265656e20617574686f72697a65642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ec9060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e0000cd060c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c6574144572726f7204045400013444546f6f4d616e7943616e646964617465730000044c546f6f206d616e792063616e646964617465731c556e6b6e6f776e00010434556e6b6e6f776e206572726f72285065726d697373696f6e000204405065726d697373696f6e20697373756554416c7265616479486f6c64696e674c6963656e7365000304945573657220616c726561647920686f6c6473206c6963656e736520746f20636f6c6c617465244e6f4c6963656e73650004049c5573657220646f6573206e6f7420686f6c642061206c6963656e736520746f20636f6c6c61746540416c726561647943616e6469646174650005046c5573657220697320616c726561647920612063616e646964617465304e6f7443616e6469646174650006045c55736572206973206e6f7420612063616e64696461746550546f6f4d616e79496e76756c6e657261626c657300070458546f6f206d616e7920696e76756c6e657261626c65734c546f6f466577496e76756c6e657261626c657300080454546f6f2066657720696e76756c6e657261626c65734c416c7265616479496e76756c6e657261626c650009047c5573657220697320616c726561647920616e20496e76756c6e657261626c653c4e6f74496e76756c6e657261626c65000a046c55736572206973206e6f7420616e20496e76756c6e657261626c655c4e6f4173736f63696174656456616c696461746f724964000b04984163636f756e7420686173206e6f206173736f6369617465642076616c696461746f722049445856616c696461746f724e6f7452656769737465726564000c048856616c696461746f72204944206973206e6f74207965742072656769737465726564048054686520604572726f726020656e756d206f6620746869732070616c6c65742ed106000002d50600d5060000040800cd0100d90600000408dd063400dd060c1c73705f636f72651863727970746f244b65795479706549640000040044011c5b75383b20345d0000e1060c3870616c6c65745f73657373696f6e1870616c6c6574144572726f7204045400011430496e76616c696450726f6f6600000460496e76616c6964206f776e6572736869702070726f6f662e5c4e6f4173736f63696174656456616c696461746f7249640001049c4e6f206173736f6369617465642076616c696461746f7220494420666f72206163636f756e742e344475706c6963617465644b65790002046452656769737465726564206475706c6963617465206b65792e184e6f4b657973000304a44e6f206b65797320617265206173736f63696174656420776974682074686973206163636f756e742e244e6f4163636f756e7400040419014b65792073657474696e67206163636f756e74206973206e6f74206c6976652c20736f206974277320696d706f737369626c6520746f206173736f6369617465206b6579732e04744572726f7220666f72207468652073657373696f6e2070616c6c65742ee5060c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401d101045300000400e90601185665633c543e0000e906000002d10100ed06084873705f636f6e73656e7375735f736c6f747310536c6f74000004002c010c7536340000f10600000408ed061000f5060c4c626f756e6465645f636f6c6c656374696f6e73407765616b5f626f756e6465645f766563385765616b426f756e64656456656308045401f906045300000400010701185665633c543e0000f9060c3c70616c6c65745f62616c616e6365731474797065732c42616c616e63654c6f636b041c42616c616e63650118000c01086964010101384c6f636b4964656e746966696572000118616d6f756e7418011c42616c616e636500011c726561736f6e73fd06011c526561736f6e730000fd060c3c70616c6c65745f62616c616e6365731474797065731c526561736f6e7300010c0c466565000000104d6973630001000c416c6c000200000107000002f9060005070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540109070453000004000d0701185665633c543e000009070c3c70616c6c65745f62616c616e6365731474797065732c52657365727665446174610844526573657276654964656e7469666965720105011c42616c616e6365011800080108696405010144526573657276654964656e746966696572000118616d6f756e7418011c42616c616e636500000d0700000209070011070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454011507045300000400210701185665633c543e000015070c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e74080849640119071c42616c616e63650118000801086964190701084964000118616d6f756e7418011c42616c616e63650000190708306f70616c5f72756e74696d654452756e74696d65486f6c64526561736f6e00010444436f6c6c61746f7253656c656374696f6e04001d07019470616c6c65745f636f6c6c61746f725f73656c656374696f6e3a3a486f6c64526561736f6e001700001d070c6470616c6c65745f636f6c6c61746f725f73656c656374696f6e1870616c6c657428486f6c64526561736f6e0001042c4c6963656e7365426f6e6400000000210700000215070025070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540129070453000004002d0701185665633c543e000029070c3c70616c6c65745f62616c616e636573147479706573204964416d6f756e74080849640105011c42616c616e63650118000801086964050101084964000118616d6f756e7418011c42616c616e636500002d0700000229070031070c3c70616c6c65745f62616c616e6365731870616c6c6574144572726f720804540004490001283856657374696e6742616c616e63650000049c56657374696e672062616c616e636520746f6f206869676820746f2073656e642076616c75652e544c69717569646974795265737472696374696f6e73000104c84163636f756e74206c6971756964697479207265737472696374696f6e732070726576656e74207769746864726177616c2e4c496e73756666696369656e7442616c616e63650002047842616c616e636520746f6f206c6f7720746f2073656e642076616c75652e484578697374656e7469616c4465706f736974000304ec56616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742e34457870656e646162696c697479000404905472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e742e5c4578697374696e6756657374696e675363686564756c65000504cc412076657374696e67207363686564756c6520616c72656164792065786973747320666f722074686973206163636f756e742e2c446561644163636f756e740006048c42656e6566696369617279206163636f756e74206d757374207072652d65786973742e3c546f6f4d616e795265736572766573000704b84e756d626572206f66206e616d65642072657365727665732065786365656420604d61785265736572766573602e30546f6f4d616e79486f6c6473000804884e756d626572206f6620686f6c64732065786365656420604d6178486f6c6473602e38546f6f4d616e79467265657a6573000904984e756d626572206f6620667265657a65732065786365656420604d6178467265657a6573602e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e35070c3473705f61726974686d657469632c66697865645f706f696e7424466978656455313238000004001801107531323800003907086870616c6c65745f7472616e73616374696f6e5f7061796d656e742052656c6561736573000108245631416e6369656e74000000085632000100003d07083c70616c6c65745f74726561737572792050726f706f73616c08244163636f756e74496401001c42616c616e636501180010012070726f706f7365720001244163636f756e74496400011476616c756518011c42616c616e636500012c62656e65666963696172790001244163636f756e744964000110626f6e6418011c42616c616e6365000041070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540110045300000400310501185665633c543e000045070c3473705f61726974686d65746963287065725f7468696e67731c5065726d696c6c0000040010010c7533320000490708346672616d655f737570706f72742050616c6c65744964000004000101011c5b75383b20385d00004d070c3c70616c6c65745f74726561737572791870616c6c6574144572726f7208045400044900011470496e73756666696369656e7450726f706f7365727342616c616e63650000047850726f706f73657227732062616c616e636520697320746f6f206c6f772e30496e76616c6964496e646578000104904e6f2070726f706f73616c206f7220626f756e7479206174207468617420696e6465782e40546f6f4d616e79417070726f76616c7300020480546f6f206d616e7920617070726f76616c7320696e207468652071756575652e58496e73756666696369656e745065726d697373696f6e0003084501546865207370656e64206f726967696e2069732076616c6964206275742074686520616d6f756e7420697420697320616c6c6f77656420746f207370656e64206973206c6f776572207468616e207468654c616d6f756e7420746f206265207370656e742e4c50726f706f73616c4e6f74417070726f7665640004047c50726f706f73616c20686173206e6f74206265656e20617070726f7665642e04784572726f7220666f72207468652074726561737572792070616c6c65742e51070c2c70616c6c65745f7375646f1870616c6c6574144572726f720404540001042c526571756972655375646f0000047c53656e646572206d75737420626520746865205375646f206163636f756e7404644572726f7220666f7220746865205375646f2070616c6c657455070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401b8045300000400f90101185665633c543e000059070c306f726d6c5f76657374696e67186d6f64756c65144572726f72040454000118445a65726f56657374696e67506572696f640000045856657374696e6720706572696f64206973207a65726f585a65726f56657374696e67506572696f64436f756e740001045c4e756d626572206f66207665737473206973207a65726f64496e73756666696369656e7442616c616e6365546f4c6f636b00020498496e73756666696369656e7420616d6f756e74206f662062616c616e636520746f206c6f636b5c546f6f4d616e7956657374696e675363686564756c6573000304b054686973206163636f756e74206861766520746f6f206d616e792076657374696e67207363686564756c657324416d6f756e744c6f770004049454686520766573746564207472616e7366657220616d6f756e7420697320746f6f206c6f776c4d617856657374696e675363686564756c65734578636565646564000504e44661696c6564206265636175736520746865206d6178696d756d2076657374696e67207363686564756c657320776173206578636565646564048054686520604572726f726020656e756d206f6620746869732070616c6c65742e5d070c306f726d6c5f78746f6b656e73186d6f64756c65144572726f7204045400014c4441737365744861734e6f5265736572766500000478417373657420686173206e6f2072657365727665206c6f636174696f6e2e544e6f7443726f7373436861696e5472616e73666572000104644e6f742063726f73732d636861696e207472616e736665722e2c496e76616c69644465737400020474496e76616c6964207472616e736665722064657374696e6174696f6e2e844e6f7443726f7373436861696e5472616e7366657261626c6543757272656e6379000304a443757272656e6379206973206e6f742063726f73732d636861696e207472616e7366657261626c652e48556e776569676861626c654d657373616765000404b4546865206d65737361676527732077656967687420636f756c64206e6f742062652064657465726d696e65642e4858636d457865637574696f6e4661696c65640005045458434d20657865637574696f6e206661696c65642e3843616e6e6f745265616e63686f72000608e8436f756c64206e6f742072652d616e63686f72207468652061737365747320746f206465636c61726520746865206665657320666f72207468654864657374696e6174696f6e20636861696e2e3c496e76616c6964416e636573747279000704c4436f756c64206e6f742067657420616e636573747279206f662061737365742072657365727665206c6f636174696f6e2e30496e76616c6964417373657400080468546865204d756c7469417373657420697320696e76616c69642e6044657374696e6174696f6e4e6f74496e7665727469626c65000904f05468652064657374696e6174696f6e20604d756c74694c6f636174696f6e602070726f76696465642063616e6e6f7420626520696e7665727465642e2842616456657273696f6e000a08ec5468652076657273696f6e206f6620746865206056657273696f6e6564602076616c75652075736564206973206e6f742061626c6520746f20626530696e7465727072657465642e7444697374696e637452657365727665466f724173736574416e64466565000b08fc57652074726965642073656e64696e672064697374696e637420617373657420616e6420666565206275742074686579206861766520646966666572656e743c7265736572766520636861696e732e1c5a65726f466565000c044054686520666565206973207a65726f2e285a65726f416d6f756e74000d0494546865207472616e73666572696e6720617373657420616d6f756e74206973207a65726f2e58546f6f4d616e794173736574734265696e6753656e74000e04d0546865206e756d626572206f662061737365747320746f2062652073656e74206973206f76657220746865206d6178696d756d2e544173736574496e6465784e6f6e4578697374656e74000f04ec5468652073706563696669656420696e64657820646f6573206e6f7420657869737420696e2061204d756c7469417373657473207374727563742e304665654e6f74456e6f75676800100448466565206973206e6f7420656e6f7567682e644e6f74537570706f727465644d756c74694c6f636174696f6e0011046c4e6f7420737570706f72746564204d756c74694c6f636174696f6e4c4d696e58636d4665654e6f74446566696e6564001204d44d696e58636d466565206e6f74207265676973746572656420666f72206365727461696e2072657365727665206c6f636174696f6e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e610700000408000d010065070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540169070453000004006d0701185665633c543e00006907082c6f726d6c5f746f6b656e732c42616c616e63654c6f636b041c42616c616e63650118000801086964010101384c6f636b4964656e746966696572000118616d6f756e7418011c42616c616e636500006d070000026907007107082c6f726d6c5f746f6b656e732c4163636f756e7444617461041c42616c616e63650118000c01106672656518011c42616c616e6365000120726573657276656418011c42616c616e636500011866726f7a656e18011c42616c616e6365000075070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540179070453000004007d0701185665633c543e00007907082c6f726d6c5f746f6b656e732c52657365727665446174610844526573657276654964656e74696669657201ac1c42616c616e63650118000801086964ac0144526573657276654964656e746966696572000118616d6f756e7418011c42616c616e636500007d0700000279070081070c2c6f726d6c5f746f6b656e73186d6f64756c65144572726f720404540001203442616c616e6365546f6f4c6f77000004585468652062616c616e636520697320746f6f206c6f775c416d6f756e74496e746f42616c616e63654661696c65640001049c43616e6e6f7420636f6e7665727420416d6f756e7420696e746f2042616c616e63652074797065544c69717569646974795265737472696374696f6e73000204d04661696c65642062656361757365206c6971756964697479207265737472696374696f6e732064756520746f206c6f636b696e67404d61784c6f636b734578636565646564000304b44661696c6564206265636175736520746865206d6178696d756d206c6f636b7320776173206578636565646564244b656570416c6976650004048c5472616e736665722f7061796d656e7420776f756c64206b696c6c206163636f756e74484578697374656e7469616c4465706f736974000504e856616c756520746f6f206c6f7720746f20637265617465206163636f756e742064756520746f206578697374656e7469616c206465706f7369742c446561644163636f756e740006048842656e6566696369617279206163636f756e74206d757374207072652d65786973743c546f6f4d616e795265736572766573000700048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8507000004081889070089070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e00008d070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454019107045300000400990701185665633c543e0000910704184f7074696f6e0404540195070108104e6f6e6500000010536f6d6504009507000001000095070c3c70616c6c65745f6964656e7469747914747970657334526567697374726172496e666f081c42616c616e63650118244163636f756e7449640100000c011c6163636f756e740001244163636f756e74496400010c66656518011c42616c616e63650001186669656c6473e50201384964656e746974794669656c6473000099070000029107009d070c3c70616c6c65745f6964656e746974791870616c6c6574144572726f7204045400014848546f6f4d616e795375624163636f756e74730000045c546f6f206d616e7920737562732d6163636f756e74732e204e6f74466f756e64000104504163636f756e742069736e277420666f756e642e204e6f744e616d6564000204504163636f756e742069736e2774206e616d65642e28456d707479496e64657800030430456d70747920696e6465782e284665654368616e6765640004043c466565206973206368616e6765642e284e6f4964656e74697479000504484e6f206964656e7469747920666f756e642e3c537469636b794a756467656d656e7400060444537469636b79206a756467656d656e742e384a756467656d656e74476976656e000704404a756467656d656e7420676976656e2e40496e76616c69644a756467656d656e7400080448496e76616c6964206a756467656d656e742e30496e76616c6964496e6465780009045454686520696e64657820697320696e76616c69642e34496e76616c6964546172676574000a04585468652074617267657420697320696e76616c69642e34546f6f4d616e794669656c6473000b046c546f6f206d616e79206164646974696f6e616c206669656c64732e44546f6f4d616e7952656769737472617273000c04e84d6178696d756d20616d6f756e74206f66207265676973747261727320726561636865642e2043616e6e6f742061646420616e79206d6f72652e38416c7265616479436c61696d6564000d04704163636f756e7420494420697320616c7265616479206e616d65642e184e6f74537562000e047053656e646572206973206e6f742061207375622d6163636f756e742e204e6f744f776e6564000f04885375622d6163636f756e742069736e2774206f776e65642062792073656e6465722e744a756467656d656e74466f72446966666572656e744964656e74697479001004d05468652070726f7669646564206a756467656d656e742077617320666f72206120646966666572656e74206964656e746974792e584a756467656d656e745061796d656e744661696c6564001104f84572726f722074686174206f6363757273207768656e20746865726520697320616e20697373756520706179696e6720666f72206a756467656d656e742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ea107083c70616c6c65745f707265696d616765345265717565737453746174757308244163636f756e74496401001c42616c616e6365011801082c556e72657175657374656408011c6465706f736974a5070150284163636f756e7449642c2042616c616e63652900010c6c656e10010c753332000000245265717565737465640c011c6465706f736974a90701704f7074696f6e3c284163636f756e7449642c2042616c616e6365293e000114636f756e7410010c75333200010c6c656e2503012c4f7074696f6e3c7533323e00010000a50700000408001800a90704184f7074696f6e04045401a5070108104e6f6e6500000010536f6d650400a5070000010000ad0700000408301000b1070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000b5070c3c70616c6c65745f707265696d6167651870616c6c6574144572726f7204045400011818546f6f426967000004a0507265696d61676520697320746f6f206c6172676520746f2073746f7265206f6e2d636861696e2e30416c72656164794e6f746564000104a4507265696d6167652068617320616c7265616479206265656e206e6f746564206f6e2d636861696e2e344e6f74417574686f72697a6564000204c85468652075736572206973206e6f7420617574686f72697a656420746f20706572666f726d207468697320616374696f6e2e204e6f744e6f746564000304fc54686520707265696d6167652063616e6e6f742062652072656d6f7665642073696e636520697420686173206e6f7420796574206265656e206e6f7465642e2452657175657374656400040409014120707265696d616765206d6179206e6f742062652072656d6f766564207768656e20746865726520617265206f75747374616e64696e672072657175657374732e304e6f745265717565737465640005042d0154686520707265696d61676520726571756573742063616e6e6f742062652072656d6f7665642073696e6365206e6f206f75747374616e64696e672072657175657374732065786973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742eb9070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401bd07045300000400c10701185665633c543e0000bd070000040c1059010000c107000002bd0700c50700000408c9071800c9070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e0000cd070c4070616c6c65745f64656d6f6372616379147479706573385265666572656e64756d496e666f0c2c426c6f636b4e756d62657201102050726f706f73616c0159011c42616c616e6365011801081c4f6e676f696e670400d10701c05265666572656e64756d5374617475733c426c6f636b4e756d6265722c2050726f706f73616c2c2042616c616e63653e0000002046696e6973686564080120617070726f76656435010110626f6f6c00010c656e6410012c426c6f636b4e756d62657200010000d1070c4070616c6c65745f64656d6f6372616379147479706573405265666572656e64756d5374617475730c2c426c6f636b4e756d62657201102050726f706f73616c0159011c42616c616e636501180014010c656e6410012c426c6f636b4e756d62657200012070726f706f73616c5901012050726f706f73616c0001247468726573686f6c6421010134566f74655468726573686f6c6400011464656c617910012c426c6f636b4e756d62657200011474616c6c79d507013854616c6c793c42616c616e63653e0000d5070c4070616c6c65745f64656d6f63726163791474797065731454616c6c79041c42616c616e63650118000c01106179657318011c42616c616e63650001106e61797318011c42616c616e636500011c7475726e6f757418011c42616c616e63650000d9070c4070616c6c65745f64656d6f637261637910766f746518566f74696e67101c42616c616e63650118244163636f756e74496401002c426c6f636b4e756d6265720110204d6178566f746573000108184469726563740c0114766f746573dd0701f4426f756e6465645665633c285265666572656e64756d496e6465782c204163636f756e74566f74653c42616c616e63653e292c204d6178566f7465733e00012c64656c65676174696f6e73e907015044656c65676174696f6e733c42616c616e63653e0001147072696f72ed07017c5072696f724c6f636b3c426c6f636b4e756d6265722c2042616c616e63653e0000002844656c65676174696e6714011c62616c616e636518011c42616c616e63650001187461726765740001244163636f756e744964000128636f6e76696374696f6e21030128436f6e76696374696f6e00012c64656c65676174696f6e73e907015044656c65676174696f6e733c42616c616e63653e0001147072696f72ed07017c5072696f724c6f636b3c426c6f636b4e756d6265722c2042616c616e63653e00010000dd070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e107045300000400e50701185665633c543e0000e1070000040810250100e507000002e10700e9070c4070616c6c65745f64656d6f63726163791474797065732c44656c65676174696f6e73041c42616c616e6365011800080114766f74657318011c42616c616e636500011c6361706974616c18011c42616c616e63650000ed070c4070616c6c65745f64656d6f637261637910766f7465245072696f724c6f636b082c426c6f636b4e756d62657201101c42616c616e6365011800080010012c426c6f636b4e756d626572000018011c42616c616e63650000f107000004085901210100f5070000040810c90700f9070c4070616c6c65745f64656d6f63726163791870616c6c6574144572726f720404540001602056616c75654c6f770000043456616c756520746f6f206c6f773c50726f706f73616c4d697373696e670001045c50726f706f73616c20646f6573206e6f742065786973743c416c726561647943616e63656c65640002049443616e6e6f742063616e63656c207468652073616d652070726f706f73616c207477696365444475706c696361746550726f706f73616c0003045450726f706f73616c20616c7265616479206d6164654c50726f706f73616c426c61636b6c69737465640004046850726f706f73616c207374696c6c20626c61636b6c6973746564444e6f7453696d706c654d616a6f72697479000504a84e6578742065787465726e616c2070726f706f73616c206e6f742073696d706c65206d616a6f726974792c496e76616c69644861736800060430496e76616c69642068617368284e6f50726f706f73616c000704504e6f2065787465726e616c2070726f706f73616c34416c72656164795665746f6564000804984964656e74697479206d6179206e6f74207665746f20612070726f706f73616c207477696365445265666572656e64756d496e76616c696400090484566f746520676976656e20666f7220696e76616c6964207265666572656e64756d2c4e6f6e6557616974696e67000a04504e6f2070726f706f73616c732077616974696e67204e6f74566f746572000b04c454686520676976656e206163636f756e7420646964206e6f7420766f7465206f6e20746865207265666572656e64756d2e304e6f5065726d697373696f6e000c04c8546865206163746f7220686173206e6f207065726d697373696f6e20746f20636f6e647563742074686520616374696f6e2e44416c726561647944656c65676174696e67000d0488546865206163636f756e7420697320616c72656164792064656c65676174696e672e44496e73756666696369656e7446756e6473000e04fc546f6f206869676820612062616c616e6365207761732070726f7669646564207468617420746865206163636f756e742063616e6e6f74206166666f72642e344e6f7444656c65676174696e67000f04a0546865206163636f756e74206973206e6f742063757272656e746c792064656c65676174696e672e28566f74657345786973740010085501546865206163636f756e742063757272656e746c792068617320766f74657320617474616368656420746f20697420616e6420746865206f7065726174696f6e2063616e6e6f74207375636365656420756e74696ce87468657365206172652072656d6f7665642c20656974686572207468726f7567682060756e766f746560206f722060726561705f766f7465602e44496e7374616e744e6f74416c6c6f776564001104d854686520696e7374616e74207265666572656e64756d206f726967696e2069732063757272656e746c7920646973616c6c6f7765642e204e6f6e73656e73650012049444656c65676174696f6e20746f206f6e6573656c66206d616b6573206e6f2073656e73652e3c57726f6e675570706572426f756e6400130450496e76616c696420757070657220626f756e642e3c4d6178566f74657352656163686564001404804d6178696d756d206e756d626572206f6620766f74657320726561636865642e1c546f6f4d616e79001504804d6178696d756d206e756d626572206f66206974656d7320726561636865642e3c566f74696e67506572696f644c6f7700160454566f74696e6720706572696f6420746f6f206c6f7740507265696d6167654e6f7445786973740017047054686520707265696d61676520646f6573206e6f742065786973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742efd070c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401300453000004005d0501185665633c543e00000108084470616c6c65745f636f6c6c65637469766514566f74657308244163636f756e74496401002c426c6f636b4e756d626572011000140114696e64657810013450726f706f73616c496e6465780001247468726573686f6c6410012c4d656d626572436f756e7400011061796573e50101385665633c4163636f756e7449643e0001106e617973e50101385665633c4163636f756e7449643e00010c656e6410012c426c6f636b4e756d626572000005080c4470616c6c65745f636f6c6c6563746976651870616c6c6574144572726f7208045400044900012c244e6f744d656d6265720000045c4163636f756e74206973206e6f742061206d656d626572444475706c696361746550726f706f73616c0001047c4475706c69636174652070726f706f73616c73206e6f7420616c6c6f7765643c50726f706f73616c4d697373696e670002044c50726f706f73616c206d7573742065786973742857726f6e67496e646578000304404d69736d61746368656420696e646578344475706c6963617465566f7465000404584475706c696361746520766f74652069676e6f72656448416c7265616479496e697469616c697a6564000504804d656d626572732061726520616c726561647920696e697469616c697a65642120546f6f4561726c79000604010154686520636c6f73652063616c6c20776173206d61646520746f6f206561726c792c206265666f72652074686520656e64206f662074686520766f74696e672e40546f6f4d616e7950726f706f73616c73000704fc54686572652063616e206f6e6c792062652061206d6178696d756d206f6620604d617850726f706f73616c7360206163746976652070726f706f73616c732e4c57726f6e6750726f706f73616c576569676874000804d054686520676976656e2077656967687420626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e4c57726f6e6750726f706f73616c4c656e677468000904d054686520676976656e206c656e67746820626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e545072696d654163636f756e744e6f744d656d626572000a04745072696d65206163636f756e74206973206e6f742061206d656d626572048054686520604572726f726020656e756d206f6620746869732070616c6c65742e09080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401300453000004005d0501185665633c543e00000d080c4470616c6c65745f636f6c6c6563746976651870616c6c6574144572726f7208045400044900012c244e6f744d656d6265720000045c4163636f756e74206973206e6f742061206d656d626572444475706c696361746550726f706f73616c0001047c4475706c69636174652070726f706f73616c73206e6f7420616c6c6f7765643c50726f706f73616c4d697373696e670002044c50726f706f73616c206d7573742065786973742857726f6e67496e646578000304404d69736d61746368656420696e646578344475706c6963617465566f7465000404584475706c696361746520766f74652069676e6f72656448416c7265616479496e697469616c697a6564000504804d656d626572732061726520616c726561647920696e697469616c697a65642120546f6f4561726c79000604010154686520636c6f73652063616c6c20776173206d61646520746f6f206561726c792c206265666f72652074686520656e64206f662074686520766f74696e672e40546f6f4d616e7950726f706f73616c73000704fc54686572652063616e206f6e6c792062652061206d6178696d756d206f6620604d617850726f706f73616c7360206163746976652070726f706f73616c732e4c57726f6e6750726f706f73616c576569676874000804d054686520676976656e2077656967687420626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e4c57726f6e6750726f706f73616c4c656e677468000904d054686520676976656e206c656e67746820626f756e6420666f72207468652070726f706f73616c2077617320746f6f206c6f772e545072696d654163636f756e744e6f744d656d626572000a04745072696d65206163636f756e74206973206e6f742061206d656d626572048054686520604572726f726020656e756d206f6620746869732070616c6c65742e11080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e000015080c4470616c6c65745f6d656d626572736869701870616c6c6574144572726f7208045400044900010c34416c72656164794d656d62657200000444416c72656164792061206d656d6265722e244e6f744d656d626572000104344e6f742061206d656d6265722e38546f6f4d616e794d656d6265727300020444546f6f206d616e79206d656d626572732e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e19080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e6465645665630804540100045300000400e50101185665633c543e00001d080c4470616c6c65745f6d656d626572736869701870616c6c6574144572726f7208045400044900010c34416c72656164794d656d62657200000444416c72656164792061206d656d6265722e244e6f744d656d626572000104344e6f742061206d656d6265722e38546f6f4d616e794d656d6265727300020444546f6f206d616e79206d656d626572732e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e2108086070616c6c65745f72616e6b65645f636f6c6c656374697665304d656d6265725265636f7264000004011072616e6b4901011052616e6b000025080000040849010000290800000408490110002d080000040810000031080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e000035080c6070616c6c65745f72616e6b65645f636f6c6c6563746976651870616c6c6574144572726f7208045400044900012434416c72656164794d656d626572000004704163636f756e7420697320616c72656164792061206d656d6265722e244e6f744d656d626572000104604163636f756e74206973206e6f742061206d656d6265722e284e6f74506f6c6c696e67000204b854686520676976656e20706f6c6c20696e64657820697320756e6b6e6f776e206f722068617320636c6f7365642e1c4f6e676f696e670003048054686520676976656e20706f6c6c206973207374696c6c206f6e676f696e672e344e6f6e6552656d61696e696e67000404ac546865726520617265206e6f2066757274686572207265636f72647320746f2062652072656d6f7665642e28436f7272757074696f6e00050468556e6578706563746564206572726f7220696e2073746174652e2852616e6b546f6f4c6f7700060494546865206d656d62657227732072616e6b20697320746f6f206c6f7720746f20766f74652e38496e76616c69645769746e6573730007049854686520696e666f726d6174696f6e2070726f766964656420697320696e636f72726563742e304e6f5065726d697373696f6e000804f8546865206f726967696e206973206e6f742073756666696369656e746c792070726976696c6567656420746f20646f20746865206f7065726174696f6e2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e39080c4070616c6c65745f7265666572656e6461147479706573385265666572656e64756d496e666f201c547261636b49640149013452756e74696d654f726967696e014503184d6f6d656e7401101043616c6c0159011c42616c616e636501181454616c6c79015101244163636f756e74496401003c5363686564756c654164647265737301790301181c4f6e676f696e6704003d08018d015265666572656e64756d5374617475733c547261636b49642c2052756e74696d654f726967696e2c204d6f6d656e742c2043616c6c2c2042616c616e63652c2054616c6c792c0a4163636f756e7449642c205363686564756c65416464726573732c3e00000020417070726f7665640c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0001002052656a65637465640c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0002002443616e63656c6c65640c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0003002054696d65644f75740c001001184d6f6d656e7400004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e00004508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e000400184b696c6c656404001001184d6f6d656e74000500003d080c4070616c6c65745f7265666572656e6461147479706573405265666572656e64756d537461747573201c547261636b49640149013452756e74696d654f726967696e014503184d6f6d656e7401101043616c6c0159011c42616c616e636501181454616c6c79015101244163636f756e74496401003c5363686564756c6541646472657373017903002c0114747261636b4901011c547261636b49640001186f726967696e4503013452756e74696d654f726967696e00012070726f706f73616c5901011043616c6c000124656e6163746d656e746d030150446973706174636854696d653c4d6f6d656e743e0001247375626d69747465641001184d6f6d656e740001487375626d697373696f6e5f6465706f7369744108016c4465706f7369743c4163636f756e7449642c2042616c616e63653e0001406465636973696f6e5f6465706f7369744508018c4f7074696f6e3c4465706f7369743c4163636f756e7449642c2042616c616e63653e3e0001206465636964696e67490801784f7074696f6e3c4465636964696e675374617475733c4d6f6d656e743e3e00011474616c6c795101011454616c6c79000120696e5f717565756535010110626f6f6c000114616c61726d510801844f7074696f6e3c284d6f6d656e742c205363686564756c6541646472657373293e000041080c4070616c6c65745f7265666572656e64611474797065731c4465706f73697408244163636f756e74496401001c42616c616e636501180008010c77686f0001244163636f756e744964000118616d6f756e7418011c42616c616e63650000450804184f7074696f6e0404540141080108104e6f6e6500000010536f6d65040041080000010000490804184f7074696f6e040454014d080108104e6f6e6500000010536f6d6504004d0800000100004d080c4070616c6c65745f7265666572656e6461147479706573384465636964696e67537461747573042c426c6f636b4e756d62657201100008011473696e636510012c426c6f636b4e756d626572000128636f6e6669726d696e672503014c4f7074696f6e3c426c6f636b4e756d6265723e0000510804184f7074696f6e0404540155080108104e6f6e6500000010536f6d650400550800000100005508000004081079030059080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454017903045300000400250601185665633c543e00005d08000002610800610800000408490165080065080c4070616c6c65745f7265666572656e646114747970657324547261636b496e666f081c42616c616e63650118184d6f6d656e740110002401106e616d65010601302627737461746963207374720001306d61785f6465636964696e6710010c7533320001406465636973696f6e5f6465706f73697418011c42616c616e6365000138707265706172655f706572696f641001184d6f6d656e7400013c6465636973696f6e5f706572696f641001184d6f6d656e74000138636f6e6669726d5f706572696f641001184d6f6d656e740001506d696e5f656e6163746d656e745f706572696f641001184d6f6d656e740001306d696e5f617070726f76616c69080114437572766500012c6d696e5f737570706f7274690801144375727665000069080c4070616c6c65745f7265666572656e646114747970657314437572766500010c404c696e65617244656372656173696e670c01186c656e6774681d05011c50657262696c6c000114666c6f6f721d05011c50657262696c6c0001106365696c1d05011c50657262696c6c000000445374657070656444656372656173696e67100114626567696e1d05011c50657262696c6c00010c656e641d05011c50657262696c6c000110737465701d05011c50657262696c6c000118706572696f641d05011c50657262696c6c000100285265636970726f63616c0c0118666163746f726d0801204669786564493634000120785f6f66667365746d0801204669786564493634000120795f6f66667365746d0801204669786564493634000200006d080c3473705f61726974686d657469632c66697865645f706f696e74204669786564493634000004007108010c693634000071080000050c0075080c4070616c6c65745f7265666572656e64611870616c6c6574144572726f72080454000449000134284e6f744f6e676f696e67000004685265666572656e64756d206973206e6f74206f6e676f696e672e284861734465706f736974000104b85265666572656e64756d2773206465636973696f6e206465706f73697420697320616c726561647920706169642e20426164547261636b0002049c54686520747261636b206964656e74696669657220676976656e2077617320696e76616c69642e1046756c6c000304310154686572652061726520616c726561647920612066756c6c20636f6d706c656d656e74206f66207265666572656e646120696e2070726f677265737320666f72207468697320747261636b2e285175657565456d70747900040480546865207175657565206f662074686520747261636b20697320656d7074792e344261645265666572656e64756d000504e4546865207265666572656e64756d20696e6465782070726f766964656420697320696e76616c696420696e207468697320636f6e746578742e2c4e6f7468696e67546f446f000604ac546865726520776173206e6f7468696e6720746f20646f20696e2074686520616476616e63656d656e742e1c4e6f547261636b000704a04e6f20747261636b2065786973747320666f72207468652070726f706f73616c206f726967696e2e28556e66696e69736865640008040101416e79206465706f7369742063616e6e6f7420626520726566756e64656420756e74696c20616674657220746865206465636973696f6e206973206f7665722e304e6f5065726d697373696f6e000904a8546865206465706f73697420726566756e646572206973206e6f7420746865206465706f7369746f722e244e6f4465706f736974000a04cc546865206465706f7369742063616e6e6f7420626520726566756e6465642073696e6365206e6f6e6520776173206d6164652e24426164537461747573000b04d0546865207265666572656e64756d2073746174757320697320696e76616c696420666f722074686973206f7065726174696f6e2e40507265696d6167654e6f744578697374000c047054686520707265696d61676520646f6573206e6f742065786973742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e79080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454017d08045300000400850801185665633c543e00007d0804184f7074696f6e0404540181080108104e6f6e6500000010536f6d650400810800000100008108084070616c6c65745f7363686564756c6572245363686564756c656414104e616d6501041043616c6c0159012c426c6f636b4e756d62657201103450616c6c6574734f726967696e014503244163636f756e7449640100001401206d617962655f69648801304f7074696f6e3c4e616d653e0001207072696f726974790801487363686564756c653a3a5072696f7269747900011063616c6c5901011043616c6c0001386d617962655f706572696f646963750301944f7074696f6e3c7363686564756c653a3a506572696f643c426c6f636b4e756d6265723e3e0001186f726967696e4503013450616c6c6574734f726967696e000085080000027d080089080c4070616c6c65745f7363686564756c65721870616c6c6574144572726f72040454000114404661696c6564546f5363686564756c65000004644661696c656420746f207363686564756c6520612063616c6c204e6f74466f756e640001047c43616e6e6f742066696e6420746865207363686564756c65642063616c6c2e5c546172676574426c6f636b4e756d626572496e50617374000204a4476976656e2074617267657420626c6f636b206e756d62657220697320696e2074686520706173742e4852657363686564756c654e6f4368616e6765000304f052657363686564756c65206661696c6564206265636175736520697420646f6573206e6f74206368616e6765207363686564756c65642074696d652e144e616d6564000404d0417474656d707420746f207573652061206e6f6e2d6e616d65642066756e6374696f6e206f6e2061206e616d6564207461736b2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8d080000029108009108086463756d756c75735f70616c6c65745f78636d705f717565756554496e626f756e644368616e6e656c44657461696c7300000c011873656e646572ad010118506172614964000114737461746595080130496e626f756e6453746174650001406d6573736167655f6d65746164617461990801a85665633c2852656c6179426c6f636b4e756d6265722c2058636d704d657373616765466f726d6174293e00009508086463756d756c75735f70616c6c65745f78636d705f717565756530496e626f756e645374617465000108084f6b0000002453757370656e6465640001000099080000029d08009d080000040810a10800a1080c74706f6c6b61646f745f70617261636861696e5f7072696d697469766573287072696d6974697665734458636d704d657373616765466f726d617400010c60436f6e636174656e6174656456657273696f6e656458636d0000005c436f6e636174656e61746564456e636f646564426c6f620001001c5369676e616c7300020000a50800000408ad011000a908000002ad0800ad08086463756d756c75735f70616c6c65745f78636d705f7175657565584f7574626f756e644368616e6e656c44657461696c730000140124726563697069656e74ad0101185061726149640001147374617465b10801344f7574626f756e6453746174650001347369676e616c735f657869737435010110626f6f6c00012c66697273745f696e6465784901010c7531360001286c6173745f696e6465784901010c7531360000b108086463756d756c75735f70616c6c65745f78636d705f7175657565344f7574626f756e645374617465000108084f6b0000002453757370656e64656400010000b50800000408ad01490100b908086463756d756c75735f70616c6c65745f78636d705f71756575653c5175657565436f6e66696744617461000018014473757370656e645f7468726573686f6c6410010c75333200013864726f705f7468726573686f6c6410010c753332000140726573756d655f7468726573686f6c6410010c7533320001407468726573686f6c645f7765696768742401185765696768740001547765696768745f72657374726963745f646563617924011857656967687400016878636d705f6d61785f696e646976696475616c5f7765696768742401185765696768740000bd080000040cad01103400c1080c6463756d756c75735f70616c6c65745f78636d705f71756575651870616c6c6574144572726f72040454000114304661696c6564546f53656e640000046c4661696c656420746f2073656e642058434d206d6573736167652e3042616458636d4f726967696e0001043c4261642058434d206f726967696e2e1842616458636d000204344261642058434d20646174612e484261644f766572776569676874496e64657800030454426164206f76657277656967687420696e6465782e3c5765696768744f7665724c696d6974000404f850726f76696465642077656967687420697320706f737369626c79206e6f7420656e6f75676820746f206578656375746520746865206d6573736167652e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ec5080c2870616c6c65745f78636d1870616c6c65742c5175657279537461747573042c426c6f636b4e756d6265720110010c1c50656e64696e67100124726573706f6e6465720102015856657273696f6e65644d756c74694c6f636174696f6e00014c6d617962655f6d617463685f71756572696572c90801784f7074696f6e3c56657273696f6e65644d756c74694c6f636174696f6e3e0001306d617962655f6e6f74696679cd0801404f7074696f6e3c2875382c207538293e00011c74696d656f757410012c426c6f636b4e756d6265720000003c56657273696f6e4e6f7469666965720801186f726967696e0102015856657273696f6e65644d756c74694c6f636174696f6e00012469735f61637469766535010110626f6f6c000100145265616479080120726573706f6e7365d508014456657273696f6e6564526573706f6e7365000108617410012c426c6f636b4e756d62657200020000c90804184f7074696f6e0404540101020108104e6f6e6500000010536f6d65040001020000010000cd0804184f7074696f6e04045401d1080108104e6f6e6500000010536f6d650400d1080000010000d10800000408080800d508082c73746167696e675f78636d4456657273696f6e6564526573706f6e736500010808563204009503013076323a3a526573706f6e73650002000856330400c903013076333a3a526573706f6e736500030000d9080000040810010200dd080000040c2c241000e1080c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401e508045300000400e90801185665633c543e0000e5080000040801021000e908000002e50800ed080c2870616c6c65745f78636d1870616c6c65745456657273696f6e4d6967726174696f6e53746167650001105c4d696772617465537570706f7274656456657273696f6e0000005c4d69677261746556657273696f6e4e6f74696669657273000100504e6f7469667943757272656e74546172676574730400f108013c4f7074696f6e3c5665633c75383e3e000200684d696772617465416e644e6f746966794f6c645461726765747300030000f10804184f7074696f6e04045401340108104e6f6e6500000010536f6d650400340000010000f5080000040c1000f90800f908082c73746167696e675f78636d4056657273696f6e6564417373657449640001040856330400d0012c76333a3a4173736574496400030000fd080c2870616c6c65745f78636d1870616c6c65746852656d6f74654c6f636b656446756e6769626c655265636f72640848436f6e73756d65724964656e74696669657201ac304d6178436f6e73756d6572730000100118616d6f756e74180110753132380001146f776e65720102015856657273696f6e65644d756c74694c6f636174696f6e0001186c6f636b65720102015856657273696f6e65644d756c74694c6f636174696f6e000124636f6e73756d657273010901d0426f756e6465645665633c28436f6e73756d65724964656e7469666965722c2075313238292c204d6178436f6e73756d6572733e000001090c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454010509045300000400090901185665633c543e0000050900000408ac180009090000020509000d090c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e646564566563080454011109045300000400150901185665633c543e000011090000040818010200150900000211090019090c2870616c6c65745f78636d1870616c6c6574144572726f720404540001502c556e726561636861626c650000085d0154686520646573697265642064657374696e6174696f6e2077617320756e726561636861626c652c2067656e6572616c6c7920626563617573652074686572652069732061206e6f20776179206f6620726f7574696e6718746f2069742e2c53656e644661696c757265000108410154686572652077617320736f6d65206f746865722069737375652028692e652e206e6f7420746f20646f207769746820726f7574696e672920696e2073656e64696e6720746865206d6573736167652ec8506572686170732061206c61636b206f6620737061636520666f7220627566666572696e6720746865206d6573736167652e2046696c74657265640002049c546865206d65737361676520657865637574696f6e206661696c73207468652066696c7465722e48556e776569676861626c654d657373616765000304b4546865206d65737361676527732077656967687420636f756c64206e6f742062652064657465726d696e65642e6044657374696e6174696f6e4e6f74496e7665727469626c65000404f05468652064657374696e6174696f6e20604d756c74694c6f636174696f6e602070726f76696465642063616e6e6f7420626520696e7665727465642e14456d707479000504805468652061737365747320746f2062652073656e742061726520656d7074792e3843616e6e6f745265616e63686f720006043501436f756c64206e6f742072652d616e63686f72207468652061737365747320746f206465636c61726520746865206665657320666f72207468652064657374696e6174696f6e20636861696e2e34546f6f4d616e79417373657473000704c4546f6f206d616e79206173736574732068617665206265656e20617474656d7074656420666f72207472616e736665722e34496e76616c69644f726967696e000804784f726967696e20697320696e76616c696420666f722073656e64696e672e2842616456657273696f6e00090421015468652076657273696f6e206f6620746865206056657273696f6e6564602076616c75652075736564206973206e6f742061626c6520746f20626520696e7465727072657465642e2c4261644c6f636174696f6e000a08410154686520676976656e206c6f636174696f6e20636f756c64206e6f7420626520757365642028652e672e20626563617573652069742063616e6e6f742062652065787072657373656420696e2074686560646573697265642076657273696f6e206f662058434d292e384e6f537562736372697074696f6e000b04bc546865207265666572656e63656420737562736372697074696f6e20636f756c64206e6f7420626520666f756e642e44416c726561647953756273637269626564000c041101546865206c6f636174696f6e20697320696e76616c69642073696e636520697420616c726561647920686173206120737562736372697074696f6e2066726f6d2075732e30496e76616c69644173736574000d0480496e76616c696420617373657420666f7220746865206f7065726174696f6e2e284c6f7742616c616e6365000e044101546865206f776e657220646f6573206e6f74206f776e2028616c6c29206f662074686520617373657420746861742074686579207769736820746f20646f20746865206f7065726174696f6e206f6e2e30546f6f4d616e794c6f636b73000f04c0546865206173736574206f776e65722068617320746f6f206d616e79206c6f636b73206f6e207468652061737365742e4c4163636f756e744e6f74536f7665726569676e001004310154686520676976656e206163636f756e74206973206e6f7420616e206964656e7469666961626c6520736f7665726569676e206163636f756e7420666f7220616e79206c6f636174696f6e2e28466565734e6f744d65740011042901546865206f7065726174696f6e207265717569726564206665657320746f20626520706169642077686963682074686520696e69746961746f7220636f756c64206e6f74206d6565742e304c6f636b4e6f74466f756e64001204f4412072656d6f7465206c6f636b20776974682074686520636f72726573706f6e64696e67206461746120636f756c64206e6f7420626520666f756e642e14496e557365001304490154686520756e6c6f636b206f7065726174696f6e2063616e6e6f742073756363656564206265636175736520746865726520617265207374696c6c20636f6e73756d657273206f6620746865206c6f636b2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e1d090c4863756d756c75735f70616c6c65745f78636d1870616c6c6574144572726f72040454000100048054686520604572726f726020656e756d206f6620746869732070616c6c65742e2109086063756d756c75735f70616c6c65745f646d705f717565756528436f6e6669674461746100000401386d61785f696e646976696475616c24011857656967687400002509086063756d756c75735f70616c6c65745f646d705f71756575653450616765496e6465784461746100000c0128626567696e5f7573656410012c50616765436f756e746572000120656e645f7573656410012c50616765436f756e7465720001406f7665727765696768745f636f756e742c013c4f766572776569676874496e646578000029090000022d09002d090000040810340031090c6063756d756c75735f70616c6c65745f646d705f71756575651870616c6c6574144572726f720404540001081c556e6b6e6f776e0000048c546865206d65737361676520696e64657820676976656e20697320756e6b6e6f776e2e244f7665724c696d6974000104310154686520616d6f756e74206f662077656967687420676976656e20697320706f737369626c79206e6f7420656e6f75676820666f7220657865637574696e6720746865206d6573736167652e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e350900000408890400003909000004088904d904003d090000040c8904d904000041090c3470616c6c65745f756e697175651870616c6c6574144572726f7204045400010c8c436f6c6c656374696f6e446563696d616c506f696e744c696d697445786365656465640000045101446563696d616c5f706f696e747320706172616d65746572206d757374206265206c6f776572207468616e205b6075705f646174615f737472756374733a3a4d41585f444543494d414c5f504f494e5453605d2e34456d707479417267756d656e74000104c84c656e677468206f66206974656d732070726f70657274696573206d7573742062652067726561746572207468616e20302ea85265706172746974696f6e43616c6c65644f6e4e6f6e526566756e6769626c65436f6c6c656374696f6e000204dc5265706572746974696f6e206973206f6e6c7920737570706f7274656420627920726566756e6769626c6520636f6c6c656374696f6e2e04a84572726f727320666f722074686520636f6d6d6f6e20556e69717565207472616e73616374696f6e732e45090c5070616c6c65745f636f6e66696775726174696f6e1870616c6c6574144572726f7204045400010464496e636f6e73697374656e74436f6e66696775726174696f6e000000048054686520604572726f726020656e756d206f6620746869732070616c6c65742e4909083c75705f646174615f7374727563747328436f6c6c656374696f6e04244163636f756e7449640100002401146f776e65720001244163636f756e7449640001106d6f646545040138436f6c6c656374696f6e4d6f64650001106e616d6535040138436f6c6c656374696f6e4e616d6500012c6465736372697074696f6e3d040154436f6c6c656374696f6e4465736372697074696f6e000130746f6b656e5f70726566697841040154436f6c6c656374696f6e546f6b656e50726566697800012c73706f6e736f72736869704d09016c53706f6e736f727368697053746174653c4163636f756e7449643e0001186c696d6974735d040140436f6c6c656374696f6e4c696d69747300012c7065726d697373696f6e7371040154436f6c6c656374696f6e5065726d697373696f6e73000114666c6167736902013c436f6c6c656374696f6e466c61677300004d09083c75705f646174615f737472756374734053706f6e736f7273686970537461746504244163636f756e7449640100010c2044697361626c65640000002c556e636f6e6669726d656404000001244163636f756e74496400010024436f6e6669726d656404000001244163636f756e744964000200005109083c75705f646174615f737472756374732850726f7065727469657300000c010c6d61705509017050726f706572746965734d61703c50726f706572747956616c75653e000138636f6e73756d65645f737061636510010c7533320001245f726573657276656410010c75333200005509083c75705f646174615f737472756374733450726f706572746965734d6170041456616c756501b1040004005909011901426f756e64656442547265654d61703c50726f70657274794b65792c2056616c75652c20436f6e73745533323c4d41585f50524f504552544945535f5045525f4954454d3e3e000059090c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b019d04045601b1040453000004005d09013842547265654d61703c4b2c20563e00005d09042042547265654d617008044b019d04045601b104000400610900000061090000026509006509000004089d04b104006909083c75705f646174615f737472756374733450726f706572746965734d6170041456616c756501a1040004006d09011901426f756e64656442547265654d61703c50726f70657274794b65792c2056616c75652c20436f6e73745533323c4d41585f50524f504552544945535f5045525f4954454d3e3e00006d090c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b019d04045601a1040453000004007109013842547265654d61703c4b2c20563e00007109042042547265654d617008044b019d04045601a104000400750900000075090000027909007909000004089d04a104007d090000040889044d040081090000041485098904d90489098d09008509083c75705f646174615f737472756374733c436f6c6c656374696f6e537461747300000c011c6372656174656410010c75333200012464657374726f79656410010c753332000114616c69766510010c75333200008909083c75705f646174615f7374727563747328546f6b656e4368696c640000080114746f6b656ed904011c546f6b656e4964000128636f6c6c656374696f6e89040130436f6c6c656374696f6e496400008d09082c5068616e746f6d547970653c75705f646174615f73747275637473040454019109000400c10900000091090000040c95099909a109009509083c75705f646174615f7374727563747324546f6b656e44617461043843726f73734163636f756e744964014d04000c012870726f70657274696573b50401345665633c50726f70657274793e0001146f776e6572bd0401584f7074696f6e3c43726f73734163636f756e7449643e0001187069656365731801107531323800009909083c75705f646174615f7374727563747334527063436f6c6c656374696f6e04244163636f756e7449640100003001146f776e65720001244163636f756e7449640001106d6f646545040138436f6c6c656374696f6e4d6f64650001106e616d65390401205665633c7531363e00012c6465736372697074696f6e390401205665633c7531363e000130746f6b656e5f70726566697834011c5665633c75383e00012c73706f6e736f72736869704d09016c53706f6e736f727368697053746174653c4163636f756e7449643e0001186c696d6974735d040140436f6c6c656374696f6e4c696d69747300012c7065726d697373696f6e7371040154436f6c6c656374696f6e5065726d697373696f6e73000168746f6b656e5f70726f70657274795f7065726d697373696f6e73a50401685665633c50726f70657274794b65795065726d697373696f6e3e00012870726f70657274696573b50401345665633c50726f70657274793e000124726561645f6f6e6c7935010110626f6f6c000114666c6167739d090148527063436f6c6c656374696f6e466c61677300009d09083c75705f646174615f7374727563747348527063436f6c6c656374696f6e466c616773000008011c666f726569676e35010110626f6f6c0001386572633732316d6574616461746135010110626f6f6c0000a109084c75705f706f765f657374696d6174655f7270631c506f76496e666f000014012870726f6f665f73697a652c010c753634000148636f6d706163745f70726f6f665f73697a652c010c753634000154636f6d707265737365645f70726f6f665f73697a652c010c75363400011c726573756c7473a50901645665633c4170706c7945787472696e736963526573756c743e0001286b65795f76616c756573b90901445665633c547269654b657956616c75653e0000a509000002a90900a9090418526573756c7408045401a8044501ad090108084f6b0400a8000000000c4572720400ad090000010000ad090c2873705f72756e74696d65507472616e73616374696f6e5f76616c6964697479605472616e73616374696f6e56616c69646974794572726f720001081c496e76616c69640400b1090148496e76616c69645472616e73616374696f6e0000001c556e6b6e6f776e0400b5090148556e6b6e6f776e5472616e73616374696f6e00010000b1090c2873705f72756e74696d65507472616e73616374696f6e5f76616c696469747948496e76616c69645472616e73616374696f6e00012c1043616c6c0000001c5061796d656e7400010018467574757265000200145374616c650003002042616450726f6f6600040044416e6369656e744269727468426c6f636b0005004445786861757374735265736f757263657300060018437573746f6d04000801087538000700304261644d616e6461746f72790008004c4d616e6461746f727956616c69646174696f6e000900244261645369676e6572000a0000b5090c2873705f72756e74696d65507472616e73616374696f6e5f76616c696469747948556e6b6e6f776e5472616e73616374696f6e00010c3043616e6e6f744c6f6f6b75700000004c4e6f556e7369676e656456616c696461746f7200010018437573746f6d0400080108753800020000b909000002bd0900bd09084c75705f706f765f657374696d6174655f72706330547269654b657956616c7565000008010c6b657934011c5665633c75383e00011476616c756534011c5665633c75383e0000c10900000300000000910900c5090c3470616c6c65745f636f6d6d6f6e1870616c6c6574144572726f7204045400019848436f6c6c656374696f6e4e6f74466f756e640000047c5468697320636f6c6c656374696f6e20646f6573206e6f742065786973742e404d7573744265546f6b656e4f776e6572000104b853656e64657220706172616d6574657220616e64206974656d206f776e6572206d75737420626520657175616c2e304e6f5065726d697373696f6e0002047c4e6f207065726d697373696f6e20746f20706572666f726d20616374696f6e7443616e7444657374726f794e6f74456d707479436f6c6c656374696f6e000304b044657374726f79696e67206f6e6c7920656d70747920636f6c6c656374696f6e7320697320616c6c6f7765645c5075626c69634d696e74696e674e6f74416c6c6f7765640004047c436f6c6c656374696f6e206973206e6f7420696e206d696e74206d6f64652e54416464726573734e6f74496e416c6c6f776c6973740005047441646472657373206973206e6f7420696e20616c6c6f77206c6973742e6c436f6c6c656374696f6e4e616d654c696d69744578636565646564000604bc436f6c6c656374696f6e206e616d652063616e206e6f74206265206c6f6e676572207468616e20363320636861722e88436f6c6c656374696f6e4465736372697074696f6e4c696d69744578636565646564000704dc436f6c6c656374696f6e206465736372697074696f6e2063616e206e6f74206265206c6f6e676572207468616e2032353520636861722e88436f6c6c656374696f6e546f6b656e5072656669784c696d69744578636565646564000804b0546f6b656e207072656669782063616e206e6f74206265206c6f6e676572207468616e20313520636861722e74546f74616c436f6c6c656374696f6e734c696d6974457863656564656400090484546f74616c20636f6c6c656374696f6e7320626f756e642065786365656465642e70436f6c6c656374696f6e41646d696e436f756e744578636565646564000a04604578636565646564206d61782061646d696e20636f756e7474436f6c6c656374696f6e4c696d6974426f756e64734578636565646564000b04bc436f6c6c656374696f6e206c696d697420626f756e64732070657220636f6c6c656374696f6e206578636565646564784f776e65725065726d697373696f6e7343616e7442655265766572746564000c040d01547269656420746f20656e61626c65207065726d697373696f6e7320776869636820617265206f6e6c79207065726d697474656420746f2062652064697361626c6564485472616e736665724e6f74416c6c6f776564000d04cc436f6c6c656374696f6e2073657474696e6773206e6f7420616c6c6f77696e67206974656d73207472616e7366657272696e67644163636f756e74546f6b656e4c696d69744578636565646564000e04ac4163636f756e7420746f6b656e206c696d69742065786365656465642070657220636f6c6c656374696f6e70436f6c6c656374696f6e546f6b656e4c696d69744578636565646564000f047c436f6c6c656374696f6e20746f6b656e206c696d6974206578636565646564484d65746164617461466c616746726f7a656e001004504d6574616461746120666c61672066726f7a656e34546f6b656e4e6f74466f756e640011044c4974656d20646f6573206e6f7420657869737440546f6b656e56616c7565546f6f4c6f77001204684974656d2069732062616c616e6365206e6f7420656e6f7567684c417070726f76656456616c7565546f6f4c6f77001304a45265717565737465642076616c7565206973206d6f7265207468616e2074686520617070726f7665646043616e74417070726f76654d6f72655468616e4f776e656400140480547269656420746f20617070726f7665206d6f7265207468616e206f776e6564544164647265737349734e6f744574684d6972726f72001504bc4f6e6c79207370656e64696e672066726f6d20657468206d6972726f7220636f756c6420626520617070726f766564344164647265737349735a65726f001604b843616e2774207472616e7366657220746f6b656e7320746f20657468657265756d207a65726f206164647265737350556e737570706f727465644f7065726174696f6e00170478546865206f7065726174696f6e206973206e6f7420737570706f727465644c4e6f7453756666696369656e74466f756e64730018049c496e73756666696369656e742066756e647320746f20706572666f726d20616e20616374696f6e585573657249734e6f74416c6c6f776564546f4e657374001904985573657220646f6573206e6f74207361746973667920746865206e657374696e672072756c6588536f75726365436f6c6c656374696f6e49734e6f74416c6c6f776564546f4e657374001a0411014f6e6c7920746f6b656e732066726f6d20737065636966696320636f6c6c656374696f6e73206d6179206e65737420746f6b656e7320756e6465722074686973206f6e656c436f6c6c656374696f6e4669656c6453697a654578636565646564001b04e4547269656420746f2073746f7265206d6f72652064617461207468616e20616c6c6f77656420696e20636f6c6c656374696f6e206669656c64484e6f5370616365466f7250726f7065727479001c04b8547269656420746f2073746f7265206d6f72652070726f70657274792064617461207468616e20616c6c6f7765645050726f70657274794c696d697452656163686564001d04b8547269656420746f2073746f7265206d6f72652070726f7065727479206b657973207468616e20616c6c6f7765645050726f70657274794b65794973546f6f4c6f6e67001e046050726f7065727479206b657920697320746f6f206c6f6e6774496e76616c6964436861726163746572496e50726f70657274794b6579001f0415014f6e6c79204153434949206c6574746572732c206469676974732c20616e642073796d626f6c7320605f602c20602d602c20616e6420602e602061726520616c6c6f77656440456d70747950726f70657274794b657900200484456d7074792070726f7065727479206b6579732061726520666f7262696464656e50436f6c6c656374696f6e497345787465726e616c002104ec547269656420746f2061636365737320616e2065787465726e616c20636f6c6c656374696f6e207769746820616e20696e7465726e616c2041504950436f6c6c656374696f6e4973496e7465726e616c002204ec547269656420746f2061636365737320616e20696e7465726e616c20636f6c6c656374696f6e207769746820616e2065787465726e616c2041504958436f6e6669726d53706f6e736f72736869704661696c0023040d01546869732061646472657373206973206e6f74207365742061732073706f6e736f722c2075736520736574436f6c6c656374696f6e53706f6e736f722066697273742e605573657249734e6f74436f6c6c656374696f6e41646d696e002404845468652075736572206973206e6f7420616e2061646d696e6973747261746f722e5446756e6769626c654974656d73486176654e6f4964002504710146756e6769626c6520746f6b656e7320686f6c64206e6f2049442c20616e64207468652064656661756c742076616c7565206f6620546f6b656e496420666f7220612066756e6769626c6520636f6c6c656374696f6e20697320302e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ec9090000040c89044d044d0400cd090c3c70616c6c65745f66756e6769626c651870616c6c6574144572726f72040454000118c04e6f7446756e6769626c654461746155736564546f4d696e7446756e6769626c65436f6c6c656374696f6e546f6b656e000004ec4e6f742046756e6769626c65206974656d2064617461207573656420746f206d696e7420696e2046756e6769626c6520636f6c6c656374696f6e2e6446756e6769626c654974656d73446f6e74486176654461746100010490547269656420746f20736574206461746120666f722066756e6769626c65206974656d2e6046756e6769626c65446973616c6c6f77734e657374696e67000204a046756e6769626c6520746f6b656e20646f6573206e6f7420737570706f7274206e657374696e672e6c53657474696e6750726f706572746965734e6f74416c6c6f7765640003049c53657474696e67206974656d2070726f70657274696573206973206e6f7420616c6c6f7765642e8053657474696e67416c6c6f77616e6365466f72416c6c4e6f74416c6c6f776564000404a453657474696e6720616c6c6f77616e636520666f7220616c6c206973206e6f7420616c6c6f7765642e7046756e6769626c65546f6b656e73417265416c7761797356616c696400050445014f6e6c7920612066756e6769626c6520636f6c6c656374696f6e20636f756c6420626520706f737369626c792062726f6b656e3b20616e792066756e6769626c6520746f6b656e2069732076616c69642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ed109083c75705f646174615f737472756374732850726f7065727469657300000c010c6d61705509017050726f706572746965734d61703c50726f706572747956616c75653e000138636f6e73756d65645f737061636510010c7533320001245f726573657276656410010c7533320000d5090000040c89044d04d90400d9090000040c8904d9044d0400dd09000004108904d9044d044d0400e1090c4470616c6c65745f726566756e6769626c651870616c6c6574144572726f72040454000114c84e6f74526566756e6769626c654461746155736564546f4d696e7446756e6769626c65436f6c6c656374696f6e546f6b656e000004fc4e6f7420526566756e6769626c65206974656d2064617461207573656420746f206d696e7420696e20526566756e6769626c6520636f6c6c656374696f6e2e5457726f6e67526566756e6769626c655069656365730001047c4d6178696d756d20726566756e676962696c6974792065786365656465642e885265706172746974696f6e5768696c654e6f744f776e696e67416c6c5069656365730002042901526566756e6769626c6520746f6b656e2063616e2774206265207265706172746974696f6e656420627920757365722077686f2069736e2774206f776e7320616c6c207069656365732e68526566756e6769626c65446973616c6c6f77734e657374696e67000304a4526566756e6769626c6520746f6b656e2063616e2774206e657374206f7468657220746f6b656e732e6c53657474696e6750726f706572746965734e6f74416c6c6f7765640004049c53657474696e67206974656d2070726f70657274696573206973206e6f7420616c6c6f7765642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742ee509084870616c6c65745f6e6f6e66756e6769626c65204974656d44617461043843726f73734163636f756e744964014d04000401146f776e65724d04013843726f73734163636f756e7449640000e909000004108904d904ed099d0400ed09083c75705f646174615f737472756374733450726f706572747953636f7065000108104e6f6e6500000010526d726b00010000f1090c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401080453000004003401185665633c543e0000f5090000040c8904d904390900f9090c4870616c6c65745f6e6f6e66756e6769626c651870616c6c6574144572726f7204045400010ccc4e6f744e6f6e66756e6769626c654461746155736564546f4d696e7446756e6769626c65436f6c6c656374696f6e546f6b656e00000405014e6f74204e6f6e66756e6769626c65206974656d2064617461207573656420746f206d696e7420696e204e6f6e66756e6769626c6520636f6c6c656374696f6e2e704e6f6e66756e6769626c654974656d73486176654e6f416d6f756e74000104605573656420616d6f756e74203e20312077697468204e46545c43616e744275726e4e6674576974684368696c6472656e00020480556e61626c6520746f206275726e204e46542077697468206368696c6472656e048054686520604572726f726020656e756d206f6620746869732070616c6c65742efd090c4070616c6c65745f7374727563747572651870616c6c6574144572726f72040454000114444f75726f626f726f73446574656374656400000421015768696c65206e657374696e672c20656e636f756e746572656420616e20616c726561647920636865636b6564206163636f756e742c20646574656374696e672061206c6f6f702e2844657074684c696d697400010445015768696c65206e657374696e672c207265616368656420746865206465707468206c696d6974206f66206e657374696e672c20657863656564696e67207468652070726f7669646564206275646765742e30427265616474684c696d69740002044d015768696c65206e657374696e672c2072656163686564207468652062726561647468206c696d6974206f66206e657374696e672c20657863656564696e67207468652070726f7669646564206275646765742e34546f6b656e4e6f74466f756e64000304d4436f756c646e27742066696e642074686520746f6b656e206f776e6572207468617420697320697473656c66206120746f6b656e2e7043616e744e657374546f6b656e556e646572436f6c6c656374696f6e0004043d01547269656420746f206e65737420746f6b656e20756e64657220636f6c6c656374696f6e20636f6e747261637420616464726573732c20696e7374656164206f6620746f6b656e2061646472657373048054686520604572726f726020656e756d206f6620746869732070616c6c65742e010a00000408001000050a00000408181000090a0c4c626f756e6465645f636f6c6c656374696f6e732c626f756e6465645f76656328426f756e64656456656308045401a5070453000004000d0a01185665633c543e00000d0a000002a50700110a0c5070616c6c65745f6170705f70726f6d6f74696f6e1870616c6c6574144572726f7204045400011c2c41646d696e4e6f74536574000004b84572726f722064756520746f20616374696f6e20726571756972696e672061646d696e20746f206265207365742e304e6f5065726d697373696f6e0001048c4e6f207065726d697373696f6e20746f20706572666f726d20616e20616374696f6e2e484e6f7453756666696369656e7446756e6473000204a0496e73756666696369656e742066756e647320746f20706572666f726d20616e20616374696f6e2e5c50656e64696e67466f72426c6f636b4f766572666c6f7700030499014f6363757273207768656e20612070656e64696e6720756e7374616b652063616e6e6f7420626520616464656420696e207468697320626c6f636b2e2050454e44494e475f4c494d49545f5045525f424c4f434b60206c696d6974732065786365656465642e3453706f6e736f724e6f74536574000404cd01546865206572726f722069732064756520746f20746865206661637420746861742074686520636f6c6c656374696f6e2f636f6e7472616374206d75737420616c72656164792062652073706f6e736f72656420696e206f7264657220746f20706572666f726d2074686520616374696f6e2e64496e73756666696369656e745374616b656442616c616e6365000504b44572726f72732063617573656420627920696e73756666696369656e74207374616b65642062616c616e63652e48496e636f6e73697374656e6379537461746500060419014572726f72732063617573656420627920696e636f7272656374207374617465206f662061207374616b657220696e20636f6e74657874206f66207468652070616c6c65742e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e150a0c5470616c6c65745f666f726569676e5f617373657473186d6f64756c65144572726f720404540001102c4261644c6f636174696f6e000008410154686520676976656e206c6f636174696f6e20636f756c64206e6f7420626520757365642028652e672e20626563617573652069742063616e6e6f742062652065787072657373656420696e2074686560646573697265642076657273696f6e206f662058434d292e504d756c74694c6f636174696f6e45786973746564000104544d756c74694c6f636174696f6e206578697374656440417373657449644e6f744578697374730002044841737365744964206e6f7420657869737473384173736574496445786973746564000304384173736574496420657869737473048054686520604572726f726020656e756d206f6620746869732070616c6c65742e190a082870616c6c65745f65766d30436f64654d65746164617461000008011073697a652c010c753634000110686173683001104832353600001d0a0000040865033000210a0c2870616c6c65745f65766d1870616c6c6574144572726f7204045400012c2842616c616e63654c6f77000004904e6f7420656e6f7567682062616c616e636520746f20706572666f726d20616374696f6e2c4665654f766572666c6f770001048043616c63756c6174696e6720746f74616c20666565206f766572666c6f7765643c5061796d656e744f766572666c6f770002049043616c63756c6174696e6720746f74616c207061796d656e74206f766572666c6f7765643857697468647261774661696c65640003044c576974686472617720666565206661696c6564384761735072696365546f6f4c6f770004045447617320707269636520697320746f6f206c6f772e30496e76616c69644e6f6e6365000504404e6f6e636520697320696e76616c6964384761734c696d6974546f6f4c6f7700060454476173206c696d697420697320746f6f206c6f772e3c4761734c696d6974546f6f4869676800070458476173206c696d697420697320746f6f20686967682e24556e646566696e656400080440556e646566696e6564206572726f722e285265656e7472616e63790009043845564d207265656e7472616e6379685472616e73616374696f6e4d757374436f6d6546726f6d454f41000a04244549502d333630372c048054686520604572726f726020656e756d206f6620746869732070616c6c65742e250a000002290a00290a0000040c65052d0a3d0a002d0a081866705f727063445472616e73616374696f6e53746174757300001c01407472616e73616374696f6e5f68617368300110483235360001447472616e73616374696f6e5f696e64657810010c75333200011066726f6d6503011048313630000108746f310a01304f7074696f6e3c483136303e000140636f6e74726163745f61646472657373310a01304f7074696f6e3c483136303e0001106c6f67739d0501205665633c4c6f673e0001286c6f67735f626c6f6f6d350a0114426c6f6f6d0000310a04184f7074696f6e0404540165030108104e6f6e6500000010536f6d65040065030000010000350a0820657468626c6f6f6d14426c6f6f6d00000400390a01405b75383b20424c4f4f4d5f53495a455d0000390a0000030001000008003d0a0c20657468657265756d1c726563656970742452656365697074563300010c184c65676163790400410a014445495036353852656365697074446174610000001c454950323933300400410a01484549503239333052656365697074446174610001001c454950313535390400410a014845495031353539526563656970744461746100020000410a0c20657468657265756d1c72656365697074444549503635385265636569707444617461000010012c7374617475735f636f64650801087538000120757365645f67617349050110553235360001286c6f67735f626c6f6f6d350a0114426c6f6f6d0001106c6f67739d0501205665633c4c6f673e0000450a0c20657468657265756d14626c6f636b14426c6f636b040454016505000c0118686561646572490a01184865616465720001307472616e73616374696f6e73510a01185665633c543e0001186f6d6d657273550a012c5665633c4865616465723e0000490a0c20657468657265756d186865616465721848656164657200003c012c706172656e745f686173683001104832353600012c6f6d6d6572735f686173683001104832353600012c62656e6566696369617279650301104831363000012873746174655f726f6f74300110483235360001447472616e73616374696f6e735f726f6f743001104832353600013472656365697074735f726f6f74300110483235360001286c6f67735f626c6f6f6d350a0114426c6f6f6d000128646966666963756c747949050110553235360001186e756d62657249050110553235360001246761735f6c696d697449050110553235360001206761735f75736564490501105532353600012474696d657374616d702c010c75363400012865787472615f6461746134011442797465730001206d69785f68617368300110483235360001146e6f6e63654d0a010c48363400004d0a0c38657468657265756d5f747970657310686173680c483634000004000101011c5b75383b20385d0000510a000002650500550a000002490a00590a0000023d0a005d0a0000022d0a00610a0c3c70616c6c65745f657468657265756d1870616c6c6574144572726f7204045400010840496e76616c69645369676e6174757265000004545369676e617475726520697320696e76616c69642e305072654c6f67457869737473000104d85072652d6c6f672069732070726573656e742c207468657265666f7265207472616e73616374206973206e6f7420616c6c6f7765642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e650a0c6870616c6c65745f65766d5f636f6465725f7375627374726174651870616c6c6574144572726f72040454000108204f75744f66476173000000244f75744f6646756e6400010014850144697370617463684572726f72206973206f70617175652c20627574207765206e65656420746f20736f6d65686f77206578747261637420636f7272656374206572726f7220696e2063617365206f66204f75744f66476173206661696c7572659101536f207765206861766520746869732070616c6c65742c20776869636820646566696e6573204f75744f66476173206572726f722c20616e64206b6e65777320697473206f776e20696420746f20636865636b2069662044697370617463684572726f725c6973207468726f776e2062656361757365206f662069740061015468657365206572726f72732073686f756c646e277420656e6420696e2065787472696e73696320726573756c74732c2061732074686579206f6e6c79207573656420696e2065766d20657865637574696f6e2070617468690a083c75705f646174615f737472756374734053706f6e736f7273686970537461746504244163636f756e744964014d04010c2044697361626c65640000002c556e636f6e6669726d656404004d0401244163636f756e74496400010024436f6e6669726d656404004d0401244163636f756e744964000200006d0a086c70616c6c65745f65766d5f636f6e74726163745f68656c706572733c53706f6e736f72696e674d6f64655400010c2044697361626c65640000002c416c6c6f776c69737465640001002047656e65726f757300020000710a0c4c626f756e6465645f636f6c6c656374696f6e7344626f756e6465645f62747265655f6d61703c426f756e64656442547265654d61700c044b01100456014905045300000400750a013842547265654d61703c4b2c20563e0000750a042042547265654d617008044b01100456014905000400790a000000790a0000027d0a007d0a0000040810490500810a000004086503650300850a0c6c70616c6c65745f65766d5f636f6e74726163745f68656c706572731870616c6c6574144572726f7204045400010c304e6f5065726d697373696f6e000004c054686973206d6574686f64206973206f6e6c792065786563757461626c6520627920636f6e7472616374206f776e6572404e6f50656e64696e6753706f6e736f72000104804e6f2070656e64696e672073706f6e736f7220666f7220636f6e74726163742e80546f6f4d616e794d6574686f64734861766553706f6e736f7265644c696d697400020419014e756d626572206f66206d6574686f647320746861742073706f6e736f726564206c696d697420697320646566696e656420666f722065786365656473206d6178696d756d2e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e890a0c5070616c6c65745f65766d5f6d6967726174696f6e1870616c6c6574144572726f7204045400010c3c4163636f756e744e6f74456d7074790000048843616e206f6e6c79206d69677261746520746f20656d70747920616464726573732e544163636f756e7449734e6f744d6967726174696e6700010409014d6967726174696f6e206f662074686973206163636f756e74206973206e6f742079657420737461727465642c206f7220616c72656164792066696e69736865642e204261644576656e74000204704661696c656420746f206465636f6465206576656e74206279746573048054686520604572726f726020656e756d206f6620746869732070616c6c65742e8d0a0c4870616c6c65745f6d61696e74656e616e63651870616c6c6574144572726f72040454000100048054686520604572726f726020656e756d206f6620746869732070616c6c65742e910a0c3870616c6c65745f7574696c6974791870616c6c6574144572726f7204045400010430546f6f4d616e7943616c6c730000045c546f6f206d616e792063616c6c7320626174636865642e048054686520604572726f726020656e756d206f6620746869732070616c6c65742e950a0c4470616c6c65745f746573745f7574696c731870616c6c6574144572726f72040454000108485465737450616c6c657444697361626c65640000003c54726967676572526f6c6c6261636b000100048054686520604572726f726020656e756d206f6620746869732070616c6c65742e990a0c4466705f73656c665f636f6e7461696e65644c756e636865636b65645f65787472696e73696348556e636865636b656445787472696e736963101c4164647265737301dd011043616c6c015d01245369676e6174757265019d0a14457874726101b50a000400e90a01250173705f72756e74696d653a3a67656e657269633a3a556e636865636b656445787472696e7369633c416464726573732c2043616c6c2c205369676e61747572652c2045787472610a3e00009d0a082873705f72756e74696d65384d756c74695369676e617475726500010c1c456432353531390400a10a0148656432353531393a3a5369676e61747572650000001c537232353531390400a90a0148737232353531393a3a5369676e61747572650001001445636473610400ad0a014065636473613a3a5369676e617475726500020000a10a0c1c73705f636f72651c65643235353139245369676e617475726500000400a50a01205b75383b2036345d0000a50a000003400000000800a90a0c1c73705f636f72651c73723235353139245369676e617475726500000400a50a01205b75383b2036345d0000ad0a0c1c73705f636f7265146563647361245369676e617475726500000400b10a01205b75383b2036355d0000b10a000003410000000800b50a00000428b90abd0ac10ac50acd0ad10ad50ad90add0ae50a00b90a10306672616d655f73797374656d28657874656e73696f6e7348636865636b5f737065635f76657273696f6e40436865636b5370656356657273696f6e04045400000000bd0a10306672616d655f73797374656d28657874656e73696f6e7340636865636b5f74785f76657273696f6e38436865636b547856657273696f6e04045400000000c10a10306672616d655f73797374656d28657874656e73696f6e7334636865636b5f67656e6573697330436865636b47656e6573697304045400000000c50a10306672616d655f73797374656d28657874656e73696f6e733c636865636b5f6d6f7274616c69747938436865636b4d6f7274616c69747904045400000400c90a010c4572610000c90a102873705f72756e74696d651c67656e657269630c6572610c4572610001010420496d6d6f7274616c0000001c4d6f7274616c31040008000001001c4d6f7274616c32040008000002001c4d6f7274616c33040008000003001c4d6f7274616c34040008000004001c4d6f7274616c35040008000005001c4d6f7274616c36040008000006001c4d6f7274616c37040008000007001c4d6f7274616c38040008000008001c4d6f7274616c3904000800000900204d6f7274616c313004000800000a00204d6f7274616c313104000800000b00204d6f7274616c313204000800000c00204d6f7274616c313304000800000d00204d6f7274616c313404000800000e00204d6f7274616c313504000800000f00204d6f7274616c313604000800001000204d6f7274616c313704000800001100204d6f7274616c313804000800001200204d6f7274616c313904000800001300204d6f7274616c323004000800001400204d6f7274616c323104000800001500204d6f7274616c323204000800001600204d6f7274616c323304000800001700204d6f7274616c323404000800001800204d6f7274616c323504000800001900204d6f7274616c323604000800001a00204d6f7274616c323704000800001b00204d6f7274616c323804000800001c00204d6f7274616c323904000800001d00204d6f7274616c333004000800001e00204d6f7274616c333104000800001f00204d6f7274616c333204000800002000204d6f7274616c333304000800002100204d6f7274616c333404000800002200204d6f7274616c333504000800002300204d6f7274616c333604000800002400204d6f7274616c333704000800002500204d6f7274616c333804000800002600204d6f7274616c333904000800002700204d6f7274616c343004000800002800204d6f7274616c343104000800002900204d6f7274616c343204000800002a00204d6f7274616c343304000800002b00204d6f7274616c343404000800002c00204d6f7274616c343504000800002d00204d6f7274616c343604000800002e00204d6f7274616c343704000800002f00204d6f7274616c343804000800003000204d6f7274616c343904000800003100204d6f7274616c353004000800003200204d6f7274616c353104000800003300204d6f7274616c353204000800003400204d6f7274616c353304000800003500204d6f7274616c353404000800003600204d6f7274616c353504000800003700204d6f7274616c353604000800003800204d6f7274616c353704000800003900204d6f7274616c353804000800003a00204d6f7274616c353904000800003b00204d6f7274616c363004000800003c00204d6f7274616c363104000800003d00204d6f7274616c363204000800003e00204d6f7274616c363304000800003f00204d6f7274616c363404000800004000204d6f7274616c363504000800004100204d6f7274616c363604000800004200204d6f7274616c363704000800004300204d6f7274616c363804000800004400204d6f7274616c363904000800004500204d6f7274616c373004000800004600204d6f7274616c373104000800004700204d6f7274616c373204000800004800204d6f7274616c373304000800004900204d6f7274616c373404000800004a00204d6f7274616c373504000800004b00204d6f7274616c373604000800004c00204d6f7274616c373704000800004d00204d6f7274616c373804000800004e00204d6f7274616c373904000800004f00204d6f7274616c383004000800005000204d6f7274616c383104000800005100204d6f7274616c383204000800005200204d6f7274616c383304000800005300204d6f7274616c383404000800005400204d6f7274616c383504000800005500204d6f7274616c383604000800005600204d6f7274616c383704000800005700204d6f7274616c383804000800005800204d6f7274616c383904000800005900204d6f7274616c393004000800005a00204d6f7274616c393104000800005b00204d6f7274616c393204000800005c00204d6f7274616c393304000800005d00204d6f7274616c393404000800005e00204d6f7274616c393504000800005f00204d6f7274616c393604000800006000204d6f7274616c393704000800006100204d6f7274616c393804000800006200204d6f7274616c393904000800006300244d6f7274616c31303004000800006400244d6f7274616c31303104000800006500244d6f7274616c31303204000800006600244d6f7274616c31303304000800006700244d6f7274616c31303404000800006800244d6f7274616c31303504000800006900244d6f7274616c31303604000800006a00244d6f7274616c31303704000800006b00244d6f7274616c31303804000800006c00244d6f7274616c31303904000800006d00244d6f7274616c31313004000800006e00244d6f7274616c31313104000800006f00244d6f7274616c31313204000800007000244d6f7274616c31313304000800007100244d6f7274616c31313404000800007200244d6f7274616c31313504000800007300244d6f7274616c31313604000800007400244d6f7274616c31313704000800007500244d6f7274616c31313804000800007600244d6f7274616c31313904000800007700244d6f7274616c31323004000800007800244d6f7274616c31323104000800007900244d6f7274616c31323204000800007a00244d6f7274616c31323304000800007b00244d6f7274616c31323404000800007c00244d6f7274616c31323504000800007d00244d6f7274616c31323604000800007e00244d6f7274616c31323704000800007f00244d6f7274616c31323804000800008000244d6f7274616c31323904000800008100244d6f7274616c31333004000800008200244d6f7274616c31333104000800008300244d6f7274616c31333204000800008400244d6f7274616c31333304000800008500244d6f7274616c31333404000800008600244d6f7274616c31333504000800008700244d6f7274616c31333604000800008800244d6f7274616c31333704000800008900244d6f7274616c31333804000800008a00244d6f7274616c31333904000800008b00244d6f7274616c31343004000800008c00244d6f7274616c31343104000800008d00244d6f7274616c31343204000800008e00244d6f7274616c31343304000800008f00244d6f7274616c31343404000800009000244d6f7274616c31343504000800009100244d6f7274616c31343604000800009200244d6f7274616c31343704000800009300244d6f7274616c31343804000800009400244d6f7274616c31343904000800009500244d6f7274616c31353004000800009600244d6f7274616c31353104000800009700244d6f7274616c31353204000800009800244d6f7274616c31353304000800009900244d6f7274616c31353404000800009a00244d6f7274616c31353504000800009b00244d6f7274616c31353604000800009c00244d6f7274616c31353704000800009d00244d6f7274616c31353804000800009e00244d6f7274616c31353904000800009f00244d6f7274616c3136300400080000a000244d6f7274616c3136310400080000a100244d6f7274616c3136320400080000a200244d6f7274616c3136330400080000a300244d6f7274616c3136340400080000a400244d6f7274616c3136350400080000a500244d6f7274616c3136360400080000a600244d6f7274616c3136370400080000a700244d6f7274616c3136380400080000a800244d6f7274616c3136390400080000a900244d6f7274616c3137300400080000aa00244d6f7274616c3137310400080000ab00244d6f7274616c3137320400080000ac00244d6f7274616c3137330400080000ad00244d6f7274616c3137340400080000ae00244d6f7274616c3137350400080000af00244d6f7274616c3137360400080000b000244d6f7274616c3137370400080000b100244d6f7274616c3137380400080000b200244d6f7274616c3137390400080000b300244d6f7274616c3138300400080000b400244d6f7274616c3138310400080000b500244d6f7274616c3138320400080000b600244d6f7274616c3138330400080000b700244d6f7274616c3138340400080000b800244d6f7274616c3138350400080000b900244d6f7274616c3138360400080000ba00244d6f7274616c3138370400080000bb00244d6f7274616c3138380400080000bc00244d6f7274616c3138390400080000bd00244d6f7274616c3139300400080000be00244d6f7274616c3139310400080000bf00244d6f7274616c3139320400080000c000244d6f7274616c3139330400080000c100244d6f7274616c3139340400080000c200244d6f7274616c3139350400080000c300244d6f7274616c3139360400080000c400244d6f7274616c3139370400080000c500244d6f7274616c3139380400080000c600244d6f7274616c3139390400080000c700244d6f7274616c3230300400080000c800244d6f7274616c3230310400080000c900244d6f7274616c3230320400080000ca00244d6f7274616c3230330400080000cb00244d6f7274616c3230340400080000cc00244d6f7274616c3230350400080000cd00244d6f7274616c3230360400080000ce00244d6f7274616c3230370400080000cf00244d6f7274616c3230380400080000d000244d6f7274616c3230390400080000d100244d6f7274616c3231300400080000d200244d6f7274616c3231310400080000d300244d6f7274616c3231320400080000d400244d6f7274616c3231330400080000d500244d6f7274616c3231340400080000d600244d6f7274616c3231350400080000d700244d6f7274616c3231360400080000d800244d6f7274616c3231370400080000d900244d6f7274616c3231380400080000da00244d6f7274616c3231390400080000db00244d6f7274616c3232300400080000dc00244d6f7274616c3232310400080000dd00244d6f7274616c3232320400080000de00244d6f7274616c3232330400080000df00244d6f7274616c3232340400080000e000244d6f7274616c3232350400080000e100244d6f7274616c3232360400080000e200244d6f7274616c3232370400080000e300244d6f7274616c3232380400080000e400244d6f7274616c3232390400080000e500244d6f7274616c3233300400080000e600244d6f7274616c3233310400080000e700244d6f7274616c3233320400080000e800244d6f7274616c3233330400080000e900244d6f7274616c3233340400080000ea00244d6f7274616c3233350400080000eb00244d6f7274616c3233360400080000ec00244d6f7274616c3233370400080000ed00244d6f7274616c3233380400080000ee00244d6f7274616c3233390400080000ef00244d6f7274616c3234300400080000f000244d6f7274616c3234310400080000f100244d6f7274616c3234320400080000f200244d6f7274616c3234330400080000f300244d6f7274616c3234340400080000f400244d6f7274616c3234350400080000f500244d6f7274616c3234360400080000f600244d6f7274616c3234370400080000f700244d6f7274616c3234380400080000f800244d6f7274616c3234390400080000f900244d6f7274616c3235300400080000fa00244d6f7274616c3235310400080000fb00244d6f7274616c3235320400080000fc00244d6f7274616c3235330400080000fd00244d6f7274616c3235340400080000fe00244d6f7274616c3235350400080000ff0000cd0a10306672616d655f73797374656d28657874656e73696f6e732c636865636b5f6e6f6e636528436865636b4e6f6e636504045400000400e00120543a3a4e6f6e63650000d10a10306672616d655f73797374656d28657874656e73696f6e7330636865636b5f7765696768742c436865636b57656967687404045400000000d50a10306f70616c5f72756e74696d653872756e74696d655f636f6d6d6f6e2c6d61696e74656e616e636540436865636b4d61696e74656e616e636500000000d90a10306f70616c5f72756e74696d653872756e74696d655f636f6d6d6f6e206964656e746974795044697361626c654964656e7469747943616c6c7300000000dd0a088c70616c6c65745f74656d706c6174655f7472616e73616374696f6e5f7061796d656e74604368617267655472616e73616374696f6e5061796d656e7404045401e10a000400bc013042616c616e63654f663c543e0000e10a08306f70616c5f72756e74696d651c52756e74696d6500000000e50a083c70616c6c65745f657468657265756d6046616b655472616e73616374696f6e46696e616c697a657204045401e10a000000e90a102873705f72756e74696d651c67656e657269634c756e636865636b65645f65787472696e73696348556e636865636b656445787472696e736963101c4164647265737301dd011043616c6c015d01245369676e6174757265019d0a14457874726101b50a00040034000000d41853797374656d011853797374656d401c4163636f756e7401010402000c4101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008004e8205468652066756c6c206163636f756e7420696e666f726d6174696f6e20666f72206120706172746963756c6172206163636f756e742049442e3845787472696e736963436f756e74000010040004b820546f74616c2065787472696e7369637320636f756e7420666f72207468652063757272656e7420626c6f636b2e2c426c6f636b576569676874010020180000000000000488205468652063757272656e742077656967687420666f722074686520626c6f636b2e40416c6c45787472696e736963734c656e000010040004410120546f74616c206c656e6774682028696e2062797465732920666f7220616c6c2065787472696e736963732070757420746f6765746865722c20666f72207468652063757272656e7420626c6f636b2e24426c6f636b486173680101040510308000000000000000000000000000000000000000000000000000000000000000000498204d6170206f6620626c6f636b206e756d6265727320746f20626c6f636b206861736865732e3445787472696e736963446174610101040510340400043d012045787472696e73696373206461746120666f72207468652063757272656e7420626c6f636b20286d61707320616e2065787472696e736963277320696e64657820746f206974732064617461292e184e756d6265720100101000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801003080000000000000000000000000000000000000000000000000000000000000000004702048617368206f66207468652070726576696f757320626c6f636b2e18446967657374010038040004f020446967657374206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e184576656e747301004804001ca0204576656e7473206465706f736974656420666f72207468652063757272656e7420626c6f636b2e001d01204e4f54453a20546865206974656d20697320756e626f756e6420616e642073686f756c64207468657265666f7265206e657665722062652072656164206f6e20636861696e2ed020497420636f756c64206f746865727769736520696e666c6174652074686520506f562073697a65206f66206120626c6f636b2e002d01204576656e747320686176652061206c6172676520696e2d6d656d6f72792073697a652e20426f7820746865206576656e747320746f206e6f7420676f206f75742d6f662d6d656d6f7279fc206a75737420696e206361736520736f6d656f6e65207374696c6c207265616473207468656d2066726f6d2077697468696e207468652072756e74696d652e284576656e74436f756e74010010100000000004b820546865206e756d626572206f66206576656e747320696e2074686520604576656e74733c543e60206c6973742e2c4576656e74546f70696373010104023025060400282501204d617070696e67206265747765656e206120746f7069632028726570726573656e74656420627920543a3a486173682920616e64206120766563746f72206f6620696e646578657394206f66206576656e747320696e2074686520603c4576656e74733c543e3e60206c6973742e00510120416c6c20746f70696320766563746f727320686176652064657465726d696e69737469632073746f72616765206c6f636174696f6e7320646570656e64696e67206f6e2074686520746f7069632e2054686973450120616c6c6f7773206c696768742d636c69656e747320746f206c6576657261676520746865206368616e67657320747269652073746f7261676520747261636b696e67206d656368616e69736d20616e64e420696e2063617365206f66206368616e67657320666574636820746865206c697374206f66206576656e7473206f6620696e7465726573742e005901205468652076616c756520686173207468652074797065206028426c6f636b4e756d626572466f723c543e2c204576656e74496e646578296020626563617573652069662077652075736564206f6e6c79206a7573744d012074686520604576656e74496e64657860207468656e20696e20636173652069662074686520746f70696320686173207468652073616d6520636f6e74656e7473206f6e20746865206e65787420626c6f636b0101206e6f206e6f74696669636174696f6e2077696c6c20626520747269676765726564207468757320746865206576656e74206d69676874206265206c6f73742e484c61737452756e74696d65557067726164650000290604000455012053746f726573207468652060737065635f76657273696f6e6020616e642060737065635f6e616d6560206f66207768656e20746865206c6173742072756e74696d6520757067726164652068617070656e65642e545570677261646564546f553332526566436f756e74010035010400044d012054727565206966207765206861766520757067726164656420736f207468617420607479706520526566436f756e74602069732060753332602e2046616c7365202864656661756c7429206966206e6f742e605570677261646564546f547269706c65526566436f756e74010035010400085d012054727565206966207765206861766520757067726164656420736f2074686174204163636f756e74496e666f20636f6e7461696e73207468726565207479706573206f662060526566436f756e74602e2046616c736548202864656661756c7429206966206e6f742e38457865637574696f6e506861736500002106040004882054686520657865637574696f6e207068617365206f662074686520626c6f636b2e01610101541830426c6f636b576569676874732d066d01025b1f5d00070088526a7402004001c2a0a91d000107d00918a44b0200d000010700e6bd4f570200f000010000c2a0a91d000107d0abacbe680200200101070088526a7402004001010700a2941a1d02005000c2a0a91d0000000004d020426c6f636b20262065787472696e7369637320776569676874733a20626173652076616c75657320616e64206c696d6974732e2c426c6f636b4c656e6774683d063000003c00000050000000500004a820546865206d6178696d756d206c656e677468206f66206120626c6f636b2028696e206279746573292e38426c6f636b48617368436f756e74101060090000045501204d6178696d756d206e756d626572206f6620626c6f636b206e756d62657220746f20626c6f636b2068617368206d617070696e677320746f206b65657020286f6c64657374207072756e6564206669727374292e20446257656967687445064040787d010000000000e1f505000000000409012054686520776569676874206f662072756e74696d65206461746162617365206f7065726174696f6e73207468652072756e74696d652063616e20696e766f6b652e1c56657273696f6e49064103106f70616c106f70616c01000000cfbd9800000000003c4fdc4d29738b36d503000000144f3c7616f5c8a102000000df6acb689907609b0400000037e397fc7c91f5e40200000040fe3ad401f8959a06000000d2bc9897eed08f1503000000f78b278be53f454c02000000582211f65bb14b8905000000ab3c0572291feb8b01000000dd718d5cc53262d401000000ea93e3f16f3d696202000000bc9d89904f5b923f0100000037c8bb1350a9a2a80400000032d67ae360cae94401000000e65b00e46cedd0aa0200000003000000010484204765742074686520636861696e27732063757272656e742076657273696f6e2e28535335385072656669784901082a0014a8205468652064657369676e61746564205353353820707265666978206f66207468697320636861696e2e0039012054686973207265706c6163657320746865202273733538466f726d6174222070726f7065727479206465636c6172656420696e2074686520636861696e20737065632e20526561736f6e20697331012074686174207468652072756e74696d652073686f756c64206b6e6f772061626f7574207468652070726566697820696e206f7264657220746f206d616b6520757365206f662069742061737020616e206964656e746966696572206f662074686520636861696e2e01590600485374617465547269654d6967726174696f6e01485374617465547269654d6967726174696f6e0c404d6967726174696f6e50726f6365737301007d013800000000000000000000000000001050204d6967726174696f6e2070726f67726573732e005d0120546869732073746f7265732074686520736e617073686f74206f6620746865206c617374206d69677261746564206b6579732e2049742063616e2062652073657420696e746f206d6f74696f6e20616e64206d6f7665d420666f727761726420627920616e79206f6620746865206d65616e732070726f766964656420627920746869732070616c6c65742e284175746f4c696d6974730100750104000cd420546865206c696d69747320746861742061726520696d706f736564206f6e206175746f6d61746963206d6967726174696f6e732e00d42049662073657420746f204e6f6e652c207468656e206e6f206175746f6d61746963206d6967726174696f6e2068617070656e732e605369676e65644d6967726174696f6e4d61784c696d6974730000790104000ce020546865206d6178696d756d206c696d697473207468617420746865207369676e6564206d6967726174696f6e20636f756c64207573652e00b4204966206e6f74207365742c206e6f207369676e6564207375626d697373696f6e20697320616c6c6f7765642e017101017804244d61784b65794c656e10100002000054b4204d6178696d616c206e756d626572206f6620627974657320746861742061206b65792063616e20686176652e00b0204652414d4520697473656c6620646f6573206e6f74206c696d697420746865206b6579206c656e6774682e01012054686520636f6e63726574652076616c7565206d757374207468657265666f726520646570656e64206f6e20796f75722073746f726167652075736167652e59012041205b606672616d655f737570706f72743a3a73746f726167653a3a53746f726167654e4d6170605d20666f72206578616d706c652063616e206861766520616e20617262697472617279206e756d626572206f664501206b65797320776869636820617265207468656e2068617368656420616e6420636f6e636174656e617465642c20726573756c74696e6720696e206172626974726172696c79206c6f6e67206b6579732e0041012055736520746865202a7374617465206d6967726174696f6e205250432a20746f20726574726965766520746865206c656e677468206f6620746865206c6f6e67657374206b657920696e20796f757201012073746f726167653a203c68747470733a2f2f6769746875622e636f6d2f706172697479746563682f7375627374726174652f6973737565732f31313634323e00290120546865206d6967726174696f6e2077696c6c2068616c7420776974682061206048616c74656460206576656e7420696620746869732076616c756520697320746f6f20736d616c6c2e49012053696e6365207468657265206973206e6f207265616c2070656e616c74792066726f6d206f7665722d657374696d6174696e672c206974206973206164766973656420746f207573652061206c61726765802076616c75652e205468652064656661756c742069732035313220627974652e008020536f6d65206b6579206c656e6774687320666f72207265666572656e63653ad0202d205b606672616d655f737570706f72743a3a73746f726167653a3a53746f7261676556616c7565605d3a2033322062797465c8202d205b606672616d655f737570706f72743a3a73746f726167653a3a53746f726167654d6170605d3a2036342062797465e0202d205b606672616d655f737570706f72743a3a73746f726167653a3a53746f72616765446f75626c654d6170605d3a2039362062797465004820466f72206d6f726520696e666f207365653501203c68747470733a2f2f7777772e736861776e74616272697a692e636f6d2f7375627374726174652f7175657279696e672d7375627374726174652d73746f726167652d7669612d7270632f3e0180013c50617261636861696e53797374656d013c50617261636861696e53797374656d6044556e696e636c756465645365676d656e7401005d060400184901204c617465737420696e636c7564656420626c6f636b2064657363656e64616e7473207468652072756e74696d652061636365707465642e20496e206f7468657220776f7264732c20746865736520617265610120616e636573746f7273206f66207468652063757272656e746c7920657865637574696e6720626c6f636b2077686963682068617665206e6f74206265656e20696e636c7564656420696e20746865206f627365727665644c2072656c61792d636861696e2073746174652e00750120546865207365676d656e74206c656e677468206973206c696d69746564206279207468652063617061636974792072657475726e65642066726f6d20746865205b60436f6e73656e737573486f6f6b605d20636f6e666967757265643c20696e207468652070616c6c65742e6c41676772656761746564556e696e636c756465645365676d656e740000810604000c69012053746f72616765206669656c642074686174206b6565707320747261636b206f662062616e64776964746820757365642062792074686520756e696e636c75646564207365676d656e7420616c6f6e672077697468207468655901206c617465737420746865206c61746573742048524d502077617465726d61726b2e205573656420666f72206c696d6974696e672074686520616363657074616e6365206f66206e657720626c6f636b73207769746890207265737065637420746f2072656c617920636861696e20636f6e73747261696e74732e5450656e64696e6756616c69646174696f6e436f6465010034040018590120496e2063617365206f662061207363686564756c656420757067726164652c20746869732073746f72616765206669656c6420636f6e7461696e73207468652076616c69646174696f6e20636f646520746f20626524206170706c6965642e003d0120417320736f6f6e206173207468652072656c617920636861696e2067697665732075732074686520676f2d6168656164207369676e616c2c2077652077696c6c206f7665727772697465207468657101205b603a636f6465605d5b73705f636f72653a3a73746f726167653a3a77656c6c5f6b6e6f776e5f6b6579733a3a434f44455d2077686963682077696c6c20726573756c7420746865206e65787420626c6f636b2070726f636573730901207769746820746865206e65772076616c69646174696f6e20636f64652e205468697320636f6e636c756465732074686520757067726164652070726f636573732e444e657756616c69646174696f6e436f64650000340400145d012056616c69646174696f6e20636f6465207468617420697320736574206279207468652070617261636861696e20616e6420697320746f20626520636f6d6d756e69636174656420746f20636f6c6c61746f7220616e647820636f6e73657175656e746c79207468652072656c61792d636861696e2e00650120546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b206966206e6f206f746865722070616c6c657420616c7265616479207365742c207468652076616c75652e3856616c69646174696f6e446174610000910104000cd020546865205b6050657273697374656456616c69646174696f6e44617461605d2073657420666f72207468697320626c6f636b2e2d0120546869732076616c756520697320657870656374656420746f20626520736574206f6e6c79206f6e63652070657220626c6f636b20616e642069742773206e657665722073746f7265643420696e2074686520747269652e5044696453657456616c69646174696f6e436f646501003501040004e02057657265207468652076616c69646174696f6e20646174612073657420746f206e6f74696679207468652072656c617920636861696e3f644c61737452656c6179436861696e426c6f636b4e756d6265720100101000000000041d01205468652072656c617920636861696e20626c6f636b206e756d626572206173736f636961746564207769746820746865206c6173742070617261636861696e20626c6f636b2e60557067726164655265737472696374696f6e5369676e616c0100850604001c750120416e206f7074696f6e20776869636820696e64696361746573206966207468652072656c61792d636861696e20726573747269637473207369676e616c6c696e6720612076616c69646174696f6e20636f646520757067726164652e610120496e206f7468657220776f7264732c20696620746869732069732060536f6d656020616e64205b604e657756616c69646174696f6e436f6465605d2069732060536f6d6560207468656e207468652070726f64756365646c2063616e6469646174652077696c6c20626520696e76616c69642e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3855706772616465476f416865616401007906040014dc204f7074696f6e616c207570677261646520676f2d6168656164207369676e616c2066726f6d207468652072656c61792d636861696e2e00710120546869732073746f72616765206974656d2069732061206d6972726f72206f662074686520636f72726573706f6e64696e672076616c756520666f72207468652063757272656e742070617261636861696e2066726f6d207468656d012072656c61792d636861696e2e20546869732076616c756520697320657068656d6572616c207768696368206d65616e7320697420646f65736e277420686974207468652073746f726167652e20546869732076616c756520697360207365742061667465722074686520696e686572656e742e3c52656c6179537461746550726f6f6600009901040018c4205468652073746174652070726f6f6620666f7220746865206c6173742072656c617920706172656e7420626c6f636b2e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e5852656c6576616e744d6573736167696e67537461746500008d0604001c65012054686520736e617073686f74206f6620736f6d652073746174652072656c6174656420746f206d6573736167696e672072656c6576616e7420746f207468652063757272656e742070617261636861696e2061732070657248207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e44486f7374436f6e66696775726174696f6e0000a1060400182901205468652070617261636861696e20686f737420636f6e66696775726174696f6e207468617420776173206f627461696e65642066726f6d207468652072656c617920706172656e742e006d012054686973206669656c64206973206d65616e7420746f2062652075706461746564206561636820626c6f636b2077697468207468652076616c69646174696f6e206461746120696e686572656e742e205468657265666f72652c4d01206265666f72652070726f63657373696e67206f662074686520696e686572656e742c20652e672e20696e20606f6e5f696e697469616c697a656020746869732064617461206d6179206265207374616c652e00ac2054686973206461746120697320616c736f20616273656e742066726f6d207468652067656e657369732e384c617374446d714d7163486561640100a90680000000000000000000000000000000000000000000000000000000000000000010f420546865206c61737420646f776e77617264206d65737361676520717565756520636861696e20686561642077652068617665206f627365727665642e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e404c61737448726d704d716348656164730100ad06040010490120546865206d65737361676520717565756520636861696e2068656164732077652068617665206f62736572766564207065722065616368206368616e6e656c20696e636f6d696e67206368616e6e656c2e00650120546869732076616c7565206973206c6f61646564206265666f726520616e642073617665642061667465722070726f63657373696e6720696e626f756e6420646f776e77617264206d65737361676573206361727269656460206279207468652073797374656d20696e686572656e742e6450726f636573736564446f776e776172644d6573736167657301001010000000000cc8204e756d626572206f6620646f776e77617264206d657373616765732070726f63657373656420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e3448726d7057617465726d61726b01001010000000000ca02048524d502077617465726d61726b2074686174207761732073657420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5048726d704f7574626f756e644d657373616765730100b90604000ca42048524d50206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e385570776172644d6573736167657301006d0104000cac20557077617264206d65737361676573207468617420776572652073656e7420696e206120626c6f636b2e00ec20546869732077696c6c20626520636c656172656420696e20606f6e5f696e697469616c697a6560206f662065616368206e657720626c6f636b2e5450656e64696e675570776172644d6573736167657301006d01040004310120557077617264206d65737361676573207468617420617265207374696c6c2070656e64696e6720616e64206e6f74207965742073656e6420746f207468652072656c617920636861696e2e84416e6e6f756e63656448726d704d6573736167657350657243616e646964617465010010100000000008650120546865206e756d626572206f662048524d50206d65737361676573207765206f6273657276656420696e20606f6e5f696e697469616c697a656020616e64207468757320757365642074686174206e756d62657220666f72f020616e6e6f756e63696e672074686520776569676874206f6620606f6e5f696e697469616c697a656020616e6420606f6e5f66696e616c697a65602e68526573657276656458636d705765696768744f766572726964650000240400085d01205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e672058434d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e645265736572766564446d705765696768744f766572726964650000240400085901205468652077656967687420776520726573657276652061742074686520626567696e6e696e67206f662074686520626c6f636b20666f722070726f63657373696e6720444d50206d657373616765732e2054686973b8206f76657272696465732074686520616d6f756e742073657420696e2074686520436f6e6669672074726169742e44417574686f72697a6564557067726164650000c106040004b820546865206e65787420617574686f72697a656420757067726164652c206966207468657265206973206f6e652e60437573746f6d56616c69646174696f6e486561644461746100003404000c2901204120637573746f6d2068656164206461746120746861742073686f756c642062652072657475726e656420617320726573756c74206f66206076616c69646174655f626c6f636b602e00110120536565206050616c6c65743a3a7365745f637573746f6d5f76616c69646174696f6e5f686561645f646174616020666f72206d6f726520696e666f726d6174696f6e2e01890101840001c506143450617261636861696e496e666f013450617261636861696e496e666f042c50617261636861696e49640100ad0110640000000001c1010000001528417574686f72736869700128417574686f72736869700418417574686f720000000400046420417574686f72206f662063757272656e7420626c6f636b2e000000001644436f6c6c61746f7253656c656374696f6e0144436f6c6c61746f7253656c656374696f6e1034496e76756c6e657261626c65730100c9060400048c2054686520696e76756c6e657261626c652c20666978656420636f6c6c61746f72732e404c6963656e73654465706f7369744f66010104020018400000000000000000000000000000000004ac205468652028636f6d6d756e6974792920636f6c6c6174696f6e206c6963656e736520686f6c646572732e2843616e646964617465730100c906040004bc205468652028636f6d6d756e6974792c206c696d697465642920636f6c6c6174696f6e2063616e646964617465732e444c617374417574686f726564426c6f636b01010405001010000000000484204c61737420626c6f636b20617574686f72656420627920636f6c6c61746f722e01c501018c0001cd06171c53657373696f6e011c53657373696f6e1c2856616c696461746f72730100e5010400047c205468652063757272656e7420736574206f662076616c696461746f72732e3043757272656e74496e646578010010100000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e345175657565644368616e67656401003501040008390120547275652069662074686520756e6465726c79696e672065636f6e6f6d6963206964656e746974696573206f7220776569676874696e6720626568696e64207468652076616c696461746f7273a420686173206368616e67656420696e20746865207175657565642076616c696461746f72207365742e285175657565644b6579730100d1060400083d012054686520717565756564206b65797320666f7220746865206e6578742073657373696f6e2e205768656e20746865206e6578742073657373696f6e20626567696e732c207468657365206b657973e02077696c6c206265207573656420746f2064657465726d696e65207468652076616c696461746f7227732073657373696f6e206b6579732e4844697361626c656456616c696461746f7273010031050400148020496e6469636573206f662064697361626c65642076616c696461746f72732e003d01205468652076656320697320616c77617973206b65707420736f7274656420736f20746861742077652063616e2066696e642077686574686572206120676976656e2076616c696461746f722069733d012064697361626c6564207573696e672062696e617279207365617263682e204974206765747320636c6561726564207768656e20606f6e5f73657373696f6e5f656e64696e67602072657475726e73642061206e657720736574206f66206964656e7469746965732e204e6578744b6579730001040500cd010400049c20546865206e6578742073657373696f6e206b65797320666f7220612076616c696461746f722e204b65794f776e657200010405d90600040004090120546865206f776e6572206f662061206b65792e20546865206b65792069732074686520604b657954797065496460202b2074686520656e636f646564206b65792e01c90101900001e106181041757261011041757261082c417574686f7269746965730100e5060400046c205468652063757272656e7420617574686f72697479207365742e2c43757272656e74536c6f740100ed062000000000000000000c80205468652063757272656e7420736c6f74206f66207468697320626c6f636b2e009420546869732077696c6c2062652073657420696e20606f6e5f696e697469616c697a65602e00000000191c41757261457874011c41757261457874082c417574686f7269746965730100e506040014942053657276657320617320636163686520666f722074686520617574686f7269746965732e0071012054686520617574686f72697469657320696e204175526120617265206f7665727772697474656e20696e20606f6e5f696e697469616c697a6560207768656e2077652073776974636820746f2061206e65772073657373696f6e2c5d0120627574207765207265717569726520746865206f6c6420617574686f72697469657320746f2076657269667920746865207365616c207768656e2076616c69646174696e67206120506f562e20546869732077696c6c0d0120616c77617973206265207570646174656420746f20746865206c6174657374204175526120617574686f72697469657320696e20606f6e5f66696e616c697a65602e20536c6f74496e666f0000f10604000cd82043757272656e7420736c6f742070616972656420776974682061206e756d626572206f6620617574686f72656420626c6f636b732e00982055706461746564206f6e206561636820626c6f636b20696e697469616c697a6174696f6e2e000000001a2042616c616e636573012042616c616e6365731c34546f74616c49737375616e6365010018400000000000000000000000000000000004982054686520746f74616c20756e6974732069737375656420696e207468652073797374656d2e40496e61637469766549737375616e636501001840000000000000000000000000000000000409012054686520746f74616c20756e697473206f66206f75747374616e64696e672064656163746976617465642062616c616e636520696e207468652073797374656d2e1c4163636f756e74010104020014010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080600901205468652042616c616e6365732070616c6c6574206578616d706c65206f662073746f72696e67207468652062616c616e6365206f6620616e206163636f756e742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b19022020202074797065204163636f756e7453746f7265203d2053746f726167654d61705368696d3c53656c663a3a4163636f756e743c52756e74696d653e2c206672616d655f73797374656d3a3a50726f76696465723c52756e74696d653e2c204163636f756e7449642c2053656c663a3a4163636f756e74446174613c42616c616e63653e3e0c20207d102060606000150120596f752063616e20616c736f2073746f7265207468652062616c616e6365206f6620616e206163636f756e7420696e20746865206053797374656d602070616c6c65742e00282023204578616d706c650034206060606e6f636f6d70696c65b02020696d706c2070616c6c65745f62616c616e6365733a3a436f6e66696720666f722052756e74696d65207b7420202074797065204163636f756e7453746f7265203d2053797374656d0c20207d102060606000510120427574207468697320636f6d657320776974682074726164656f6666732c2073746f72696e67206163636f756e742062616c616e63657320696e207468652073797374656d2070616c6c65742073746f7265736d0120606672616d655f73797374656d60206461746120616c6f6e677369646520746865206163636f756e74206461746120636f6e747261727920746f2073746f72696e67206163636f756e742062616c616e63657320696e207468652901206042616c616e636573602070616c6c65742c20776869636820757365732061206053746f726167654d61706020746f2073746f72652062616c616e6365732064617461206f6e6c792e4101204e4f54453a2054686973206973206f6e6c79207573656420696e207468652063617365207468617420746869732070616c6c6574206973207573656420746f2073746f72652062616c616e6365732e144c6f636b730101040200f506040008b820416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e2501204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e20526573657276657301010402000507040004a4204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e14486f6c6473010104020011070400046c20486f6c6473206f6e206163636f756e742062616c616e6365732e1c467265657a6573010104020025070400048820467265657a65206c6f636b73206f6e206163636f756e742062616c616e6365732e01d901019414484578697374656e7469616c4465706f73697418400000000000000000000000000000000020410120546865206d696e696d756d20616d6f756e7420726571756972656420746f206b65657020616e206163636f756e74206f70656e2e204d5553542042452047524541544552205448414e205a45524f2100590120496620796f75202a7265616c6c792a206e65656420697420746f206265207a65726f2c20796f752063616e20656e61626c652074686520666561747572652060696e7365637572655f7a65726f5f65646020666f72610120746869732070616c6c65742e20486f77657665722c20796f7520646f20736f20617420796f7572206f776e207269736b3a20746869732077696c6c206f70656e2075702061206d616a6f7220446f5320766563746f722e590120496e206361736520796f752068617665206d756c7469706c6520736f7572636573206f662070726f7669646572207265666572656e6365732c20796f75206d617920616c736f2067657420756e65787065637465648c206265686176696f757220696620796f7520736574207468697320746f207a65726f2e00f020426f74746f6d206c696e653a20446f20796f757273656c662061206661766f757220616e64206d616b65206974206174206c65617374206f6e6521204d61784c6f636b7310103200000008f420546865206d6178696d756d206e756d626572206f66206c6f636b7320746861742073686f756c64206578697374206f6e20616e206163636f756e742edc204e6f74207374726963746c7920656e666f726365642c20627574207573656420666f722077656967687420657374696d6174696f6e2e2c4d61785265736572766573101032000000040d0120546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e204d6178486f6c647310100a00000004190120546865206d6178696d756d206e756d626572206f6620686f6c647320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e284d6178467265657a657310100a00000004610120546865206d6178696d756d206e756d626572206f6620696e646976696475616c20667265657a65206c6f636b7320746861742063616e206578697374206f6e20616e206163636f756e7420617420616e792074696d652e0131071e2454696d657374616d70012454696d657374616d70080c4e6f7701002c20000000000000000004902043757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e2444696455706461746501003501040004b420446964207468652074696d657374616d7020676574207570646174656420696e207468697320626c6f636b3f01e9010004344d696e696d756d506572696f642c207017000000000000104d0120546865206d696e696d756d20706572696f64206265747765656e20626c6f636b732e204265776172652074686174207468697320697320646966666572656e7420746f20746865202a65787065637465642a5d0120706572696f6420746861742074686520626c6f636b2070726f64756374696f6e206170706172617475732070726f76696465732e20596f75722063686f73656e20636f6e73656e7375732073797374656d2077696c6c5d012067656e6572616c6c7920776f726b2077697468207468697320746f2064657465726d696e6520612073656e7369626c6520626c6f636b2074696d652e20652e672e20466f7220417572612c2069742077696c6c206265a020646f75626c65207468697320706572696f64206f6e2064656661756c742073657474696e67732e0020485472616e73616374696f6e5061796d656e7401485472616e73616374696f6e5061796d656e7408444e6578744665654d756c7469706c6965720100350740000064a7b3b6e00d0000000000000000003853746f7261676556657273696f6e0100390704000000019c04604f7065726174696f6e616c4665654d756c7469706c696572080405545901204120666565206d756c6974706c69657220666f7220604f7065726174696f6e616c602065787472696e7369637320746f20636f6d7075746520227669727475616c207469702220746f20626f6f73742074686569722c20607072696f7269747960004d0120546869732076616c7565206973206d756c7469706c656420627920746865206066696e616c5f6665656020746f206f627461696e206120227669727475616c20746970222074686174206973206c61746572f420616464656420746f20612074697020636f6d706f6e656e7420696e20726567756c617220607072696f72697479602063616c63756c6174696f6e732e4d01204974206d65616e732074686174206120604e6f726d616c60207472616e73616374696f6e2063616e2066726f6e742d72756e20612073696d696c61726c792d73697a656420604f7065726174696f6e616c6041012065787472696e736963202877697468206e6f20746970292c20627920696e636c7564696e672061207469702076616c75652067726561746572207468616e20746865207669727475616c207469702e003c20606060727573742c69676e6f726540202f2f20466f7220604e6f726d616c608c206c6574207072696f72697479203d207072696f726974795f63616c6328746970293b0054202f2f20466f7220604f7065726174696f6e616c601101206c6574207669727475616c5f746970203d2028696e636c7573696f6e5f666565202b2074697029202a204f7065726174696f6e616c4665654d756c7469706c6965723bc4206c6574207072696f72697479203d207072696f726974795f63616c6328746970202b207669727475616c5f746970293b1020606060005101204e6f746520746861742073696e636520776520757365206066696e616c5f6665656020746865206d756c7469706c696572206170706c69657320616c736f20746f2074686520726567756c61722060746970605d012073656e74207769746820746865207472616e73616374696f6e2e20536f2c206e6f74206f6e6c7920646f657320746865207472616e73616374696f6e206765742061207072696f726974792062756d702062617365646101206f6e207468652060696e636c7573696f6e5f666565602c2062757420776520616c736f20616d706c6966792074686520696d70616374206f662074697073206170706c69656420746f20604f7065726174696f6e616c6038207472616e73616374696f6e732e002120547265617375727901205472656173757279103450726f706f73616c436f756e74010010100000000004a4204e756d626572206f662070726f706f73616c7320746861742068617665206265656e206d6164652e2450726f706f73616c7300010405103d070400047c2050726f706f73616c7320746861742068617665206265656e206d6164652e2c4465616374697661746564010018400000000000000000000000000000000004f02054686520616d6f756e7420776869636820686173206265656e207265706f7274656420617320696e61637469766520746f2043757272656e63792e24417070726f76616c7301004107040004f82050726f706f73616c20696e646963657320746861742068617665206265656e20617070726f76656420627574206e6f742079657420617761726465642e01ed0101a01c3050726f706f73616c426f6e6445071050c30000085501204672616374696f6e206f6620612070726f706f73616c27732076616c756520746861742073686f756c6420626520626f6e64656420696e206f7264657220746f20706c616365207468652070726f706f73616c2e110120416e2061636365707465642070726f706f73616c2067657473207468657365206261636b2e20412072656a65637465642070726f706f73616c20646f6573206e6f742e4c50726f706f73616c426f6e644d696e696d756d1840000064a7b3b6e00d0000000000000000044901204d696e696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e4c50726f706f73616c426f6e644d6178696d756d250544010000a0dec5adc9353600000000000000044901204d6178696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e2c5370656e64506572696f64101019000000048820506572696f64206265747765656e2073756363657373697665207370656e64732e104275726e450710000000000411012050657263656e74616765206f662073706172652066756e64732028696620616e7929207468617420617265206275726e7420706572207370656e6420706572696f642e2050616c6c6574496449072070792f74727372790419012054686520747265617375727927732070616c6c65742069642c207573656420666f72206465726976696e672069747320736f7665726569676e206163636f756e742049442e304d6178417070726f76616c731010640000000c150120546865206d6178696d756d206e756d626572206f6620617070726f76616c7320746861742063616e207761697420696e20746865207370656e64696e672071756575652e004d01204e4f54453a205468697320706172616d6574657220697320616c736f20757365642077697468696e2074686520426f756e746965732050616c6c657420657874656e73696f6e20696620656e61626c65642e014d0722105375646f01105375646f040c4b6579000000040004842054686520604163636f756e74496460206f6620746865207375646f206b65792e01f10101a400015107231c56657374696e67011c56657374696e67044056657374696e675363686564756c65730101040200550704000c842056657374696e67207363686564756c6573206f6620616e206163636f756e742e00e02056657374696e675363686564756c65733a206d6170204163636f756e744964203d3e205665633c56657374696e675363686564756c653e01f50101b404444d696e5665737465645472616e7366657218400000e8890423c78a000000000000000004e820546865206d696e696d756d20616d6f756e74207472616e7366657272656420746f2063616c6c20607665737465645f7472616e73666572602e015907251c58546f6b656e730001fd0101c0083053656c664c6f636174696f6ed414010100bd2004542053656c6620636861696e206c6f636174696f6e2e344261736558636d57656967687424180284d717a10f104420426173652058434d207765696768742e00f8205468652061637475616c6c792077656967687420666f7220616e2058434d206d6573736167652069732060543a3a4261736558636d576569676874202b6c20543a3a576569676865723a3a77656967687428266d736729602e015d072618546f6b656e730118546f6b656e731034546f74616c49737375616e6365010104050d0118400000000000000000000000000000000004902054686520746f74616c2069737375616e6365206f66206120746f6b656e20747970652e144c6f636b73010108020561076507040008d820416e79206c6971756964697479206c6f636b73206f66206120746f6b656e207479706520756e64657220616e206163636f756e742e2501204e4f54453a2053686f756c64206f6e6c79206265206163636573736564207768656e2073657474696e672c206368616e67696e6720616e642066726565696e672061206c6f636b2e204163636f756e7473010108020561077107c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018b8205468652062616c616e6365206f66206120746f6b656e207479706520756e64657220616e206163636f756e742e00fc204e4f54453a2049662074686520746f74616c2069732065766572207a65726f2c206465637265617365206163636f756e7420726566206163636f756e742e001901204e4f54453a2054686973206973206f6e6c79207573656420696e20746865206361736520746861742074686973206d6f64756c65206973207573656420746f2073746f7265282062616c616e6365732e205265736572766573010108020561077507040004a4204e616d6564207265736572766573206f6e20736f6d65206163636f756e742062616c616e6365732e014d0201090108204d61784c6f636b73101032000000002c4d61785265736572766573101032000000040d0120546865206d6178696d756d206e756d626572206f66206e616d656420726573657276657320746861742063616e206578697374206f6e20616e206163636f756e742e01810727204964656e7469747901204964656e7469747910284964656e746974794f660001040500f90204000c210120496e666f726d6174696f6e20746861742069732070657274696e656e7420746f206964656e746966792074686520656e7469747920626568696e6420616e206163636f756e742e00c02054574f582d4e4f54453a204f4b20e2809520604163636f756e7449646020697320612073656375726520686173682e1c53757065724f660001040200e1020400086101205468652073757065722d6964656e74697479206f6620616e20616c7465726e6174697665202273756222206964656e7469747920746f676574686572207769746820697473206e616d652c2077697468696e2074686174510120636f6e746578742e20496620746865206163636f756e74206973206e6f7420736f6d65206f74686572206163636f756e742773207375622d6964656e746974792c207468656e206a75737420604e6f6e65602e18537562734f660101040500850744000000000000000000000000000000000014b820416c7465726e6174697665202273756222206964656e746974696573206f662074686973206163636f756e742e001d0120546865206669727374206974656d20697320746865206465706f7369742c20746865207365636f6e64206973206120766563746f72206f6620746865206163636f756e74732e00c02054574f582d4e4f54453a204f4b20e2809520604163636f756e7449646020697320612073656375726520686173682e285265676973747261727301008d070400104d012054686520736574206f6620726567697374726172732e204e6f7420657870656374656420746f206765742076657279206269672061732063616e206f6e6c79206265206164646564207468726f7567682061a8207370656369616c206f726967696e20286c696b656c79206120636f756e63696c206d6f74696f6e292e0029012054686520696e64657820696e746f20746869732063616e206265206361737420746f2060526567697374726172496e6465786020746f2067657420612076616c69642076616c75652e015102011501183042617369634465706f73697418400000e8890423c78a000000000000000004d42054686520616d6f756e742068656c64206f6e206465706f73697420666f7220612072656769737465726564206964656e74697479304669656c644465706f7369741840008062175ed158000000000000000000042d012054686520616d6f756e742068656c64206f6e206465706f73697420706572206164646974696f6e616c206669656c6420666f7220612072656769737465726564206964656e746974792e445375624163636f756e744465706f73697418400000c84e676dc11b00000000000000000c65012054686520616d6f756e742068656c64206f6e206465706f73697420666f7220612072656769737465726564207375626163636f756e742e20546869732073686f756c64206163636f756e7420666f7220746865206661637465012074686174206f6e652073746f72616765206974656d27732076616c75652077696c6c20696e637265617365206279207468652073697a65206f6620616e206163636f756e742049442c20616e642074686572652077696c6c350120626520616e6f746865722074726965206974656d2077686f73652076616c7565206973207468652073697a65206f6620616e206163636f756e7420494420706c75732033322062797465732e384d61785375624163636f756e7473101064000000040d0120546865206d6178696d756d206e756d626572206f66207375622d6163636f756e747320616c6c6f77656420706572206964656e746966696564206163636f756e742e4c4d61784164646974696f6e616c4669656c6473101064000000086501204d6178696d756d206e756d626572206f66206164646974696f6e616c206669656c64732074686174206d61792062652073746f72656420696e20616e2049442e204e656564656420746f20626f756e642074686520492f4fe020726571756972656420746f2061636365737320616e206964656e746974792c206275742063616e2062652070726574747920686967682e344d617852656769737472617273101014000000085101204d61786d696d756d206e756d626572206f66207265676973747261727320616c6c6f77656420696e207468652073797374656d2e204e656564656420746f20626f756e642074686520636f6d706c65786974797c206f662c20652e672e2c207570646174696e67206a756467656d656e74732e019d072820507265696d6167650120507265696d6167650824537461747573466f720001040630a1070400049020546865207265717565737420737461747573206f66206120676976656e20686173682e2c507265696d616765466f7200010406ad07b1070400000119030119010001b507292444656d6f6372616379012444656d6f6372616379303c5075626c696350726f70436f756e74010010100000000004f420546865206e756d626572206f6620287075626c6963292070726f706f73616c7320746861742068617665206265656e206d61646520736f206661722e2c5075626c696350726f70730100b907040004050120546865207075626c69632070726f706f73616c732e20556e736f727465642e20546865207365636f6e64206974656d206973207468652070726f706f73616c2e244465706f7369744f660001040510c50704000c842054686f73652077686f2068617665206c6f636b65642061206465706f7369742e00d82054574f582d4e4f54453a20536166652c20617320696e6372656173696e6720696e7465676572206b6579732061726520736166652e3c5265666572656e64756d436f756e74010010100000000004310120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e6461207374617274656420736f206661722e344c6f77657374556e62616b6564010010100000000008250120546865206c6f77657374207265666572656e64756d20696e64657820726570726573656e74696e6720616e20756e62616b6564207265666572656e64756d2e20457175616c20746fdc20605265666572656e64756d436f756e74602069662074686572652069736e2774206120756e62616b6564207265666572656e64756d2e405265666572656e64756d496e666f4f660001040510cd0704000cb420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e0009012054574f582d4e4f54453a205341464520617320696e646578657320617265206e6f7420756e64657220616e2061747461636b6572e280997320636f6e74726f6c2e20566f74696e674f660101040500d907d8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000105d0120416c6c20766f74657320666f72206120706172746963756c617220766f7465722e2057652073746f7265207468652062616c616e636520666f7220746865206e756d626572206f6620766f74657320746861742077655d012068617665207265636f726465642e20546865207365636f6e64206974656d2069732074686520746f74616c20616d6f756e74206f662064656c65676174696f6e732c20746861742077696c6c2062652061646465642e00e82054574f582d4e4f54453a205341464520617320604163636f756e7449646073206172652063727970746f2068617368657320616e797761792e544c6173745461626c656457617345787465726e616c010035010400085901205472756520696620746865206c617374207265666572656e64756d207461626c656420776173207375626d69747465642065787465726e616c6c792e2046616c7365206966206974207761732061207075626c6963282070726f706f73616c2e304e65787445787465726e616c0000f107040010590120546865207265666572656e64756d20746f206265207461626c6564207768656e6576657220697420776f756c642062652076616c696420746f207461626c6520616e2065787465726e616c2070726f706f73616c2e550120546869732068617070656e73207768656e2061207265666572656e64756d206e6565647320746f206265207461626c656420616e64206f6e65206f662074776f20636f6e646974696f6e7320617265206d65743aa4202d20604c6173745461626c656457617345787465726e616c60206973206066616c7365603b206f7268202d20605075626c696350726f70736020697320656d7074792e24426c61636b6c6973740001040630f50704000851012041207265636f7264206f662077686f207665746f656420776861742e204d6170732070726f706f73616c206861736820746f206120706f737369626c65206578697374656e7420626c6f636b206e756d626572e82028756e74696c207768656e206974206d6179206e6f742062652072657375626d69747465642920616e642077686f207665746f65642069742e3443616e63656c6c6174696f6e73010104063035010400042901205265636f7264206f6620616c6c2070726f706f73616c7320746861742068617665206265656e207375626a65637420746f20656d657267656e63792063616e63656c6c6174696f6e2e284d657461646174614f66000104022d0130040018ec2047656e6572616c20696e666f726d6174696f6e20636f6e6365726e696e6720616e792070726f706f73616c206f72207265666572656e64756d2e6901205468652060507265696d61676548617368602072656665727320746f2074686520707265696d616765206f66207468652060507265696d61676573602070726f76696465722077686963682063616e2062652061204a534f4e882064756d70206f7220495046532068617368206f662061204a534f4e2066696c652e00750120436f6e73696465722061206761726261676520636f6c6c656374696f6e20666f722061206d65746164617461206f662066696e6973686564207265666572656e64756d7320746f2060756e7265717565737460202872656d6f76652944206c6172676520707265696d616765732e011d03011d01303c456e6163746d656e74506572696f6410101900000014e82054686520706572696f64206265747765656e20612070726f706f73616c206265696e6720617070726f76656420616e6420656e61637465642e0031012049742073686f756c642067656e6572616c6c792062652061206c6974746c65206d6f7265207468616e2074686520756e7374616b6520706572696f6420746f20656e737572652074686174510120766f74696e67207374616b657273206861766520616e206f70706f7274756e69747920746f2072656d6f7665207468656d73656c7665732066726f6d207468652073797374656d20696e207468652063617365b4207768657265207468657920617265206f6e20746865206c6f73696e672073696465206f66206120766f74652e304c61756e6368506572696f6410102c01000004e420486f77206f6674656e2028696e20626c6f636b7329206e6577207075626c6963207265666572656e646120617265206c61756e636865642e30566f74696e67506572696f6410102c01000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e44566f74654c6f636b696e67506572696f64101019000000109020546865206d696e696d756d20706572696f64206f6620766f7465206c6f636b696e672e0065012049742073686f756c64206265206e6f2073686f72746572207468616e20656e6163746d656e7420706572696f6420746f20656e73757265207468617420696e207468652063617365206f6620616e20617070726f76616c2c49012074686f7365207375636365737366756c20766f7465727320617265206c6f636b656420696e746f2074686520636f6e73657175656e636573207468617420746865697220766f74657320656e7461696c2e384d696e696d756d4465706f73697418400000000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e38496e7374616e74416c6c6f776564350104000c550120496e64696361746f7220666f72207768657468657220616e20656d657267656e6379206f726967696e206973206576656e20616c6c6f77656420746f2068617070656e2e20536f6d6520636861696e73206d617961012077616e7420746f207365742074686973207065726d616e656e746c7920746f206066616c7365602c206f7468657273206d61792077616e7420746f20636f6e646974696f6e206974206f6e207468696e67732073756368a020617320616e207570677261646520686176696e672068617070656e656420726563656e746c792e5446617374547261636b566f74696e67506572696f6410103200000004ec204d696e696d756d20766f74696e6720706572696f6420616c6c6f77656420666f72206120666173742d747261636b207265666572656e64756d2e34436f6f6c6f6666506572696f6410102c01000004610120506572696f6420696e20626c6f636b7320776865726520616e2065787465726e616c2070726f706f73616c206d6179206e6f742062652072652d7375626d6974746564206166746572206265696e67207665746f65642e204d6178566f74657310106400000010b020546865206d6178696d756d206e756d626572206f6620766f74657320666f7220616e206163636f756e742e00d420416c736f207573656420746f20636f6d70757465207765696768742c20616e206f7665726c79206269672076616c75652063616e1501206c65616420746f2065787472696e7369632077697468207665727920626967207765696768743a20736565206064656c65676174656020666f7220696e7374616e63652e304d617850726f706f73616c73101064000000040d0120546865206d6178696d756d206e756d626572206f66207075626c69632070726f706f73616c7320746861742063616e20657869737420617420616e792074696d652e2c4d61784465706f73697473101064000000041d0120546865206d6178696d756d206e756d626572206f66206465706f736974732061207075626c69632070726f706f73616c206d6179206861766520617420616e792074696d652e384d6178426c61636b6c697374656410106400000004d820546865206d6178696d756d206e756d626572206f66206974656d732077686963682063616e20626520626c61636b6c69737465642e01f9072a1c436f756e63696c011c436f756e63696c182450726f706f73616c730100fd07040004902054686520686173686573206f6620746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600010406305d01040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700010406300108040004b420566f746573206f6e206120676976656e2070726f706f73616c2c206966206974206973206f6e676f696e672e3450726f706f73616c436f756e74010010100000000004482050726f706f73616c7320736f206661722e1c4d656d626572730100e5010400043901205468652063757272656e74206d656d62657273206f662074686520636f6c6c6563746976652e20546869732069732073746f72656420736f7274656420286a7573742062792076616c7565292e145072696d65000000040004650120546865207072696d65206d656d62657220746861742068656c70732064657465726d696e65207468652064656661756c7420766f7465206265686176696f7220696e2063617365206f6620616273656e746174696f6e732e012d0301310104444d617850726f706f73616c57656967687424280700a0db215d0200000104250120546865206d6178696d756d20776569676874206f6620612064697370617463682063616c6c20746861742063616e2062652070726f706f73656420616e642065786563757465642e0105082b48546563686e6963616c436f6d6d69747465650148546563686e6963616c436f6d6d6974746565182450726f706f73616c7301000908040004902054686520686173686573206f6620746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600010406305d01040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700010406300108040004b420566f746573206f6e206120676976656e2070726f706f73616c2c206966206974206973206f6e676f696e672e3450726f706f73616c436f756e74010010100000000004482050726f706f73616c7320736f206661722e1c4d656d626572730100e5010400043901205468652063757272656e74206d656d62657273206f662074686520636f6c6c6563746976652e20546869732069732073746f72656420736f7274656420286a7573742062792076616c7565292e145072696d65000000040004650120546865207072696d65206d656d62657220746861742068656c70732064657465726d696e65207468652064656661756c7420766f7465206265686176696f7220696e2063617365206f6620616273656e746174696f6e732e01310301390104444d617850726f706f73616c57656967687424280700a0db215d0200000104250120546865206d6178696d756d20776569676874206f6620612064697370617463682063616c6c20746861742063616e2062652070726f706f73656420616e642065786563757465642e010d082c44436f756e63696c4d656d626572736869700144436f756e63696c4d656d62657273686970081c4d656d6265727301001108040004c8205468652063757272656e74206d656d626572736869702c2073746f72656420617320616e206f726465726564205665632e145072696d65000000040004a4205468652063757272656e74207072696d65206d656d6265722c206966206f6e65206578697374732e013503013d01000115082d70546563686e6963616c436f6d6d69747465654d656d626572736869700170546563686e6963616c436f6d6d69747465654d656d62657273686970081c4d656d6265727301001908040004c8205468652063757272656e74206d656d626572736869702c2073746f72656420617320616e206f726465726564205665632e145072696d65000000040004a4205468652063757272656e74207072696d65206d656d6265722c206966206f6e65206578697374732e01390301410100011d082e5046656c6c6f7773686970436f6c6c656374697665015046656c6c6f7773686970436f6c6c656374697665182c4d656d626572436f756e7401010405490110100000000008690120546865206e756d626572206f66206d656d6265727320696e2074686520636f6c6c6563746976652077686f2068617665206174206c65617374207468652072616e6b206163636f7264696e6720746f2074686520696e64657830206f6620746865207665632e1c4d656d62657273000104050021080400049c205468652063757272656e74206d656d62657273206f662074686520636f6c6c6563746976652e244964546f496e646578000108050525081004000461012054686520696e646578206f6620656163682072616e6b732773206d656d62657220696e746f207468652067726f7570206f66206d656d626572732077686f2068617665206174206c6561737420746861742072616e6b2e24496e646578546f496400010805052908000400085d0120546865206d656d6265727320696e2074686520636f6c6c65637469766520627920696e6465782e20416c6c20696e646963657320696e207468652072616e67652060302e2e4d656d626572436f756e74602077696c6c65012072657475726e2060536f6d65602c20686f77657665722061206d656d626572277320696e646578206973206e6f742067756172616e7465656420746f2072656d61696e20756e6368616e676564206f7665722074696d652e18566f74696e6700010802052d084d01040004b420566f746573206f6e206120676976656e2070726f706f73616c2c206966206974206973206f6e676f696e672e34566f74696e67436c65616e757000010402103108040000013d03014501000135082f4c46656c6c6f77736869705265666572656e6461014c46656c6c6f77736869705265666572656e6461143c5265666572656e64756d436f756e74010010100000000004310120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e6461207374617274656420736f206661722e445265666572656e64756d496e666f466f7200010402103908040004b420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e28547261636b517565756501010405490159080400105d012054686520736f72746564206c697374206f66207265666572656e646120726561647920746f206265206465636964656420627574206e6f7420796574206265696e6720646563696465642c206f7264657265642062797c20636f6e76696374696f6e2d776569676874656420617070726f76616c732e00410120546869732073686f756c6420626520656d70747920696620604465636964696e67436f756e7460206973206c657373207468616e2060547261636b496e666f3a3a6d61785f6465636964696e67602e344465636964696e67436f756e7401010405490110100000000004c420546865206e756d626572206f66207265666572656e6461206265696e6720646563696465642063757272656e746c792e284d657461646174614f66000104021030040018050120546865206d6574616461746120697320612067656e6572616c20696e666f726d6174696f6e20636f6e6365726e696e6720746865207265666572656e64756d2e6901205468652060507265696d61676548617368602072656665727320746f2074686520707265696d616765206f66207468652060507265696d61676573602070726f76696465722077686963682063616e2062652061204a534f4e882064756d70206f7220495046532068617368206f662061204a534f4e2066696c652e00750120436f6e73696465722061206761726261676520636f6c6c656374696f6e20666f722061206d65746164617461206f662066696e6973686564207265666572656e64756d7320746f2060756e7265717565737460202872656d6f76652944206c6172676520707265696d616765732e01410301550114445375626d697373696f6e4465706f7369741840e803000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e244d617851756575656410106400000004e4204d6178696d756d2073697a65206f6620746865207265666572656e64756d20717565756520666f7220612073696e676c6520747261636b2e44556e6465636964696e6754696d656f757410102c01000008550120546865206e756d626572206f6620626c6f636b73206166746572207375626d697373696f6e20746861742061207265666572656e64756d206d75737420626567696e206265696e6720646563696465642062792ee4204f6e63652074686973207061737365732c207468656e20616e796f6e65206d61792063616e63656c20746865207265666572656e64756d2e34416c61726d496e74657276616c1010010000000c5d01205175616e74697a6174696f6e206c6576656c20666f7220746865207265666572656e64756d2077616b657570207363686564756c65722e204120686967686572206e756d6265722077696c6c20726573756c7420696e5d012066657765722073746f726167652072656164732f777269746573206e656564656420666f7220736d616c6c657220766f746572732c2062757420616c736f20726573756c7420696e2064656c61797320746f207468655501206175746f6d61746963207265666572656e64756d20737461747573206368616e6765732e204578706c6963697420736572766963696e6720696e737472756374696f6e732061726520756e61666665637465642e18547261636b735d085501040a004c64656d6f63726163795f70726f706f73616c730a0000000000e8890423c78a0000000000000000190000002c01000032000000050000000000ca9a3b0065cd1d00ca9a3b0000ca9a3b000000000065cd1d04e020496e666f726d6174696f6e20636f6e6365726e696e672074686520646966666572656e74207265666572656e64756d20747261636b732e01750830245363686564756c657201245363686564756c65720c3c496e636f6d706c65746553696e6365000010040000184167656e6461010104051079080400044d01204974656d7320746f2062652065786563757465642c20696e64657865642062792074686520626c6f636b206e756d626572207468617420746865792073686f756c64206265206578656375746564206f6e2e184c6f6f6b757000010405047903040010f8204c6f6f6b75702066726f6d2061206e616d6520746f2074686520626c6f636b206e756d62657220616e6420696e646578206f6620746865207461736b2e00590120466f72207633202d3e207634207468652070726576696f75736c7920756e626f756e646564206964656e7469746965732061726520426c616b65322d3235362068617368656420746f20666f726d2074686520763430206964656e7469746965732e01710301b90508344d6178696d756d57656967687424280700b864d9450200c00004290120546865206d6178696d756d207765696768742074686174206d6179206265207363686564756c65642070657220626c6f636b20666f7220616e7920646973706174636861626c65732e504d61785363686564756c6564506572426c6f636b101032000000141d0120546865206d6178696d756d206e756d626572206f66207363686564756c65642063616c6c7320696e2074686520717565756520666f7220612073696e676c6520626c6f636b2e0018204e4f54453a5101202b20446570656e64656e742070616c6c657473272062656e63686d61726b73206d696768742072657175697265206120686967686572206c696d697420666f72207468652073657474696e672e205365742061c420686967686572206c696d697420756e646572206072756e74696d652d62656e63686d61726b736020666561747572652e018908311c4f726967696e730000000000632458636d705175657565012458636d7051756575652844496e626f756e6458636d7053746174757301008d080400049420537461747573206f662074686520696e626f756e642058434d50206368616e6e656c732e4c496e626f756e6458636d704d657373616765730101080205a50834040004190120496e626f756e64206167677265676174652058434d50206d657373616765732e2049742063616e206f6e6c79206265206f6e6520706572205061726149642f626c6f636b2e484f7574626f756e6458636d705374617475730100a9080400185d0120546865206e6f6e2d656d7074792058434d50206368616e6e656c7320696e206f72646572206f66206265636f6d696e67206e6f6e2d656d7074792c20616e642074686520696e646578206f6620746865206669727374510120616e64206c617374206f7574626f756e64206d6573736167652e204966207468652074776f20696e64696365732061726520657175616c2c207468656e20697420696e6469636174657320616e20656d707479590120717565756520616e64207468657265206d7573742062652061206e6f6e2d604f6b6020604f7574626f756e64537461747573602e20576520617373756d65207175657565732067726f77206e6f20677265617465725901207468616e203635353335206974656d732e20517565756520696e646963657320666f72206e6f726d616c206d6573736167657320626567696e206174206f6e653b207a65726f20697320726573657276656420696e11012063617365206f6620746865206e65656420746f2073656e64206120686967682d7072696f72697479207369676e616c206d657373616765207468697320626c6f636b2e09012054686520626f6f6c20697320747275652069662074686572652069732061207369676e616c206d6573736167652077616974696e6720746f2062652073656e742e504f7574626f756e6458636d704d657373616765730101080205b50834040004bc20546865206d65737361676573206f7574626f756e6420696e206120676976656e2058434d50206368616e6e656c2e385369676e616c4d6573736167657301010402ad0134040004a020416e79207369676e616c206d657373616765732077616974696e6720746f2062652073656e742e2c5175657565436f6e6669670100b90874020000000500000001000000821a06000008000700c817a804020004000415012054686520636f6e66696775726174696f6e20776869636820636f6e74726f6c73207468652064796e616d696373206f6620746865206f7574626f756e642071756575652e284f766572776569676874000104052cbd08040010050120546865206d657373616765732074686174206578636565646564206d617820696e646976696475616c206d65737361676520776569676874206275646765742e003901205468657365206d657373616765207374617920696e20746869732073746f72616765206d617020756e74696c207468657920617265206d616e75616c6c79206469737061746368656420766961582060736572766963655f6f766572776569676874602e50436f756e746572466f724f766572776569676874010010100000000004ac436f756e74657220666f72207468652072656c6174656420636f756e7465642073746f72616765206d61703c4f766572776569676874436f756e7401002c20000000000000000008690120546865206e756d626572206f66206f766572776569676874206d657373616765732065766572207265636f7264656420696e20604f766572776569676874602e20416c736f20646f75626c657320617320746865206e6578748420617661696c61626c652066726565206f76657277656967687420696e6465782e38517565756553757370656e6465640100350104000441012057686574686572206f72206e6f74207468652058434d502071756575652069732073757370656e6465642066726f6d20657865637574696e6720696e636f6d696e672058434d73206f72206e6f742e017d0301bd050001c108322c506f6c6b61646f7458636d012c506f6c6b61646f7458636d30305175657279436f756e74657201002c200000000000000000048820546865206c617465737420617661696c61626c6520717565727920696e6465782e1c51756572696573000104022cc5080400045420546865206f6e676f696e6720717565726965732e28417373657454726170730101040630101000000000106820546865206578697374696e672061737365742074726170732e007501204b65792069732074686520626c616b6532203235362068617368206f6620286f726967696e2c2076657273696f6e656420604d756c7469417373657473602920706169722e2056616c756520697320746865206e756d626572206f661d012074696d65732074686973207061697220686173206265656e20747261707065642028757375616c6c79206a75737420312069662069742065786973747320617420616c6c292e385361666558636d56657273696f6e00001004000861012044656661756c742076657273696f6e20746f20656e636f64652058434d207768656e206c61746573742076657273696f6e206f662064657374696e6174696f6e20697320756e6b6e6f776e2e20496620604e6f6e65602c3d01207468656e207468652064657374696e6174696f6e732077686f73652058434d2076657273696f6e20697320756e6b6e6f776e2061726520636f6e7369646572656420756e726561636861626c652e40537570706f7274656456657273696f6e0001080502d90810040004f020546865204c61746573742076657273696f6e732074686174207765206b6e6f7720766172696f7573206c6f636174696f6e7320737570706f72742e4056657273696f6e4e6f746966696572730001080502d9082c040004050120416c6c206c6f636174696f6e7320746861742077652068617665207265717565737465642076657273696f6e206e6f74696669636174696f6e732066726f6d2e5056657273696f6e4e6f74696679546172676574730001080502d908dd0804000871012054686520746172676574206c6f636174696f6e73207468617420617265207375627363726962656420746f206f75722076657273696f6e206368616e6765732c2061732077656c6c20617320746865206d6f737420726563656e7494206f66206f75722076657273696f6e7320776520696e666f726d6564207468656d206f662e5456657273696f6e446973636f7665727951756575650100e10804000c65012044657374696e6174696f6e732077686f7365206c61746573742058434d2076657273696f6e20776520776f756c64206c696b6520746f206b6e6f772e204475706c696361746573206e6f7420616c6c6f7765642c20616e6471012074686520607533326020636f756e74657220697320746865206e756d626572206f662074696d6573207468617420612073656e6420746f207468652064657374696e6174696f6e20686173206265656e20617474656d707465642c8c20776869636820697320757365642061732061207072696f726974697a6174696f6e2e4043757272656e744d6967726174696f6e0000ed080400049c205468652063757272656e74206d6967726174696f6e27732073746167652c20696620616e792e5452656d6f74654c6f636b656446756e6769626c657300010c050202f508fd08040004f02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e20612072656d6f746520636861696e2e3c4c6f636b656446756e6769626c657300010402000d09040004e02046756e6769626c6520617373657473207768696368207765206b6e6f7720617265206c6f636b6564206f6e207468697320636861696e2e5458636d457865637574696f6e53757370656e64656401003501040004b420476c6f62616c2073757370656e73696f6e207374617465206f66207468652058434d206578656375746f722e01810301c10500011909332843756d756c757358636d0001250401c90500011d093420446d7051756575650120446d7051756575651434436f6e66696775726174696f6e01002109280700e40b540202000400044c2054686520636f6e66696775726174696f6e2e2450616765496e646578010025094000000000000000000000000000000000044020546865207061676520696e6465782e1450616765730101040210290904000444205468652071756575652070616765732e284f766572776569676874000104022c2d090400046420546865206f766572776569676874206d657373616765732e50436f756e746572466f724f766572776569676874010010100000000004ac436f756e74657220666f72207468652072656c6174656420636f756e7465642073746f72616765206d617001290401cd05000131093524496e666c6174696f6e0124496e666c6174696f6e14645374617274696e6759656172546f74616c49737375616e636501001840000000000000000000000000000000000474207374617274696e67207965617220746f74616c2069737375616e636538426c6f636b496e666c6174696f6e01001840000000000000000000000000000000000401012043757272656e7420696e666c6174696f6e20666f722060496e666c6174696f6e426c6f636b496e74657276616c60206e756d626572206f6620626c6f636b73484e657874496e666c6174696f6e426c6f636b010010100000000004e4204e65787420746172676574202872656c61792920626c6f636b207768656e20696e666c6174696f6e2077696c6c206265206170706c696564584e657874526563616c63756c6174696f6e426c6f636b010010100000000004e4204e65787420746172676574202872656c61792920626c6f636b207768656e20696e666c6174696f6e20697320726563616c63756c61746564285374617274426c6f636b0100101000000000049c2052656c617920626c6f636b207768656e20696e666c6174696f6e206861732073746172746564012d04000458496e666c6174696f6e426c6f636b496e74657276616c101064000000043501204e756d626572206f6620626c6f636b7320746861742070617373206265747765656e2074726561737572792062616c616e636520757064617465732064756520746f20696e666c6174696f6e003c18556e697175650118556e697175652430436861696e56657273696f6e01002c2000000000000000000450205573656420666f72206d6967726174696f6e73404372656174654974656d4261736b657400010402350910040008cc2028436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c2077686f206372656174656420287265616c2929410120544f444f3a204f666620636861696e20776f726b65722073686f756c642072656d6f76652066726f6d2074686973206d6170207768656e20636f6c6c656374696f6e20676574732072656d6f766564444e66745472616e736665724261736b65740001080202390910040004d820436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c20746f6b656e2069642028636f6e74726f6c6c65643f32295846756e6769626c655472616e736665724261736b65740001080205350910040004c420436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c206f776e696e67207573657220287265616c2960526546756e6769626c655472616e736665724261736b657400010c0202053d0910040004d820436f6c6c656374696f6e2069642028636f6e74726f6c6c65643f32292c20746f6b656e2069642028636f6e74726f6c6c65643f32294c546f6b656e50726f70657274794261736b657400010802023909100400045901204c6173742073706f6e736f72696e67206f6620746f6b656e2070726f70657274792073657474696e67202f2f20746f646f3a646f63207265706872617365207468697320616e642074686520666f6c6c6f77696e67404e6674417070726f76654261736b65740001080202390910040004c0204c6173742073706f6e736f72696e67206f66204e465420617070726f76616c20696e206120636f6c6c656374696f6e5446756e6769626c65417070726f76654261736b65740001080205350910040004f0204c6173742073706f6e736f72696e67206f662066756e6769626c6520746f6b656e7320617070726f76616c20696e206120636f6c6c656374696f6e5c526566756e6769626c65417070726f76654261736b657400010c0202053d0910040004c0204c6173742073706f6e736f72696e67206f662052465420617070726f76616c20696e206120636f6c6c656374696f6e0131040034386e657374696e675f62756467657410100500000004fc2041206d6178696d756d206e756d626572206f66206c6576656c73206f6620646570746820696e2074686520746f6b656e206e657374696e6720747265652e686d61785f636f6c6c656374696f6e5f6e616d655f6c656e6774681010400000000494204d6178696d616c206c656e677468206f66206120636f6c6c656374696f6e206e616d652e846d61785f636f6c6c656374696f6e5f6465736372697074696f6e5f6c656e67746810100001000004b0204d6178696d616c206c656e677468206f66206120636f6c6c656374696f6e206465736372697074696f6e2e5c6d61785f746f6b656e5f7072656669785f6c656e6774681010100000000488204d6178696d616c206c656e677468206f66206120746f6b656e207072656669782e5c636f6c6c656374696f6e5f61646d696e735f6c696d6974101005000000047c204d6178696d756d2061646d696e732070657220636f6c6c656374696f6e2e5c6d61785f70726f70657274795f6b65795f6c656e6774681010000100000488204d6178696d616c206c656e677468206f6620612070726f7065727479206b65792e646d61785f70726f70657274795f76616c75655f6c656e6774681010008000000490204d6178696d616c206c656e677468206f6620612070726f70657274792076616c75652e5c6d61785f70726f706572746965735f7065725f6974656d10104000000004982041206d6178696d756d206e756d626572206f6620746f6b656e2070726f706572746965732e786d61785f636f6c6c656374696f6e5f70726f706572746965735f73697a65101000a0000004b0204d6178696d756d2073697a6520666f7220616c6c20636f6c6c656374696f6e2070726f706572746965732e646d61785f746f6b656e5f70726f706572746965735f73697a651010008000000498204d6178696d756d2073697a65206f6620616c6c20746f6b656e2070726f706572746965732e746e66745f64656661756c745f636f6c6c656374696f6e5f6c696d6974735d04840140420f000100080000010001ffffffff0105000000010500000001000101010104782044656661756c74204e465420636f6c6c656374696f6e206c696d69742e747266745f64656661756c745f636f6c6c656374696f6e5f6c696d6974735d04840140420f000100080000010001ffffffff0105000000010500000001000101010104782044656661756c742052465420636f6c6c656374696f6e206c696d69742e7066745f64656661756c745f636f6c6c656374696f6e5f6c696d6974735d04840140420f000100080000010001ffffffff0105000000010500000001000101010104742044656661756c7420465420636f6c6c656374696f6e206c696d69742e0141093d34436f6e66696775726174696f6e0134436f6e66696775726174696f6e1878576569676874546f466565436f656666696369656e744f7665727269646501002c20449004f701540801004c4d696e47617350726963654f7665727269646501002c20788dfa33b4010000008c41707050726f6d6f6d6f74696f6e436f6e66696775726174696f6e4f7665727269646501001505100000000000a4436f6c6c61746f7253656c656374696f6e44657369726564436f6c6c61746f72734f76657272696465010010100a0000000090436f6c6c61746f7253656c656374696f6e4c6963656e7365426f6e644f766572726964650100184000000040eaed7446d09c2c9f0c0000000098436f6c6c61746f7253656c656374696f6e4b69636b5468726573686f6c644f76657272696465010010102c01000000010d0501d105207444656661756c74576569676874546f466565436f656666696369656e742c20449004f701540801004844656661756c744d696e47617350726963652c20788dfa33b401000000584d617858636d416c6c6f7765644c6f636174696f6e73101010000000005441707050726f6d6f74696f6e4461696c79526174651d051020a10700003844617952656c6179426c6f636b73101040380000009044656661756c74436f6c6c61746f7253656c656374696f6e4d6178436f6c6c61746f727310100a000000008c44656661756c74436f6c6c61746f7253656c656374696f6e4c6963656e7365426f6e64184000000040eaed7446d09c2c9f0c000000009444656661756c74436f6c6c61746f7253656c656374696f6e4b69636b5468726573686f6c6410102c010000000145093f204368617267696e6700000000004018436f6d6d6f6e0118436f6d6d6f6e245843726561746564436f6c6c656374696f6e436f756e740100890410000000000469012053746f72616765206f662074686520636f756e74206f66206372656174656420636f6c6c656374696f6e732e20457373656e7469616c6c7920636f6e7461696e7320746865206c61737420636f6c6c656374696f6e2049442e6044657374726f796564436f6c6c656374696f6e436f756e7401008904100000000004b42053746f72616765206f662074686520636f756e74206f662064656c6574656420636f6c6c656374696f6e732e38436f6c6c656374696f6e427949640001040289044909040004702053746f72616765206f6620636f6c6c656374696f6e20696e666f2e50436f6c6c656374696f6e50726f7065727469657301010402890451092400000000000000000004882053746f72616765206f6620636f6c6c656374696f6e2070726f706572746965732e74436f6c6c656374696f6e50726f70657274795065726d697373696f6e730101040289046909040004dc2053746f72616765206f6620746f6b656e2070726f7065727479207065726d697373696f6e73206f66206120636f6c6c656374696f6e2e2c41646d696e416d6f756e7401010402890410100000000004b02053746f72616765206f662074686520616d6f756e74206f6620636f6c6c656374696f6e2061646d696e732e1c497341646d696e01010802027d0935010400046c204c697374206f6620636f6c6c656374696f6e2061646d696e732e24416c6c6f776c69737401010802027d0935010400047820416c6c6f776c697374656420636f6c6c656374696f6e2075736572732e4444756d6d7953746f7261676556616c7565000081090400040501204e6f74207573656420627920636f64652c20657869737473206f6e6c7920746f2070726f7669646520736f6d6520747970657320746f206d657461646174612e0001d5050c5c436f6c6c656374696f6e4372656174696f6e507269636518400000c84e676dc11b000000000000000004882053657420707269636520746f20637265617465206120636f6c6c656374696f6e2e3c436f6e7472616374416464726573736503506c4e9fe1ae37a41e93cee429e8e1881abdcbb54f041901204164647265737320756e6465722077686963682074686520436f6c6c656374696f6e48656c70657220636f6e747261637420776f756c6420626520617661696c61626c652e5c636f6c6c656374696f6e5f61646d696e735f6c696d6974101005000000047c204d6178696d756d2061646d696e732070657220636f6c6c656374696f6e2e01c509422046756e6769626c65012046756e6769626c650c2c546f74616c537570706c7901010405890418400000000000000000000000000000000004d420546f74616c20616d6f756e74206f662066756e6769626c6520746f6b656e7320696e73696465206120636f6c6c656374696f6e2e1c42616c616e636501010805027d0918400000000000000000000000000000000004e820416d6f756e74206f6620746f6b656e73206f776e656420627920616e206163636f756e7420696e73696465206120636f6c6c656374696f6e2e24416c6c6f77616e636501010c050002c9091840000000000000000000000000000000000405012053746f7261676520666f72206173736574732064656c65676174656420746f2061206c696d6974656420657874656e7420746f206f746865722075736572732e00000001cd094328526566756e6769626c650128526566756e6769626c652430546f6b656e734d696e74656401010405890410100000000004bc20546f74616c20616d6f756e74206f66206d696e74656420746f6b656e7320696e206120636f6c6c656374696f6e2e2c546f6b656e734275726e7401010405890410100000000004a020416d6f756e74206f6620746f6b656e73206275726e7420696e206120636f6c6c656374696f6e2e3c546f6b656e50726f7065727469657300010805053909d109040004cc20416d6f756e74206f6620706965636573206120726566756e6769626c6520746f6b656e2069732073706c697420696e746f2e2c546f74616c537570706c7901010805053909184000000000000000000000000000000000048420546f74616c20616d6f756e74206f662070696563657320666f7220746f6b656e144f776e656401010c050205d5093501040004ac205573656420746f20656e756d657261746520746f6b656e73206f776e6564206279206163636f756e742e384163636f756e7442616c616e636501010805027d0910100000000004450120416d6f756e74206f6620746f6b656e7320286e6f742070696563657329207061727469616c6c79206f776e656420627920616e206163636f756e742077697468696e206120636f6c6c656374696f6e2e1c42616c616e636501010c050502d90918400000000000000000000000000000000004a420416d6f756e74206f6620746f6b656e20706965636573206f776e6564206279206163636f756e742e24416c6c6f77616e636501011005050002dd0918400000000000000000000000000000000004e50120416c6c6f77616e636520736574206279206120746f6b656e206f776e657220666f7220616e6f74686572207573657220746f20706572666f726d206f6e65206f66206365727461696e207472616e73616374696f6e73206f6e2061206e756d626572206f6620706965636573206f66206120746f6b656e2e4c436f6c6c656374696f6e416c6c6f77616e636501010c050202c90935010400048d01205370656e6465722073657420627920612077616c6c6574206f776e6572207468617420636f756c6420706572666f726d206365727461696e207472616e73616374696f6e73206f6e20616c6c20746f6b656e7320696e207468652077616c6c65742e00000001e109442c4e6f6e66756e6769626c65012c4e6f6e66756e6769626c652830546f6b656e734d696e74656401010405890410100000000004bc20546f74616c20616d6f756e74206f66206d696e74656420746f6b656e7320696e206120636f6c6c656374696f6e2e2c546f6b656e734275726e7401010405890410100000000004a020416d6f756e74206f66206275726e7420746f6b656e7320696e206120636f6c6c656374696f6e2e24546f6b656e4461746100010805053909e509040004c020546f6b656e20646174612c207573656420746f207061727469616c6c79206465736372696265206120746f6b656e2e3c546f6b656e50726f7065727469657300010805053909d109040004f0204d6170206f66206b65792d76616c75652070616972732c2064657363726962696e6720746865206d65746164617461206f66206120746f6b656e2e48546f6b656e41757850726f7065727469657300011005050505e909f109040024d020437573746f6d2064617461206f66206120746f6b656e20746861742069732073657269616c697a656420746f2062797465732cb0207072696d6172696c7920726573657276656420666f72206f6e2d636861696e206f7065726174696f6e732cac206e6f726d616c6c79206f627363757265642066726f6d207468652065787465726e616c2075736572732e00c420417578696c696172792070726f706572746965732061726520736c696768746c7920646966666572656e742066726f6dd420757375616c205b60546f6b656e50726f70657274696573605d2064756520746f20616e20756e6c696d69746564206e756d626572d820616e642073657061726174656c792073746f72656420616e64207772697474656e2d746f206b65792d76616c75652070616972732e00482043757272656e746c7920756e757365642e144f776e656401010c050205d5093501040004ac205573656420746f20656e756d657261746520746f6b656e73206f776e6564206279206163636f756e742e34546f6b656e4368696c6472656e01010c050505f509350104000490205573656420746f20656e756d657261746520746f6b656e2773206368696c6472656e2e384163636f756e7442616c616e636501010805027d0910100000000004d820416d6f756e74206f6620746f6b656e73206f776e656420627920616e206163636f756e7420696e206120636f6c6c656374696f6e2e24416c6c6f77616e6365000108050539094d040400048d0120416c6c6f77616e636520736574206279206120746f6b656e206f776e657220666f7220616e6f74686572207573657220746f20706572666f726d206f6e65206f66206365727461696e207472616e73616374696f6e73206f6e206120746f6b656e2e4c436f6c6c656374696f6e416c6c6f77616e636501010c050202c90935010400049101204f70657261746f722073657420627920612077616c6c6574206f776e6572207468617420636f756c6420706572666f726d206365727461696e207472616e73616374696f6e73206f6e20616c6c20746f6b656e7320696e207468652077616c6c65742e00000001f90945245374727563747572650001290501d9050001fd09463041707050726f6d6f74696f6e013041707050726f6d6f74696f6e182c546f74616c5374616b6564010018400000000000000000000000000000000004802053746f7265732074686520746f74616c207374616b656420616d6f756e742e1441646d696e00000004000485012053746f72657320746865206061646d696e60206163636f756e742e20536f6d652065787472696e736963732063616e206f6e6c7920626520657865637574656420696620746865792077657265207369676e6564206279206061646d696e602e185374616b65640101080205010a050a5000000000000000000000000000000000000000001809012053746f7265732074686520616d6f756e74206f6620746f6b656e73207374616b6564206279206163636f756e7420696e2074686520626c6f636b6e756d6265722e0074202a202a2a4b6579312a2a202d205374616b6572206163636f756e742ee4202a202a2a4b6579322a2a202d2052656c617920626c6f636b206e756d626572207768656e20746865207374616b6520776173206d6164652ed4202a202a2a2842616c616e63652c20426c6f636b4e756d626572292a2a202d2042616c616e6365206f6620746865207374616b652e490120546865206e756d626572206f66207468652072656c617920626c6f636b20696e207768696368207765206d75737420706572666f726d2074686520696e74657265737420726563616c63756c6174696f6e405374616b65735065724163636f756e74010104020008040010c42053746f726573206e756d626572206f66207374616b65207265636f72647320666f7220616e20604163636f756e74602e0070202a202a2a4b65792a2a202d205374616b6572206163636f756e742e80202a202a2a56616c75652a2a202d20416d6f756e74206f66207374616b65732e3850656e64696e67556e7374616b650101040510090a040010a82050656e64696e6720756e7374616b65207265636f72647320666f7220616e20604163636f756e74602e0070202a202a2a4b65792a2a202d205374616b6572206163636f756e742e80202a202a2a56616c75652a2a202d20416d6f756e74206f66207374616b65732e6050726576696f757343616c63756c617465645265636f72640000010a0400082d012053746f7265732061206b657920666f72207265636f726420666f722077686963682074686520726576656e756520726563616c63756c6174696f6e2077617320706572666f726d65642eb90120496620604e6f6e65602c207468656e20726563616c63756c6174696f6e20686173206e6f7420796574206265656e20706572666f726d6564206f722063616c63756c6174696f6e732068617665206265656e20636f6d706c6574656420666f7220616c6c207374616b6572732e012d0501dd05182050616c6c657449644907206170707374616b65041901205468652061707027732070616c6c65742069642c207573656420666f72206465726976696e672069747320736f7665726569676e206163636f756e7420616464726573732e40467265657a654964656e7469666965720501406170707374616b656170707374616b65049420467265657a65206964656e7469666965722075736564206279207468652070616c6c657454526563616c63756c6174696f6e496e74657276616c101040380000044420496e2072656c617920626c6f636b732e3c50656e64696e67496e74657276616c1010e0c40000045420496e2070617261636861696e20626c6f636b732e38496e74657276616c496e636f6d651d051020a107000429012052617465206f662072657475726e20666f7220696e74657276616c20696e20626c6f636b7320646566696e656420696e2060526563616c63756c6174696f6e496e74657276616c602e1c4e6f6d696e616c1840000064a7b3b6e00d0000000000000000047420446563696d616c7320666f7220746865206043757272656e6379602e01110a4934466f726569676e4173736574730134466f726569676e41737365747314484e657874466f726569676e4173736574496401001010000000000c8c204e65787420617661696c61626c6520466f726569676e20417373657449642049442e008c204e657874466f726569676e417373657449643a20466f726569676e4173736574496454466f726569676e41737365744c6f636174696f6e730001040510d404000c84205468652073746f726167657320666f72204d756c74694c6f636174696f6e732e000d0120466f726569676e41737365744c6f636174696f6e733a206d617020466f726569676e41737365744964203d3e204f7074696f6e3c4d756c74694c6f636174696f6e3e544c6f636174696f6e546f43757272656e637949647300010405d41004000c78205468652073746f726167657320666f722043757272656e63794964732e000d01204c6f636174696f6e546f43757272656e63794964733a206d6170204d756c74694c6f636174696f6e203d3e204f7074696f6e3c466f726569676e417373657449643e3841737365744d6574616461746173000104050d01390504000c84205468652073746f726167657320666f722041737365744d65746164617461732e00d82041737365744d65746164617461733a206d6170204173736574496473203d3e204f7074696f6e3c41737365744d657461646174613e30417373657442696e64696e6700010405108904040008dc205468652073746f726167657320666f722061737365747320746f2066756e6769626c6520636f6c6c656374696f6e2062696e64696e670001350501e1050001150a500c45564d010c45564d10304163636f756e74436f64657301010402650334040000504163636f756e74436f6465734d65746164617461000104026503190a0400003c4163636f756e7453746f726167657301010802021d0a30800000000000000000000000000000000000000000000000000000000000000000002c43757272656e744c6f677301009d05040008a0205772697474656e206f6e206c6f672c207265736574206166746572207472616e73616374696f6e942053686f756c6420626520656d707479206265747765656e207472616e73616374696f6e7301450501e5050001210a6420457468657265756d0120457468657265756d181c50656e64696e670100250a040004d02043757272656e74206275696c64696e6720626c6f636b2773207472616e73616374696f6e7320616e642072656365697074732e3043757272656e74426c6f636b0000450a04000470205468652063757272656e7420457468657265756d20626c6f636b2e3c43757272656e7452656365697074730000590a0400047c205468652063757272656e7420457468657265756d2072656365697074732e6843757272656e745472616e73616374696f6e537461747573657300005d0a04000488205468652063757272656e74207472616e73616374696f6e2073746174757365732e24426c6f636b48617368010104054905308000000000000000000000000000000000000000000000000000000000000000000034496e6a65637465644e6f6e63650100490580000000000000000000000000000000000000000000000000000000000000000004190120496e6a6563746564207472616e73616374696f6e732073686f756c64206861766520756e69717565206e6f6e63652c20686572652077652073746f72652063757272656e7401610501e9050001610a654445766d436f6465725375627374726174650000000001650a964845766d436f6e747261637448656c70657273014845766d436f6e747261637448656c7065727324144f776e6572010104036503650350000000000000000000000000000000000000000010682053746f7265206f776e657220666f7220636f6e74726163742e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732e88202a202a2a56616c75652a2a202d206f776e657220666f7220636f6e74726163742e3853656c6653706f6e736f72696e6701010403650335010400049c20446570726563617465643a20746869732073746f7261676520697320646570726563617465642853706f6e736f72696e67010104056503690a040010982053746f726520666f7220636f6e74726163742073706f6e736f72736869702073746174652e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732e84202a202a2a56616c75652a2a202d2073706f6e736f72736869702073746174652e3853706f6e736f72696e674d6f64650001040365036d0a04001c6c2053746f726520666f722073706f6e736f72696e67206d6f64652e00282023232320557361676591012050726566657220746f2064656c65746520636f6c6c656374696f6e2066726f6d2073746f72616765206966206d6f64652063686167656420746f205b6044697361626c6564605d2853706f6e736f72696e674d6f6465543a3a44697361626c6564292e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732ed0202a202a2a56616c75652a2a202d205b6073706f6e736f72696e67206d6f6465605d2853706f6e736f72696e674d6f646554292e4c53706f6e736f72696e67526174654c696d69740101040365031010201c000010b42053746f7261676520666f722073706f6e736f72696e672072617465206c696d697420696e20626c6f636b732e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732ea8202a202a2a56616c75652a2a202d20616d6f756e74206f662073706f6e736f72656420626c6f636b732e4853706f6e736f72696e674665654c696d6974010104036503710a040014882053746f7261676520666f72206c6173742073706f6e736f72656420626c6f636b2e007c202a202a2a4b6579312a2a202d20636f6e747261637420616464726573732e94202a202a2a4b6579322a2a202d2073706f6e736f726564207573657220616464726573732eac202a202a2a56616c75652a2a202d206c6173742073706f6e736f72656420626c6f636b206e756d6265722e3453706f6e736f724261736b65740001080303810a1004000040416c6c6f776c697374456e61626c6564010104036503350104001c69012053746f7265676520666f7220636f6e7472616374732077697468205b60416c6c6f776c6973746564605d2853706f6e736f72696e674d6f6465543a3a416c6c6f776c6973746564292073706f6e736f72696e67206d6f64652e002820232323205573616765bd012050726566657220746f2064656c65746520636f6c6c656374696f6e2066726f6d2073746f72616765206966206d6f64652063686167656420746f206e6f6e2060416c6c6f776c6973746564602c207468616e20736574202a2a56616c75652a2a20746f202a2a66616c73652a2a2e0078202a202a2a4b65792a2a202d20636f6e747261637420616464726573732e4501202a202a2a56616c75652a2a202d20697320636f6e747261637420696e205b60416c6c6f776c6973746564605d2853706f6e736f72696e674d6f6465543a3a416c6c6f776c697374656429206d6f64652e24416c6c6f776c6973740101080303810a3501040020c02053746f7261676520666f72207573657273207468617420616c6c6f77656420666f722073706f6e736f72736869702e00282023232320557361676539012050726566657220746f2064656c657465207265636f72642066726f6d2073746f726167652069662075736572206e6f206d6f726520616c6c6f77656420666f722073706f6e736f72736869702e007c202a202a2a4b6579312a2a202d20636f6e747261637420616464726573732ec0202a202a2a4b6579322a2a202d2075736572207468617420616c6c6f77656420666f722073706f6e736f72736869702ea4202a202a2a56616c75652a2a202d20616c6c6f77616e636520666f722073706f6e736f72736869702e018905010d06043c436f6e747261637441646472657373650350842899ecf380553e8a4de75bf534cdf6fbf6404904d820416464726573732c20756e646572207768696368206d6167696320636f6e74726163742077696c6c20626520617661696c61626c6501850a975445766d5472616e73616374696f6e5061796d656e740000000000983045766d4d6967726174696f6e013045766d4d6967726174696f6e04404d6967726174696f6e50656e64696e6701010405650335010400000191050111060001890a992c4d61696e74656e616e6365012c4d61696e74656e616e6365041c456e61626c65640100350104000001a50501150600018d0a9a3c42616c616e6365734164617074657200000000009b1c5574696c6974790001a905011906044c626174636865645f63616c6c735f6c696d69741010aa2a000004a820546865206c696d6974206f6e20746865206e756d626572206f6620626174636865642063616c6c732e01910a9c24546573745574696c730124546573745574696c73081c456e61626c656401003501040000245465737456616c756501001010000000000001b105011d060001950aff990a042840436865636b5370656356657273696f6eb90a1038436865636b547856657273696f6ebd0a1030436865636b47656e65736973c10a3038436865636b4d6f7274616c697479c50a3028436865636b4e6f6e6365cd0aac2c436865636b576569676874d10aac40436865636b4d61696e74656e616e6365d50aac5044697361626c654964656e7469747943616c6c73d90aac604368617267655472616e73616374696f6e5061796d656e74dd0aac6046616b655472616e73616374696f6e46696e616c697a6572e50aace10a","id":"1"}
\ No newline at end of file
deletedjs-packages/scripts/src/proposefasttrack.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/proposefasttrack.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import {ApiPromise, WsProvider} from '@polkadot/api';
-import {blake2AsHex} from '@polkadot/util-crypto';
-
-async function main() {
-  const networkUrl = process.argv[2];
-
-  const wsProvider = new WsProvider(networkUrl);
-  const api = await ApiPromise.create({provider: wsProvider});
-
-  const externalDemocracyProposal = (await api.query.democracy.nextExternal() as any).unwrap()[0];
-
-  let proposalHash;
-  if(externalDemocracyProposal.isInline) {
-    proposalHash = blake2AsHex(externalDemocracyProposal.asInline, 256);
-  } else if(externalDemocracyProposal.isLegacy) {
-    proposalHash = externalDemocracyProposal.asLegacy.toJSON().hash;
-  } else {
-    proposalHash = externalDemocracyProposal.asLookup.toJSON().hash;
-  }
-
-  const voringPeriod = 7200;
-  const delay = 10;
-
-  const democracyFastTrack = api.tx.democracy.fastTrack(proposalHash, voringPeriod, delay);
-
-  const techCommThreshold = ((await api.query.technicalCommittee.members()).toJSON() as any[]).length;
-  const techCommProposal = api.tx.technicalCommittee.propose(
-    techCommThreshold,
-    democracyFastTrack.method.toHex(),
-    democracyFastTrack.encodedLength,
-  );
-
-  const encodedCall = techCommProposal.method.toHex();
-
-  console.log('-----------------');
-  console.log('Fast Track Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`);
-  console.log('-----------------');
-
-  await api.disconnect();
-}
-
-await main();
deletedjs-packages/scripts/src/proposeupgrade.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/proposeupgrade.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import {ApiPromise, WsProvider} from '@polkadot/api';
-import {blake2AsHex} from '@polkadot/util-crypto';
-import {readFileSync} from 'fs';
-
-async function main() {
-  const networkUrl = process.argv[2];
-  const wasmFile = process.argv[3];
-
-  const wsProvider = new WsProvider(networkUrl);
-  const api = await ApiPromise.create({provider: wsProvider});
-
-  const wasmFileBytes = readFileSync(wasmFile);
-  const wasmFileHash = blake2AsHex(wasmFileBytes, 256);
-
-  const authorizeUpgrade = api.tx.parachainSystem.authorizeUpgrade(wasmFileHash, true);
-  const enableMaintenance = api.tx.maintenance.enable();
-
-  const councilMembers = (await api.query.council.members()).toJSON() as any[];
-  const councilProposalThreshold = Math.floor(councilMembers.length / 2) + 1;
-
-  const democracyProposalContent = api.tx.utility.batchAll([
-    authorizeUpgrade.method.toHex(),
-    enableMaintenance.method.toHex(),
-  ]).method.toHex();
-
-  const democracyProposalHash = blake2AsHex(democracyProposalContent, 256);
-  const democracyProposalPreimage = api.tx.preimage.notePreimage(democracyProposalContent).method.toHex();
-
-  const democracyProposal = api.tx.democracy.externalProposeDefault({
-    Legacy: democracyProposalHash,
-  });
-
-  const councilProposal = api.tx.council.propose(
-    councilProposalThreshold,
-    democracyProposal,
-    democracyProposal.method.encodedLength,
-  ).method.toHex();
-
-  const proposeUpgradeBatch = api.tx.utility.batchAll([
-    democracyProposalPreimage,
-    councilProposal,
-  ]);
-
-  const encodedCall = proposeUpgradeBatch.method.toHex();
-
-  console.log('-----------------');
-  console.log('Upgrade Proposal: ', `https://polkadot.js.org/apps/?rpc=${networkUrl}#/extrinsics/decode/${encodedCall}`);
-  console.log('-----------------');
-
-  await api.disconnect();
-}
-
-await main();
deletedjs-packages/scripts/src/transfer.nload.tsdiffbeforeafterboth
--- a/js-packages/scripts/src/transfer.nload.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-/* eslint-disable @typescript-eslint/no-floating-promises */
-import os from 'os';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds} from '@unique/tests/src/util/index.js';
-import {UniqueHelper} from '@unique/playgrounds/src/unique.js';
-import * as notReallyCluster from 'cluster'; // https://github.com/nodejs/node/issues/42271#issuecomment-1063415346
-const cluster = notReallyCluster as unknown as notReallyCluster.Cluster;
-
-async function findUnusedAddress(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, seedAddition = ''): Promise<IKeyringPair> {
-  let bal = 0n;
-  let unused;
-  do {
-    const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;
-    unused = await privateKey(`//${randomSeed}`);
-    bal = await helper.balance.getSubstrate(unused.address);
-  } while(bal !== 0n);
-  return unused;
-}
-
-function findUnusedAddresses(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, amount: number): Promise<IKeyringPair[]> {
-  return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(helper, privateKey, '_' + Date.now())));
-}
-
-// Innacurate transfer fee
-const FEE = 10n ** 8n;
-
-let counters: { [key: string]: number } = {};
-function increaseCounter(name: string, amount: number) {
-  if(!counters[name]) {
-    counters[name] = 0;
-  }
-  counters[name] += amount;
-}
-function flushCounterToMaster() {
-  if(Object.keys(counters).length === 0) {
-    return;
-  }
-    process.send!(counters);
-    counters = {};
-}
-
-async function distributeBalance(source: IKeyringPair, helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, totalAmount: bigint, stages: number) {
-  const accounts = [source];
-  // we don't need source in output array
-  const failedAccounts = [0];
-
-  const finalUserAmount = 2 ** stages - 1;
-  accounts.push(...await findUnusedAddresses(helper, privateKey, finalUserAmount));
-  // findUnusedAddresses produces at least 1 request per user
-  increaseCounter('requests', finalUserAmount);
-
-  for(let stage = 0; stage < stages; stage++) {
-    const usersWithBalance = 2 ** stage;
-    const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage);
-    // console.log(`Stage ${stage}/${stages}, ${usersWithBalance} => ${usersWithBalance * 2} = ${amount}`);
-    const txs: Promise<boolean | void>[] = [];
-    for(let i = 0; i < usersWithBalance; i++) {
-      const newUser = accounts[i + usersWithBalance];
-      // console.log(`${accounts[i].address} => ${newUser.address} = ${amountToSplit}`);
-      const tx = helper.balance.transferToSubstrate(accounts[i], newUser.address, amount);
-      txs.push(tx.catch(() => {
-        failedAccounts.push(i + usersWithBalance);
-        increaseCounter('txFailed', 1);
-      }));
-      increaseCounter('tx', 1);
-    }
-    await Promise.all(txs);
-  }
-
-  for(const account of failedAccounts.reverse()) {
-    accounts.splice(account, 1);
-  }
-  return accounts;
-}
-
-if(cluster.isMaster) {
-  let testDone = false;
-  usingPlaygrounds(async (helper) => {
-    const prevCounters: { [key: string]: number } = {};
-    while(!testDone) {
-      for(const name in counters) {
-        if(!(name in prevCounters)) {
-          prevCounters[name] = 0;
-        }
-        if(counters[name] === prevCounters[name]) {
-          continue;
-        }
-        console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`);
-        prevCounters[name] = counters[name];
-      }
-      await helper.wait.newBlocks(1);
-    }
-  });
-  const waiting: Promise<void>[] = [];
-  console.log(`Starting ${os.cpus().length} workers`);
-  usingPlaygrounds(async (helper, privateKey) => {
-    const alice = await privateKey('//Alice');
-    for(const id in os.cpus()) {
-      const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;
-      const workerAccount = await privateKey(WORKER_NAME);
-      await helper.balance.transferToSubstrate(alice, workerAccount.address, 400n * 10n ** 23n);
-
-      const worker = cluster.fork({
-        WORKER_NAME,
-        STAGES: id + 2,
-      });
-      worker.on('message', msg => {
-        for(const key in msg) {
-          increaseCounter(key, msg[key]);
-        }
-      });
-      waiting.push(new Promise(res => worker.on('exit', res)));
-    }
-    await Promise.all(waiting);
-    testDone = true;
-  });
-} else {
-  increaseCounter('startedWorkers', 1);
-  usingPlaygrounds(async (helper, privateKey) => {
-    await distributeBalance(await privateKey(process.env.WORKER_NAME as string), helper, privateKey, 400n * 10n ** 22n, 10);
-  });
-  const interval = setInterval(() => {
-    flushCounterToMaster();
-  }, 100);
-  interval.unref();
-}
addedjs-packages/scripts/transfer.nload.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/scripts/transfer.nload.ts
@@ -0,0 +1,142 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+/* 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 * as notReallyCluster from 'cluster'; // https://github.com/nodejs/node/issues/42271#issuecomment-1063415346
+const cluster = notReallyCluster as unknown as notReallyCluster.Cluster;
+
+async function findUnusedAddress(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, seedAddition = ''): Promise<IKeyringPair> {
+  let bal = 0n;
+  let unused;
+  do {
+    const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;
+    unused = await privateKey(`//${randomSeed}`);
+    bal = await helper.balance.getSubstrate(unused.address);
+  } while(bal !== 0n);
+  return unused;
+}
+
+function findUnusedAddresses(helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, amount: number): Promise<IKeyringPair[]> {
+  return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(helper, privateKey, '_' + Date.now())));
+}
+
+// Innacurate transfer fee
+const FEE = 10n ** 8n;
+
+let counters: { [key: string]: number } = {};
+function increaseCounter(name: string, amount: number) {
+  if(!counters[name]) {
+    counters[name] = 0;
+  }
+  counters[name] += amount;
+}
+function flushCounterToMaster() {
+  if(Object.keys(counters).length === 0) {
+    return;
+  }
+    process.send!(counters);
+    counters = {};
+}
+
+async function distributeBalance(source: IKeyringPair, helper: UniqueHelper, privateKey: (account: string) => Promise<IKeyringPair>, totalAmount: bigint, stages: number) {
+  const accounts = [source];
+  // we don't need source in output array
+  const failedAccounts = [0];
+
+  const finalUserAmount = 2 ** stages - 1;
+  accounts.push(...await findUnusedAddresses(helper, privateKey, finalUserAmount));
+  // findUnusedAddresses produces at least 1 request per user
+  increaseCounter('requests', finalUserAmount);
+
+  for(let stage = 0; stage < stages; stage++) {
+    const usersWithBalance = 2 ** stage;
+    const amount = totalAmount / (2n ** BigInt(stage)) - FEE * BigInt(stage);
+    // console.log(`Stage ${stage}/${stages}, ${usersWithBalance} => ${usersWithBalance * 2} = ${amount}`);
+    const txs: Promise<boolean | void>[] = [];
+    for(let i = 0; i < usersWithBalance; i++) {
+      const newUser = accounts[i + usersWithBalance];
+      // console.log(`${accounts[i].address} => ${newUser.address} = ${amountToSplit}`);
+      const tx = helper.balance.transferToSubstrate(accounts[i], newUser.address, amount);
+      txs.push(tx.catch(() => {
+        failedAccounts.push(i + usersWithBalance);
+        increaseCounter('txFailed', 1);
+      }));
+      increaseCounter('tx', 1);
+    }
+    await Promise.all(txs);
+  }
+
+  for(const account of failedAccounts.reverse()) {
+    accounts.splice(account, 1);
+  }
+  return accounts;
+}
+
+if(cluster.isMaster) {
+  let testDone = false;
+  usingPlaygrounds(async (helper) => {
+    const prevCounters: { [key: string]: number } = {};
+    while(!testDone) {
+      for(const name in counters) {
+        if(!(name in prevCounters)) {
+          prevCounters[name] = 0;
+        }
+        if(counters[name] === prevCounters[name]) {
+          continue;
+        }
+        console.log(`${name.padEnd(15)} = ${counters[name] - prevCounters[name]}`);
+        prevCounters[name] = counters[name];
+      }
+      await helper.wait.newBlocks(1);
+    }
+  });
+  const waiting: Promise<void>[] = [];
+  console.log(`Starting ${os.cpus().length} workers`);
+  usingPlaygrounds(async (helper, privateKey) => {
+    const alice = await privateKey('//Alice');
+    for(const id in os.cpus()) {
+      const WORKER_NAME = `//LoadWorker${id}_${Date.now()}`;
+      const workerAccount = await privateKey(WORKER_NAME);
+      await helper.balance.transferToSubstrate(alice, workerAccount.address, 400n * 10n ** 23n);
+
+      const worker = cluster.fork({
+        WORKER_NAME,
+        STAGES: id + 2,
+      });
+      worker.on('message', msg => {
+        for(const key in msg) {
+          increaseCounter(key, msg[key]);
+        }
+      });
+      waiting.push(new Promise(res => worker.on('exit', res)));
+    }
+    await Promise.all(waiting);
+    testDone = true;
+  });
+} else {
+  increaseCounter('startedWorkers', 1);
+  usingPlaygrounds(async (helper, privateKey) => {
+    await distributeBalance(await privateKey(process.env.WORKER_NAME as string), helper, privateKey, 400n * 10n ** 22n, 10);
+  });
+  const interval = setInterval(() => {
+    flushCounterToMaster();
+  }, 100);
+  interval.unref();
+}
deletedjs-packages/scripts/tsconfig.jsondiffbeforeafterboth
--- a/js-packages/scripts/tsconfig.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "extends": "../tsconfig.packages.json",
-  "compilerOptions": {
-    "rootDir": "./src",
-    "outDir": "dist",
-    "resolveJsonModule": true
-  },
-  "references": [
-    { "path": "../types/tsconfig.json" },
-    { "path": "../playgrounds/tsconfig.json" },
-    { "path": "../tests/tsconfig.json" }
-  ]
-}
\ No newline at end of file
addedjs-packages/tests/addCollectionAdmin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/addCollectionAdmin.test.ts
@@ -0,0 +1,120 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+
+describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
+  let donor: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itSub('Add collection admin.', async ({helper}) => {
+    const [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
+    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
+
+    const collection = await helper.collection.getData(collectionId);
+    expect(collection!.normalizedOwner!).to.be.equal(helper.address.normalizeSubstrate(alice.address));
+
+    await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});
+
+    const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId);
+    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
+  });
+});
+
+describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
+  let donor: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itSub("Not owner can't add collection admin.", async ({helper}) => {
+    const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
+
+    const collection = await helper.collection.getData(collectionId);
+    expect(collection?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
+
+    await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/);
+    await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.NoPermission/);
+
+    const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId);
+    expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: charlie.address});
+    expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: bob.address});
+  });
+
+  itSub("Admin can't add collection admin.", async ({helper}) => {
+    const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
+
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    const adminListAfterAddAdmin = await collection.getAdmins();
+    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
+
+    await expect(collection.addAdmin(bob, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/);
+
+    const adminListAfterAddNewAdmin = await collection.getAdmins();
+    expect(adminListAfterAddNewAdmin).to.be.deep.contains({Substrate: bob.address});
+    expect(adminListAfterAddNewAdmin).to.be.not.deep.contains({Substrate: charlie.address});
+  });
+
+  itSub("Can't add collection admin of not existing collection.", async ({helper}) => {
+    const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    const collectionId = NON_EXISTENT_COLLECTION_ID;
+
+    await expect(helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/);
+
+    // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+    await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
+  });
+
+  itSub("Can't add an admin to a destroyed collection.", async ({helper}) => {
+    const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
+
+    await collection.burn(alice);
+    await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/);
+
+    // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
+    await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
+  });
+
+  itSub('Add an admin to a collection that has reached the maximum number of admins limit', async ({helper}) => {
+    const [alice, ...accounts] = await helper.arrange.createAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor);
+    const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
+
+    const chainAdminLimit = (helper.getApi().consts.common.collectionAdminsLimit as any).toNumber();
+    expect(chainAdminLimit).to.be.equal(5);
+
+    for(let i = 0; i < chainAdminLimit; i++) {
+      await collection.addAdmin(alice, {Substrate: accounts[i].address});
+      const adminListAfterAddAdmin = await collection.getAdmins();
+      expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: accounts[i].address});
+    }
+
+    await expect(collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address})).to.be.rejectedWith(/common\.CollectionAdminCountExceeded/);
+  });
+});
addedjs-packages/tests/adminTransferAndBurn.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/adminTransferAndBurn.test.ts
@@ -0,0 +1,61 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, itSub} from './util/index.js';
+
+describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('admin transfers other user\'s token', async ({helper}) => {
+    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
+    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
+    const limits = await helper.collection.getEffectiveLimits(collectionId);
+    expect(limits.ownerCanTransfer).to.be.true;
+
+    const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
+    await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})).to.be.rejected;
+
+    await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address});
+    const newTokenOwner = await helper.nft.getTokenOwner(collectionId, tokenId);
+    expect(newTokenOwner).to.be.deep.equal({Substrate: charlie.address});
+  });
+
+  itSub('admin burns other user\'s token', async ({helper}) => {
+    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
+
+    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
+    const limits = await helper.collection.getEffectiveLimits(collectionId);
+    expect(limits.ownerCanTransfer).to.be.true;
+
+    const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
+
+    await expect(helper.nft.burnToken(alice, collectionId, tokenId)).to.be.rejected;
+
+    await helper.nft.burnToken(bob, collectionId, tokenId);
+    const token = await helper.nft.getToken(collectionId, tokenId);
+    expect(token).to.be.null;
+  });
+});
addedjs-packages/tests/allowLists.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/allowLists.test.ts
@@ -0,0 +1,361 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+describe('Integration Test ext. Allow list tests', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor);
+    });
+  });
+
+  describe('Positive', () => {
+    itSub('Owner can add address to allow list', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      // allow list does not need to be enabled to add someone in advance
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
+      const allowList = await helper.nft.getAllowList(collectionId);
+      expect(allowList).to.deep.contain({Substrate: bob.address});
+    });
+
+    itSub('Admin can add address to allow list', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});
+
+      // allow list does not need to be enabled to add someone in advance
+      await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});
+      const allowList = await helper.nft.getAllowList(collectionId);
+      expect(allowList).to.deep.contain({Substrate: charlie.address});
+    });
+
+    itSub('If address is already added to allow list, nothing happens', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
+      const allowList = await helper.nft.getAllowList(collectionId);
+      expect(allowList).to.deep.contain({Substrate: bob.address});
+    });
+  });
+
+  describe('Negative', () => {
+    itSub('Nobody can add address to allow list of non-existing collection', async ({helper}) => {
+      const collectionId = NON_EXISTENT_COLLECTION_ID;
+      await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.CollectionNotFound/);
+    });
+
+    itSub('Nobody can add address to allow list of destroyed collection', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.collection.burn(alice, collectionId);
+      await expect(helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}))
+        .to.be.rejectedWith(/common\.CollectionNotFound/);
+    });
+
+    itSub('Non-privileged user cannot add address to allow list', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.NoPermission/);
+    });
+  });
+});
+
+describe('Integration Test ext. Remove from Allow List', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor);
+    });
+  });
+
+  describe('Positive', () => {
+    itSub('Owner can remove address from allow list', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
+
+      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});
+
+      const allowList = await helper.nft.getAllowList(collectionId);
+
+      expect(allowList).to.not.deep.contain({Substrate: bob.address});
+    });
+
+    itSub('Admin can remove address from allow list', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
+      await helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: bob.address});
+
+      const allowList = await helper.nft.getAllowList(collectionId);
+      expect(allowList).to.not.deep.contain({Substrate: bob.address});
+    });
+
+    itSub('If address is already removed from allow list, nothing happens', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
+      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});
+      const allowListBefore = await helper.nft.getAllowList(collectionId);
+      expect(allowListBefore).to.not.deep.contain({Substrate: bob.address});
+
+      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});
+
+      const allowListAfter = await helper.nft.getAllowList(collectionId);
+      expect(allowListAfter).to.not.deep.contain({Substrate: bob.address});
+    });
+  });
+
+  describe('Negative', () => {
+    itSub('Non-privileged user cannot remove address from allow list', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+      await expect(helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.NoPermission/);
+
+      const allowList = await helper.nft.getAllowList(collectionId);
+      expect(allowList).to.deep.contain({Substrate: charlie.address});
+    });
+
+    itSub('Nobody can remove address from allow list of non-existing collection', async ({helper}) => {
+      const collectionId = NON_EXISTENT_COLLECTION_ID;
+      await expect(helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.CollectionNotFound/);
+    });
+
+    itSub('Nobody can remove address from allow list of deleted collection', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
+      await helper.collection.burn(alice, collectionId);
+
+      await expect(helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}))
+        .to.be.rejectedWith(/common\.CollectionNotFound/);
+    });
+  });
+});
+
+describe('Integration Test ext. Transfer if included in Allow List', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor);
+    });
+  });
+
+  describe('Positive', () => {
+    itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
+      expect(owner).to.be.deep.equal({Substrate: charlie.address});
+    });
+
+    itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+
+      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});
+      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
+      expect(owner).to.be.deep.equal({Substrate: charlie.address});
+    });
+
+    itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+
+      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
+      expect(owner).to.be.deep.equal({Substrate: charlie.address});
+    });
+
+    itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+
+      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});
+      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
+      expect(owner).to.be.deep.equal({Substrate: charlie.address});
+    });
+  });
+
+  describe('Negative', () => {
+    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+
+      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+    });
+
+    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address});
+
+      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+    });
+
+    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
+
+      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+    });
+
+    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
+      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
+
+      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address});
+
+      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
+        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+    });
+
+    itSub('If Public Access mode is set to AllowList, tokens can\'t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await expect(helper.nft.burnToken(bob, collectionId, tokenId))
+        .to.be.rejectedWith(/common\.NoPermission/);
+    });
+
+    itSub('If Public Access mode is set to AllowList, token transfers can\'t be Approved by a non-allowlisted address (see Approve method)', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
+      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
+      await expect(helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}))
+        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+    });
+  });
+});
+
+describe('Integration Test ext. Mint if included in Allow List', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  const permissionSet: ICollectionPermissions[] = [
+    {access: 'Normal', mintMode: false},
+    {access: 'Normal', mintMode: true},
+    {access: 'AllowList', mintMode: false},
+    {access: 'AllowList', mintMode: true},
+  ];
+
+  const testPermissionSuite = (permissions: ICollectionPermissions) => {
+    const allowlistedMintingShouldFail = !permissions.mintMode!;
+
+    const appropriateRejectionMessage = permissions.mintMode! ? /common\.AddressNotInAllowlist/ : /common\.PublicMintingNotAllowed/;
+
+    const allowlistedMintingTest = () => itSub(
+      `With the condtions above, tokens can${allowlistedMintingShouldFail ? '\'t' : ''} be created by allow-listed addresses`,
+      async ({helper}) => {
+        const collection = await helper.nft.mintCollection(alice, {});
+        await collection.setPermissions(alice, permissions);
+        await collection.addToAllowList(alice, {Substrate: bob.address});
+
+        if(allowlistedMintingShouldFail)
+          await expect(collection.mintToken(bob, {Substrate: bob.address})).to.be.rejectedWith(appropriateRejectionMessage);
+        else
+          await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected;
+      },
+    );
+
+
+    describe(`Public Access Mode = ${permissions.access}, Mint Mode = ${permissions.mintMode}`, () => {
+      describe('Positive', () => {
+        itSub('With the condtions above, tokens can be created by owner', async ({helper}) => {
+          const collection = await helper.nft.mintCollection(alice, {});
+          await collection.setPermissions(alice, permissions);
+          await expect(collection.mintToken(alice, {Substrate: alice.address})).to.not.be.rejected;
+        });
+
+        itSub('With the condtions above, tokens can be created by admin', async ({helper}) => {
+          const collection = await helper.nft.mintCollection(alice, {});
+          await collection.setPermissions(alice, permissions);
+          await collection.addAdmin(alice, {Substrate: bob.address});
+          await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected;
+        });
+
+        if(!allowlistedMintingShouldFail) allowlistedMintingTest();
+      });
+
+      describe('Negative', () => {
+        itSub('With the condtions above, tokens can\'t be created by non-priviliged non-allow-listed address', async ({helper}) => {
+          const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+          await collection.setPermissions(alice, permissions);
+          await expect(collection.mintToken(bob, {Substrate: bob.address}))
+            .to.be.rejectedWith(appropriateRejectionMessage);
+        });
+
+        if(allowlistedMintingShouldFail) allowlistedMintingTest();
+      });
+    });
+  };
+
+  for(const permissions of permissionSet) {
+    testPermissionSuite(permissions);
+  }
+});
addedjs-packages/tests/apiConsts.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/apiConsts.test.ts
@@ -0,0 +1,116 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {ApiPromise} from '@polkadot/api';
+import {usingPlaygrounds, itSub, expect, COLLECTION_HELPER, CONTRACT_HELPER} from './util/index.js';
+
+
+const MAX_COLLECTION_DESCRIPTION_LENGTH = 256n;
+const MAX_COLLECTION_NAME_LENGTH = 64n;
+const COLLECTION_ADMINS_LIMIT = 5n;
+const MAX_COLLECTION_PROPERTIES_SIZE = 40960n;
+const MAX_TOKEN_PREFIX_LENGTH = 16n;
+const MAX_PROPERTY_KEY_LENGTH = 256n;
+const MAX_PROPERTY_VALUE_LENGTH = 32768n;
+const MAX_PROPERTIES_PER_ITEM = 64n;
+const MAX_TOKEN_PROPERTIES_SIZE = 32768n;
+const NESTING_BUDGET = 5n;
+
+const DEFAULT_COLLETCTION_LIMIT = {
+  accountTokenOwnershipLimit: '1,000,000',
+  sponsoredDataSize: '2,048',
+  sponsoredDataRateLimit: 'SponsoringDisabled',
+  tokenLimit: '4,294,967,295',
+  sponsorTransferTimeout: '5',
+  sponsorApproveTimeout: '5',
+  ownerCanTransfer: false,
+  ownerCanDestroy: true,
+  transfersEnabled: true,
+};
+
+describe('integration test: API UNIQUE consts', () => {
+  let api: ApiPromise;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper) => {
+      api = await helper.getApi();
+    });
+  });
+
+  itSub('DEFAULT_NFT_COLLECTION_LIMITS', () => {
+    expect(api.consts.unique.nftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+  });
+
+  itSub('DEFAULT_RFT_COLLECTION_LIMITS', () => {
+    expect(api.consts.unique.rftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+  });
+
+  itSub('DEFAULT_FT_COLLECTION_LIMITS', () => {
+    expect(api.consts.unique.ftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+  });
+
+  itSub('MAX_COLLECTION_NAME_LENGTH', () => {
+    checkConst(api.consts.unique.maxCollectionNameLength, MAX_COLLECTION_NAME_LENGTH);
+  });
+
+  itSub('MAX_COLLECTION_DESCRIPTION_LENGTH', () => {
+    checkConst(api.consts.unique.maxCollectionDescriptionLength, MAX_COLLECTION_DESCRIPTION_LENGTH);
+  });
+
+  itSub('MAX_COLLECTION_PROPERTIES_SIZE', () => {
+    checkConst(api.consts.unique.maxCollectionPropertiesSize, MAX_COLLECTION_PROPERTIES_SIZE);
+  });
+
+  itSub('MAX_TOKEN_PREFIX_LENGTH', () => {
+    checkConst(api.consts.unique.maxTokenPrefixLength, MAX_TOKEN_PREFIX_LENGTH);
+  });
+
+  itSub('MAX_PROPERTY_KEY_LENGTH', () => {
+    checkConst(api.consts.unique.maxPropertyKeyLength, MAX_PROPERTY_KEY_LENGTH);
+  });
+
+  itSub('MAX_PROPERTY_VALUE_LENGTH', () => {
+    checkConst(api.consts.unique.maxPropertyValueLength, MAX_PROPERTY_VALUE_LENGTH);
+  });
+
+  itSub('MAX_PROPERTIES_PER_ITEM', () => {
+    checkConst(api.consts.unique.maxPropertiesPerItem, MAX_PROPERTIES_PER_ITEM);
+  });
+
+  itSub('NESTING_BUDGET', () => {
+    checkConst(api.consts.unique.nestingBudget, NESTING_BUDGET);
+  });
+
+  itSub('MAX_TOKEN_PROPERTIES_SIZE', () => {
+    checkConst(api.consts.unique.maxTokenPropertiesSize, MAX_TOKEN_PROPERTIES_SIZE);
+  });
+
+  itSub('COLLECTION_ADMINS_LIMIT', () => {
+    checkConst(api.consts.unique.collectionAdminsLimit, COLLECTION_ADMINS_LIMIT);
+  });
+
+  itSub('HELPERS_CONTRACT_ADDRESS', () => {
+    expect(api.consts.evmContractHelpers.contractAddress.toString().toLowerCase()).to.be.equal(CONTRACT_HELPER.toLowerCase());
+  });
+
+  itSub('EVM_COLLECTION_HELPERS_ADDRESS', () => {
+    expect(api.consts.common.contractAddress.toString().toLowerCase()).to.be.equal(COLLECTION_HELPER.toLowerCase());
+  });
+});
+
+function checkConst<T>(constValue: any, expectedValue: T) {
+  expect(constValue.toBigInt()).equal(expectedValue);
+}
\ No newline at end of file
addedjs-packages/tests/approve.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/approve.test.ts
@@ -0,0 +1,658 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js';
+import {CrossAccountId} from '@unique/playgrounds/unique.js';
+
+
+
+[
+  {method: 'approveToken', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toICrossAccountId()},
+  {method: 'approveTokenFromEth', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toEthereum().toICrossAccountId()},
+].map(testCase => {
+  describe(`Integration Test ${testCase.method}(spender, collection_id, item_id, amount):`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('[nft] Execute the extrinsic and check approvedList', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
+      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true;
+    });
+
+    itSub('[fungible] Execute the extrinsic and check approvedList', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
+      expect(amount).to.be.equal(1n);
+    });
+
+    itSub.ifWithPallets('[refungible] Execute the extrinsic and check approvedList', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
+      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
+      expect(amount).to.be.equal(1n);
+    });
+
+    itSub('[nft] Remove approval by using 0 amount', async ({helper}) => {
+      const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const collectionId = collection.collectionId;
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
+      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true;
+      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
+      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false;
+    });
+
+    itSub('[fungible] Remove approval by using 0 amount', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
+      expect(amountBefore).to.be.equal(1n);
+
+      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
+      const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
+      expect(amountAfter).to.be.equal(0n);
+    });
+
+    itSub.ifWithPallets('[refungible] Remove approval by using 0 amount', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
+      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
+      expect(amountBefore).to.be.equal(1n);
+
+      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
+      const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
+      expect(amountAfter).to.be.equal(0n);
+    });
+
+    itSub('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
+      const result = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address});
+      await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+  });
+
+  describe(`[${testCase.method}] Normal user can approve other users to transfer:`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('NFT', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
+      await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.true;
+    });
+
+    itSub('Fungible up to an approved amount', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob));
+      expect(amount).to.be.equal(1n);
+    });
+
+    itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n});
+      await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n);
+      const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob));
+      expect(amount).to.be.equal(100n);
+    });
+  });
+
+  describe(`[${testCase.method}] Approved users can transferFrom up to approved amount:`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('NFT', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
+      await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address});
+      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
+      expect(owner).to.be.deep.equal({Substrate: alice.address});
+    });
+
+    itSub('Fungible up to an approved amount', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
+      await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
+      const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
+      expect(after - before).to.be.equal(1n);
+    });
+
+    itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n});
+      await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
+      await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
+      const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
+      expect(after - before).to.be.equal(1n);
+    });
+  });
+
+  describe(`[${testCase.method}] Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('NFT', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
+      await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address});
+      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
+      expect(owner).to.be.deep.equal({Substrate: alice.address});
+      const transferTokenFromTx = () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address});
+      await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow');
+    });
+
+    itSub('Fungible up to an approved amount', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
+      await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
+      const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
+      expect(after - before).to.be.equal(1n);
+
+      const transferTokenFromTx = () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
+      await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow');
+    });
+
+    itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n});
+      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n);
+      const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
+      await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n);
+      const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
+      expect(after - before).to.be.equal(100n);
+      const transferTokenFromTx = () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n);
+      await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow');
+    });
+  });
+
+  describe(`[${testCase.method}] Approved amount decreases by the transferred amount:`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+    let dave: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 10n);
+
+      const charlieBefore = await helper.ft.getBalance(collectionId, {Substrate: charlie.address});
+      await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: charlie.address}, 2n);
+      const charlieAfter = await helper.ft.getBalance(collectionId, {Substrate: charlie.address});
+      expect(charlieAfter - charlieBefore).to.be.equal(2n);
+
+      const daveBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
+      await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: dave.address}, 8n);
+      const daveAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
+      expect(daveAfter - daveBefore).to.be.equal(8n);
+    });
+  });
+
+  describe(`[${testCase.method}] User may clear the approvals to approving for 0 amount:`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('NFT', async ({helper}) => {
+      const owner = testCase.account(alice);
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: owner});
+
+      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true;
+
+      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
+      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false;
+
+      const transferTokenFromTx = helper.nft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: bob.address});
+      await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow');
+    });
+
+    itSub('Fungible', async ({helper}) => {
+      const owner = testCase.account(alice);
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, owner);
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
+      expect(amountBefore).to.be.equal(1n);
+
+      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
+      const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
+      expect(amountAfter).to.be.equal(0n);
+
+      const transferTokenFromTx = helper.ft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n);
+      await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow');
+    });
+
+    itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => {
+      const owner = testCase.account(alice);
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner, pieces: 100n});
+      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
+      const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
+      expect(amountBefore).to.be.equal(1n);
+
+      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
+      const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
+      expect(amountAfter).to.be.equal(0n);
+
+      const transferTokenFromTx = helper.rft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n);
+      await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow');
+    });
+  });
+
+  describe(`[${testCase.method}] User cannot approve for the amount greater than they own:`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('1 for NFT', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
+      const result = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 2n);
+      await expect(result).to.be.rejectedWith('nonfungible.NonfungibleItemsHaveNoAmount');
+      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false;
+    });
+
+    itSub('Fungible', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      const result = (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 11n);
+      await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+
+    itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
+      const result = (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 101n);
+      await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+  });
+
+  describe(`[${testCase.method}] Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('cannot be called by collection admin on non-owned item', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
+      await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
+      const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+  });
+
+  describe(`[${testCase.method}] Negative Integration Test approve(spender, collection_id, item_id, amount):`, () => {
+    let alice: IKeyringPair;
+    let bob: IKeyringPair;
+    let charlie: IKeyringPair;
+    const NONEXISTENT_COLLECTION = (2 ** 32) - 1;
+
+    before(async () => {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        const donor = await privateKey({url: import.meta.url});
+        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+      });
+    });
+
+    itSub('[nft] Approve for a collection that does not exist', async ({helper}) => {
+      const approveTx = (helper.nft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address});
+      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
+    });
+
+    itSub('[fungible] Approve for a collection that does not exist', async ({helper}) => {
+      const approveTx = (helper.ft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address});
+      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
+    });
+
+    itSub.ifWithPallets('[refungible] Approve for a collection that does not exist', [Pallets.ReFungible], async ({helper}) => {
+      const approveTx = (helper.rft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address});
+      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
+    });
+
+    itSub('[nft] Approve for a collection that was destroyed', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.nft.burn(alice, collectionId);
+      const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address});
+      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
+    });
+
+    itSub('[fungible] Approve for a collection that was destroyed', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.ft.burn(alice, collectionId);
+      const approveTx = (helper.ft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address});
+      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
+    });
+
+    itSub.ifWithPallets('[refungible] Approve for a collection that was destroyed', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.rft.burn(alice, collectionId);
+      const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address});
+      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
+    });
+
+    itSub('[nft] Approve transfer of a token that does not exist', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address});
+      await expect(approveTx).to.be.rejectedWith('common.TokenNotFound');
+    });
+
+    itSub.ifWithPallets('[refungible] Approve transfer of a token that does not exist', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address});
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); // TODO: why the error is not common.TokenNotFound
+    });
+
+    itSub('[nft] Approve using the address that does not own the approved token', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
+      const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address});
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+
+    itSub('[fungible] Approve using the address that does not own the approved token', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+      const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address});
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+
+    itSub.ifWithPallets('[refungible] Approve using the address that does not own the approved token', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
+      const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address});
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+
+    itSub.ifWithPallets('should fail if approved more ReFungibles than owned', [Pallets.ReFungible], async ({helper}) => {
+      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n});
+      await helper.rft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 100n);
+      await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 100n);
+
+      const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 101n);
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+
+    itSub('should fail if approved more Fungibles than owned', async ({helper}) => {
+      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      await helper.ft.mintTokens(alice, collectionId, 10n, alice.address);
+      const tokenId = await helper.ft.getLastTokenId(collectionId);
+
+      await helper.ft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 10n);
+      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 10n);
+      const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 11n);
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+
+    itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => {
+      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
+      await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: false});
+
+      const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address});
+      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
+    });
+  });
+});
+
+describe('Normal user can approve other users to be wallet operator:', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('[nft] Enable and disable approval', async ({helper}) => {
+    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+
+    const checkBeforeApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+    expect(checkBeforeApproval).to.be.false;
+
+    await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true);
+    const checkAfterApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+    expect(checkAfterApproval).to.be.true;
+
+    await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false);
+    const checkAfterDisapproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+    expect(checkAfterDisapproval).to.be.false;
+  });
+
+  itSub.ifWithPallets('[rft] Enable and disable approval', [Pallets.ReFungible], async ({helper}) => {
+    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+
+    const checkBeforeApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+    expect(checkBeforeApproval).to.be.false;
+
+    await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true);
+    const checkAfterApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+    expect(checkAfterApproval).to.be.true;
+
+    await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false);
+    const checkAfterDisapproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
+    expect(checkAfterDisapproval).to.be.false;
+  });
+});
+
+describe('Administrator and collection owner do not need approval in order to execute TransferFrom (with owner_can_transfer_flag = true):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+  let dave: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub('NFT', async ({helper}) => {
+    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
+    const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: charlie.address});
+
+    await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address});
+    const owner1 = await helper.nft.getTokenOwner(collectionId, tokenId);
+    expect(owner1).to.be.deep.equal({Substrate: dave.address});
+
+    await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
+    await helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address});
+    const owner2 = await helper.nft.getTokenOwner(collectionId, tokenId);
+    expect(owner2).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub('Fungible up to an approved amount', async ({helper}) => {
+    const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
+    await helper.ft.mintTokens(alice, collectionId, 10n, charlie.address);
+    const tokenId = await helper.ft.getLastTokenId(collectionId);
+
+    const daveBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
+    await helper.ft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n);
+    const daveBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
+    expect(daveBalanceAfter - daveBalanceBefore).to.be.equal(1n);
+
+    await helper.collection.addAdmin(alice ,collectionId, {Substrate: bob.address});
+
+    const aliceBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
+    await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n);
+    const aliceBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
+    expect(aliceBalanceAfter - aliceBalanceBefore).to.be.equal(1n);
+  });
+
+  itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
+    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
+    const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: charlie.address, pieces: 100n});
+
+    const daveBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address});
+    await helper.rft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n);
+    const daveAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address});
+    expect(daveAfter - daveBefore).to.be.equal(1n);
+
+    await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
+
+    const aliceBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
+    await helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n);
+    const aliceAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
+    expect(aliceAfter - aliceBefore).to.be.equal(1n);
+  });
+});
+
+describe('Repeated approvals add up', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+  let dave: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {});
+    await collection.mint(alice, 10n);
+    await collection.approveTokens(alice, {Substrate: bob.address}, 1n);
+    await collection.approveTokens(alice, {Substrate: charlie.address}, 1n);
+    // const allowances1 = await getAllowance(collectionId, 0, Alice.address, Bob.address);
+    // const allowances2 = await getAllowance(collectionId, 0, Alice.address, Charlie.address);
+    // expect(allowances1 + allowances2).to.be.eq(2n);
+  });
+
+  itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice, 10n);
+    await token.approve(alice, {Substrate: bob.address}, 1n);
+    await token.approve(alice, {Substrate: charlie.address}, 1n);
+    // const allowances1 = await getAllowance(collectionId, itemId, Alice.address, Bob.address);
+    // const allowances2 = await getAllowance(collectionId, itemId, Alice.address, Charlie.address);
+    // expect(allowances1 + allowances2).to.be.eq(2n);
+  });
+
+  // Canceled by changing approve logic
+  itSub.skip('Cannot approve for more than total user\'s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {});
+    await collection.mint(alice, 10n, {Substrate: dave.address});
+    await collection.approveTokens(dave, {Substrate: bob.address}, 5n);
+    await expect(collection.approveTokens(dave, {Substrate: charlie.address}, 6n))
+      .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received');
+  });
+
+  // Canceled by changing approve logic
+  itSub.skip('Cannot approve for more than total user\'s amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice, 100n, {Substrate: dave.address});
+    await token.approve(dave, {Substrate: bob.address}, 50n);
+    await expect(token.approve(dave, {Substrate: charlie.address}, 51n))
+      .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received');
+  });
+});
addedjs-packages/tests/burnItem.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/burnItem.test.ts
@@ -0,0 +1,165 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from './util/index.js';
+
+
+describe('integration test: ext. burnItem():', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  itSub('Burn item in NFT collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    const token = await collection.mintToken(alice);
+
+    await token.burn(alice);
+    expect(await token.doesExist()).to.be.false;
+  });
+
+  itSub('Burn item in Fungible collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {}, 10);
+    await collection.mint(alice, 10n);
+
+    await collection.burnTokens(alice, 1n);
+    expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n);
+  });
+});
+
+describe('integration test: ext. burnItem() with admin permissions:', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Burn item in NFT collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    const token = await collection.mintToken(alice);
+
+    await token.burnFrom(bob, {Substrate: alice.address});
+    expect(await token.doesExist()).to.be.false;
+  });
+
+  itSub('Burn item in Fungible collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {}, 0);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await collection.mint(alice, 10n);
+
+    await collection.burnTokensFrom(bob, {Substrate: alice.address}, 1n);
+    expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n);
+  });
+});
+
+describe('Negative integration test: ext. burnItem():', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Burn a token that was never created', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    await expect(collection.burnToken(alice, 10)).to.be.rejectedWith('common.TokenNotFound');
+  });
+
+  itSub('Burn a token using the address that does not own it', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    const token = await collection.mintToken(alice);
+
+    await expect(token.burn(bob)).to.be.rejectedWith('common.NoPermission');
+  });
+
+  itSub('Transfer a burned token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    const token = await collection.mintToken(alice);
+    await token.burn(alice);
+
+    await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith('common.TokenNotFound');
+  });
+
+  itSub('Burn more than owned in Fungible collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {}, 0);
+    await collection.mint(alice, 10n);
+
+    await expect(collection.burnTokens(alice, 11n)).to.be.rejectedWith('common.TokenValueTooLow');
+    expect(await collection.getBalance({Substrate: alice.address})).to.eq(10n);
+  });
+
+  itSub('Zero burn NFT', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Coll', description: 'Desc', tokenPrefix: 'T'});
+    const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address});
+    const tokenBob = await collection.mintToken(alice, {Substrate: bob.address});
+
+    // 1. Zero burn of own tokens allowed:
+    await helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenAlice.tokenId, 0]);
+    // 2. Zero burn of non-owned tokens not allowed:
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission');
+    // 3. Zero burn of non-existing tokens not allowed:
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, 9999, 0])).to.be.rejectedWith('common.TokenNotFound');
+    expect(await tokenAlice.doesExist()).to.be.true;
+    expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address});
+    expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address});
+    // 4. Storage is not corrupted:
+    await tokenAlice.transfer(alice, {Substrate: bob.address});
+    await tokenBob.transfer(bob, {Substrate: alice.address});
+    expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address});
+    expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address});
+  });
+
+  itSub('zero burnFrom NFT', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'});
+    const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address});
+    const approvedNft = await collection.mintToken(alice, {Substrate: bob.address});
+    await approvedNft.approve(bob, {Substrate: alice.address});
+
+    // 1. Zero burnFrom of non-existing tokens not allowed:
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
+    // 2. Zero burnFrom of not approved tokens not allowed:
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
+    // 3. Zero burnFrom of approved tokens allowed:
+    await helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, approvedNft.tokenId, 0]);
+
+    // 4.1 approvedNft still approved:
+    expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true;
+    // 4.2 bob is still the owner:
+    expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address});
+    expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address});
+    // 4.3 Alice can burn approved nft:
+    await approvedNft.burnFrom(alice, {Substrate: bob.address});
+    expect(await approvedNft.doesExist()).to.be.false;
+  });
+});
addedjs-packages/tests/change-collection-owner.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/change-collection-owner.test.ts
@@ -0,0 +1,170 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, itSub} from './util/index.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+
+describe('Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Changing owner changes owner address', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const beforeChanging = await helper.collection.getData(collection.collectionId);
+    expect(beforeChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
+
+    await collection.changeOwner(alice, bob.address);
+    const afterChanging = await helper.collection.getData(collection.collectionId);
+    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
+  });
+});
+
+describe('Integration Test changeCollectionOwner(collection_id, new_owner) special checks for exOwner:', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub('Changing the owner of the collection is not allowed for the former owner', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+
+    await collection.changeOwner(alice, bob.address);
+
+    const changeOwnerTx = () => collection.changeOwner(alice, alice.address);
+    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
+
+    const afterChanging = await helper.collection.getData(collection.collectionId);
+    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
+  });
+
+  itSub('New collectionOwner has access to sponsorship management operations in the collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.changeOwner(alice, bob.address);
+
+    const afterChanging = await helper.collection.getData(collection.collectionId);
+    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
+
+    await collection.setSponsor(bob, charlie.address);
+    await collection.confirmSponsorship(charlie);
+    await collection.removeSponsor(bob);
+    const limits = {
+      accountTokenOwnershipLimit: 1,
+      tokenLimit: 1,
+      sponsorTransferTimeout: 1,
+      ownerCanDestroy: true,
+      ownerCanTransfer: true,
+    };
+
+    await collection.setLimits(bob, limits);
+    const gotLimits = await collection.getEffectiveLimits();
+    expect(gotLimits).to.be.deep.contains(limits);
+
+    await collection.setPermissions(bob, {access: 'AllowList', mintMode: true});
+
+    await collection.burn(bob);
+    const collectionData = await helper.collection.getData(collection.collectionId);
+    expect(collectionData).to.be.null;
+  });
+
+  itSub('New collectionOwner has access to changeCollectionOwner', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.changeOwner(alice, bob.address);
+    await collection.changeOwner(bob, charlie.address);
+    const collectionData = await collection.getData();
+    expect(collectionData?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(charlie.address));
+  });
+});
+
+describe('Negative Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub('Not owner can\'t change owner.', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const changeOwnerTx = () => collection.changeOwner(bob, bob.address);
+    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Collection admin can\'t change owner.', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    const changeOwnerTx = () => collection.changeOwner(bob, bob.address);
+    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Can\'t change owner of a non-existing collection.', async ({helper}) => {
+    const collectionId = NON_EXISTENT_COLLECTION_ID;
+    const changeOwnerTx = () => helper.collection.changeOwner(bob, collectionId, bob.address);
+    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('Former collectionOwner not allowed to sponsorship management operations in the collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.changeOwner(alice, bob.address);
+
+    const changeOwnerTx = () => collection.changeOwner(alice, alice.address);
+    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
+
+    const afterChanging = await helper.collection.getData(collection.collectionId);
+    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
+
+    const setSponsorTx = () => collection.setSponsor(alice, charlie.address);
+    const confirmSponsorshipTx = () => collection.confirmSponsorship(alice);
+    const removeSponsorTx = () => collection.removeSponsor(alice);
+    await expect(setSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
+    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
+    await expect(removeSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
+
+    const limits = {
+      accountTokenOwnershipLimit: 1,
+      tokenLimit: 1,
+      sponsorTransferTimeout: 1,
+      ownerCanDestroy: true,
+      ownerCanTransfer: true,
+    };
+
+    const setLimitsTx = () => collection.setLimits(alice, limits);
+    await expect(setLimitsTx()).to.be.rejectedWith(/common\.NoPermission/);
+
+    const setPermissionTx = () => collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
+    await expect(setPermissionTx()).to.be.rejectedWith(/common\.NoPermission/);
+
+    const burnTx = () => collection.burn(alice);
+    await expect(burnTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+});
addedjs-packages/tests/check-event/burnItemEvent.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/check-event/burnItemEvent.test.ts
@@ -0,0 +1,44 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+
+describe('Burn Item event ', () => {
+  let alice: IKeyringPair;
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+  itSub('Check event from burnItem(): ', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    await token.burn(alice);
+    await helper.wait.newBlocks(1);
+
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contains('common.ItemDestroyed');
+    expect(eventStrings).to.contains('treasury.Deposit');
+    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
+  });
+});
addedjs-packages/tests/check-event/createCollectionEvent.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/check-event/createCollectionEvent.test.ts
@@ -0,0 +1,40 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+describe('Create collection event ', () => {
+  let alice: IKeyringPair;
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+  itSub('Check event from createCollection(): ', async ({helper}) => {
+    await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    await helper.wait.newBlocks(1);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contains('common.CollectionCreated');
+    expect(eventStrings).to.contains('treasury.Deposit');
+    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
+  });
+});
addedjs-packages/tests/check-event/createItemEvent.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/check-event/createItemEvent.test.ts
@@ -0,0 +1,41 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+describe('Create Item event ', () => {
+  let alice: IKeyringPair;
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+  itSub('Check event from createItem(): ', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    await collection.mintToken(alice, {Substrate: alice.address});
+    await helper.wait.newBlocks(1);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contains('common.ItemCreated');
+    expect(eventStrings).to.contains('treasury.Deposit');
+    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
+  });
+});
addedjs-packages/tests/check-event/createMultipleItemsEvent.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/check-event/createMultipleItemsEvent.test.ts
@@ -0,0 +1,46 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+describe('Create Multiple Items Event event ', () => {
+  let alice: IKeyringPair;
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+  itSub('Check event from createMultipleItems(): ', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    await collection.mintMultipleTokens(alice, [
+      {owner: {Substrate: alice.address}},
+      {owner: {Substrate: alice.address}},
+    ]);
+
+    await helper.wait.newBlocks(1);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contains('common.ItemCreated');
+    expect(eventStrings).to.contains('treasury.Deposit');
+    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
+  });
+});
addedjs-packages/tests/check-event/destroyCollectionEvent.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/check-event/destroyCollectionEvent.test.ts
@@ -0,0 +1,42 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+describe('Destroy collection event ', () => {
+  let alice: IKeyringPair;
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itSub('Check event from destroyCollection(): ', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    await collection.burn(alice);
+    await helper.wait.newBlocks(1);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contains('common.CollectionDestroyed');
+    expect(eventStrings).to.contains('treasury.Deposit');
+    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
+  });
+});
addedjs-packages/tests/check-event/transferEvent.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/check-event/transferEvent.test.ts
@@ -0,0 +1,45 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+describe('Transfer event ', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
+    });
+  });
+
+  itSub('Check event from transfer(): ', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    await token.transfer(alice, {Substrate: bob.address});
+    await helper.wait.newBlocks(1);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contains('common.Transfer');
+    expect(eventStrings).to.contains('treasury.Deposit');
+    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
+  });
+});
addedjs-packages/tests/check-event/transferFromEvent.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/check-event/transferFromEvent.test.ts
@@ -0,0 +1,44 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+describe('Transfer event ', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
+    });
+  });
+
+  itSub('Check event from transfer(): ', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    await token.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address});
+    await helper.wait.newBlocks(1);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contains('common.Transfer');
+    expect(eventStrings).to.contains('treasury.Deposit');
+    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
+  });
+});
addedjs-packages/tests/collator-selection/collatorSelection.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/collator-selection/collatorSelection.seqtest.ts
@@ -0,0 +1,423 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js';
+
+async function nodeAddress(name: string) {
+  // eslint-disable-next-line require-await
+  return await usingPlaygrounds(async (helper) => {
+    const envNodeStash = `RELAY_UNIQUE_NODE_${name.toUpperCase()}_STASH`;
+
+    const nodeStash = process.env[envNodeStash];
+    if(nodeStash) {
+      return helper.address.normalizeSubstrateToChainFormat(nodeStash);
+    } else {
+      throw Error(`"${envNodeStash}" env var is not set`);
+    }
+  });
+}
+
+async function getInitialInvulnerables() {
+  return await Promise.all([
+    nodeAddress('alpha'),
+    nodeAddress('beta'),
+    nodeAddress('gamma'),
+    nodeAddress('delta'),
+  ]);
+}
+
+async function resetInvulnerables() {
+  await usingPlaygrounds(async (helper, privateKey) => {
+    const superuser = await privateKey('//Alice');
+    const initialInvulnerables = await getInitialInvulnerables();
+
+    const invulnerables = await helper.collatorSelection.getInvulnerables();
+
+    // Remove all invulnerables but the first one
+    const firstInvulnerable = invulnerables[0];
+
+    let nonce = await helper.chain.getNonce(superuser.address);
+    await Promise.all(invulnerables.slice(1).map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++})));
+
+    // Add the initial invulnerables
+    await Promise.all(initialInvulnerables.map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [invulnerable], true, {nonce: nonce++})));
+
+    // Remove the first invulnerable if it's not an initial one
+    if(!initialInvulnerables.includes(firstInvulnerable)) {
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [firstInvulnerable]);
+    }
+  });
+}
+
+// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).
+describe('Integration Test: Collator Selection', () => {
+  let superuser: IKeyringPair;
+  let previousLicenseBond = 0n;
+  let licenseBond = 0n;
+
+  before(async function() {
+    if(!process.env.RUN_COLLATOR_TESTS) this.skip();
+
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);
+      superuser = await privateKey('//Alice');
+
+      previousLicenseBond = await helper.collatorSelection.getLicenseBond();
+      licenseBond = 10n * helper.balance.getOneTokenNominal();
+      await helper.getSudo().collatorSelection.setLicenseBond(superuser, licenseBond);
+    });
+  });
+
+  describe('Dynamic shuffling of collators', () => {
+    // These two are the default invulnerables, and should return to be invulnerables after this suite.
+    let alphaNode: string;
+    let betaNode: string;
+
+    let gammaNode: string;
+    let deltaNode: string;
+
+    before(async function() {
+      await usingPlaygrounds(async (helper) => {
+        // todo:collator see again if blocks start to be finalized in dev mode
+        // Skip the collator block production in dev mode, since the blocks are sealed automatically.
+        if(await helper.arrange.isDevNode()) this.skip();
+
+        [alphaNode, betaNode, gammaNode, deltaNode] = await getInitialInvulnerables();
+
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(invulnerables.length, 'Invalid initial invulnerables number').to.be.equal(4);
+        expect(invulnerables, 'Invalid initial invulnerables').containSubset([alphaNode, betaNode, gammaNode, deltaNode]);
+      });
+    });
+
+    itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
+      let nonce = await helper.chain.getNonce(superuser.address);
+
+      nonce = await helper.chain.getNonce(superuser.address);
+      await expect(Promise.all([
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alphaNode], true, {nonce: nonce++}),
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [betaNode], true, {nonce: nonce++}),
+      ])).to.be.fulfilled;
+
+      const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+      expect(newInvulnerables).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
+
+      await helper.wait.newSessions(2);
+
+      const newValidators = await helper.callRpc('api.query.session.validators');
+      expect(newValidators).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
+
+      const lastBlockNumber = await helper.chain.getLatestBlockNumber();
+      await helper.wait.newBlocks(1);
+      const lastGammaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [gammaNode])).toNumber();
+      const lastDeltaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [deltaNode])).toNumber();
+      expect(lastGammaBlock >= lastBlockNumber || lastDeltaBlock >= lastBlockNumber).to.be.true;
+    });
+
+    after(async () => {
+      await resetInvulnerables();
+    });
+  });
+
+  describe('Getting and releasing licenses to collate', () => {
+    let crowd: IKeyringPair[];
+
+    before(async function() {
+      await usingPlaygrounds(async (helper) => {
+        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
+
+        // set session keys for everyone
+        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
+      });
+    });
+
+    describe('Positive', () => {
+      itSub('Can lease and release a license', async ({helper}) => {
+        const account = crowd.pop()!;
+
+        // make sure it does not have any reserved funds
+        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(0n);
+
+        // getting a license reserves a license bond cost
+        await helper.collatorSelection.obtainLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
+        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(licenseBond);
+
+        // releasing a license un-reserves the license bond cost
+        await helper.collatorSelection.releaseLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
+
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(0n);
+        expect(balance.free > 100n - licenseBond);
+      });
+
+      itSub('Can force revoke a license', async ({helper}) => {
+        const account = crowd.pop()!;
+
+        // getting a license reserves a license bond cost
+        const previousBalance = await helper.balance.getSubstrateFull(account.address);
+        await helper.collatorSelection.obtainLicense(account);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
+
+        // force-releasing a license un-reserves the license bond cost as well
+        await helper.getSudo().collatorSelection.forceReleaseLicense(superuser, account.address);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(previousBalance.reserved);
+
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(previousBalance.reserved);
+        expect(balance.free > previousBalance.free - licenseBond);
+      });
+    });
+
+    describe('Negative', () => {
+      itSub('Cannot get a license without session keys set', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([100n], superuser);
+        await expect(helper.collatorSelection.obtainLicense(account))
+          .to.be.rejectedWith(/collatorSelection.ValidatorNotRegistered/);
+      });
+
+      itSub('Cannot register a license twice', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.obtainLicense(account))
+          .to.be.rejectedWith(/collatorSelection.AlreadyHoldingLicense/);
+      });
+
+      itSub('Cannot release a license twice', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.releaseLicense(account);
+        await expect(helper.collatorSelection.releaseLicense(account))
+          .to.be.rejectedWith(/collatorSelection.NoLicense/);
+      });
+
+      itSub('Cannot force revoke a license as non-sudo', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.forceReleaseLicense(superuser, account.address))
+          .to.be.rejectedWith(/BadOrigin/);
+      });
+    });
+  });
+
+  describe('Onboarding, collating, and offboarding as collator candidates', () => {
+    let crowd: IKeyringPair[];
+
+    before(async function() {
+      await usingPlaygrounds(async (helper) => {
+        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
+
+        // set session keys for everyone
+        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
+      });
+    });
+
+    describe('Positive', () => {
+      itSub('Can onboard and offboard repeatedly', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
+
+        await helper.collatorSelection.offboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
+
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
+
+        await helper.collatorSelection.offboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
+      });
+
+      itSub('Penalizes and forfeits license from faulty collators', async ({helper}) => {
+        // This one shouldn't even be able to produce blocks.
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        expect(await helper.collatorSelection.getCandidates()).to.contain(account.address);
+
+        // Wait for 3 new sessions before checking that the collator will be kicked:
+        // one to get collator onboarded, and another two for the collator to fail
+        await helper.wait.newSessions(3);
+
+        expect(await helper.collatorSelection.getCandidates()).to.not.contain(account.address);
+        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
+
+        // The account's reserved funds get slashed as a penalty
+        const balance = await helper.balance.getSubstrateFull(account.address);
+        expect(balance.reserved).to.be.equal(0n);
+        expect(balance.free < 100n - licenseBond);
+      });
+    });
+
+    describe('Negative', () => {
+      itSub('Cannot onboard without a license', async ({helper}) => {
+        const account = crowd.pop()!;
+        await expect(helper.collatorSelection.onboard(account))
+          .to.be.rejectedWith(/collatorSelection.NoLicense/);
+      });
+
+      itSub('Cannot offboard without a license', async ({helper}) => {
+        const account = crowd.pop()!;
+        await expect(helper.collatorSelection.offboard(account))
+          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
+      });
+
+      itSub('Cannot offboard while not onboarded', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await expect(helper.collatorSelection.offboard(account))
+          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
+      });
+
+      itSub('Cannot onboard while already onboarded', async ({helper}) => {
+        const account = crowd.pop()!;
+        await helper.collatorSelection.obtainLicense(account);
+        await helper.collatorSelection.onboard(account);
+        await expect(helper.collatorSelection.onboard(account))
+          .to.be.rejectedWith(/collatorSelection.AlreadyCandidate/);
+      });
+    });
+  });
+
+  describe('Addition and removal of invulnerables', () => {
+    describe('Positive', () => {
+      itSub('Adds an invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([10n], superuser);
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+
+        await helper.session.setOwnKeysFromAddress(account);
+        await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);
+
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables);
+      });
+
+      itSub('Removes an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        const lastInvulnerable = invulnerables.pop()!;
+
+        await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        // invulnerables had its last element removed, so they should be equal
+        expect(newInvulnerables).to.have.all.members(invulnerables);
+      });
+    });
+
+    describe('Negative', () => {
+      itSub('Does not duplicate an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        // adding an already invulnerable should not fail, but should not duplicate it either
+        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0]))
+          .to.be.fulfilled;
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.have.all.members(invulnerables);
+      });
+
+      itSub('Cannot remove a non-existent invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([0n], superuser);
+        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, account.address))
+          .to.be.rejectedWith(/collatorSelection.NotInvulnerable/);
+      });
+
+      itSub('Cannot allow invulnerables to be empty', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        const lastInvulnerable = invulnerables.pop()!;
+
+        let nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(invulnerables.map((i: any) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));
+
+        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))
+          .to.be.rejectedWith(/collatorSelection.TooFewInvulnerables/);
+
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);
+
+        // restore the invulnerables to the previous state
+        nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(invulnerables.map((i: any) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++})));
+      });
+
+      itSub('Cannot have too many invulnerables', async ({helper}) => {
+        // todo:collator make sure that there is enough session time for a set of tests
+        // 28 non-functioning collators, teehee.
+
+        const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
+        const invulnerablesUntilLimit = (await helper.collatorSelection.getDesiredCollators()) - invulnerablesLength;
+        const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
+        const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
+
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
+          helper.session.setOwnKeysFromAddress(i)));
+        await helper.session.setOwnKeysFromAddress(lastInvulnerable);
+
+        let nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));
+
+        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))
+          .to.be.rejectedWith(/collatorSelection.TooManyInvulnerables/);
+
+        // restore the invulnerables to the previous state
+        nonce = await helper.chain.getNonce(superuser.address);
+        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
+          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++})));
+      });
+
+      itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => {
+        const [account] = await helper.arrange.createAccounts([10n], superuser);
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+
+        await helper.session.setOwnKeysFromAddress(account);
+        await expect(helper.collatorSelection.addInvulnerable(superuser, account.address))
+          .to.be.rejectedWith(/BadOrigin/);
+
+        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
+        expect(newInvulnerables).to.be.members(invulnerables);
+      });
+
+      itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => {
+        const invulnerables = await helper.collatorSelection.getInvulnerables();
+        await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0]))
+          .to.be.rejectedWith(/BadOrigin/);
+        expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
+      });
+
+      after(async function() {
+        await resetInvulnerables();
+      });
+    });
+  });
+
+  after(async function() {
+    if(!process.env.RUN_COLLATOR_TESTS) return;
+
+    await usingPlaygrounds(async (helper) => {
+      if(helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;
+
+      await helper.getSudo().collatorSelection.setLicenseBond(superuser, previousLicenseBond);
+
+      const candidates = await helper.collatorSelection.getCandidates();
+      let nonce = await helper.chain.getNonce(superuser.address);
+      await Promise.all(candidates.map(candidate =>
+        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.forceReleaseLicense', [candidate], true, {nonce: nonce++})));
+    });
+  });
+});
addedjs-packages/tests/collator-selection/identity.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/collator-selection/identity.seqtest.ts
@@ -0,0 +1,284 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {UniqueHelper} from '@unique/playgrounds/unique.js';
+
+async function getIdentities(helper: UniqueHelper) {
+  const identities: [string, any][] = [];
+  for(const [key, value] of await helper.getApi().query.identity.identityOf.entries())
+    identities.push([(key as any).toHuman(), (value as any).unwrap()]);
+  return identities;
+}
+
+async function getIdentityAccounts(helper: UniqueHelper) {
+  return (await getIdentities(helper)).flatMap(([key, _value]) => key);
+}
+
+async function getSubIdentityAccounts(helper: UniqueHelper, address: string) {
+  return ((await helper.getApi().query.identity.subsOf(address)).toHuman() as any)[1];
+}
+
+async function getSubIdentityName(helper: UniqueHelper, address: string) {
+  return ((await helper.getApi().query.identity.superOf(address)).toHuman() as any);
+}
+
+describe('Integration Test: Identities Manipulation', () => {
+  let superuser: IKeyringPair;
+
+  before(async function() {
+    if(!process.env.RUN_COLLATOR_TESTS) this.skip();
+
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Identity]);
+      superuser = await privateKey('//Alice');
+    });
+  });
+
+  itSub('Normal calls do not work', async ({helper}) => {
+    // console.error = () => {};
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.identity.setIdentity', [{info: {display: {Raw: 'Meowser'}}}] as any))
+      .to.be.rejectedWith(/Transaction call is not expected/);
+  });
+
+  describe('Identities', () => {
+    itSub('Sets identities', async ({helper}) => {
+      const oldIdentitiesCount = (await getIdentityAccounts(helper)).length;
+
+      const crowdSize = 10;
+      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
+      const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
+
+      expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + crowdSize);
+    });
+
+    itSub('Setting identities does not delete existing but does overwrite', async ({helper}) => {
+      const crowd = await helper.arrange.createCrowd(10, 0n, superuser);
+      const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
+
+      // insert a single identity
+      let singleIdentity = identities.pop()!;
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [[singleIdentity]] as any);
+
+      const oldIdentitiesCount = (await getIdentityAccounts(helper)).length;
+
+      // change an identity and push it with a few new others
+      singleIdentity = [singleIdentity[0], {info: {display: {Raw: 'something special'}}}];
+      identities.push(singleIdentity);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
+
+      // oldIdentitiesCount + 9 because one identity is overwritten, not inserted on top
+      expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + 9);
+      expect((await helper.callRpc('api.query.identity.identityOf', [singleIdentity[0]])).toHuman().info.display)
+        .to.be.deep.equal({Raw: 'something special'});
+    });
+
+    itSub('Removes identities', async ({helper}) => {
+      const crowd = await helper.arrange.createCrowd(10, 0n, superuser);
+      const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
+      const oldIdentities = await getIdentityAccounts(helper);
+
+      // delete a couple, check that they are no longer there
+      const scapegoats = [crowd.pop()!.address, crowd.pop()!.address];
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [scapegoats]);
+      const newIdentities = await getIdentityAccounts(helper);
+      expect(newIdentities.concat(scapegoats)).to.be.have.members(oldIdentities);
+    });
+  });
+
+  describe('Sub-identities', () => {
+    itSub('Sets subs', async ({helper}) => {
+      const crowdSize = 18;
+      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
+      const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!];
+
+      const subsPerSup = crowd.length / supers.length;
+      let subCount = 0;
+      const subs = [
+        crowd.slice(subCount, subCount += subsPerSup + 1),
+        crowd.slice(subCount, subCount += subsPerSup),
+        crowd.slice(subCount, subCount += subsPerSup - 1),
+      ];
+
+      const subsInfo = supers.map((acc, i) => [
+        acc.address, [
+          1000000n + BigInt(i + 1),
+          subs[i].map((sub, j) => [
+            sub.address, {Raw: `accounter #${j}`},
+          ]),
+        ],
+      ]);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any);
+
+      for(let i = 0; i < supers.length; i++) {
+        // check deposit
+        expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i);
+
+        const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address);
+        // check sub-identities as account ids
+        expect(subsAccounts).to.include.members(subs[i].map(x => x.address));
+
+        for(let j = 0; j < subsAccounts.length; j++) {
+          // check sub-identities' names
+          expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`});
+        }
+      }
+    });
+
+    itSub('Setting sub-identities does not delete other existing but does overwrite own', async ({helper}) => {
+      const crowdSize = 18;
+      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
+      const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!];
+
+      const subsPerSup = crowd.length / supers.length;
+      let subCount = 0;
+      const subs = [
+        crowd.slice(subCount, subCount += subsPerSup + 1),
+        crowd.slice(subCount, subCount += subsPerSup),
+        crowd.slice(subCount, subCount += subsPerSup - 1),
+      ];
+
+      const subsInfo1 = supers.map((acc, i) => [
+        acc.address, [
+          1000000n + BigInt(i + 1),
+          subs[i].map((sub, j) => [
+            sub.address, {Raw: `accounter #${j}`},
+          ]),
+        ],
+      ]);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any);
+
+      // change some sub-identities...
+      subs[2].pop(); subs[2].pop(); subs[2].push(...await helper.arrange.createAccounts([0n], superuser));
+
+      // ...and set them
+      const subsInfo2 = [[
+        supers[2].address, [
+          999999n,
+          subs[2].map((sub, j) => [
+            sub.address, {Raw: `discounter #${j}`},
+          ]),
+        ],
+      ]];
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any);
+
+      // make sure everything else is the same
+      for(let i = 0; i < supers.length - 1; i++) {
+        // check deposit
+        expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i);
+
+        const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address);
+        // check sub-identities as account ids
+        expect(subsAccounts).to.include.members(subs[i].map(x => x.address));
+
+        for(let j = 0; j < subsAccounts; j++) {
+          // check sub-identities' names
+          expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`});
+        }
+      }
+
+      // check deposit
+      expect(((await helper.getApi().query.identity.subsOf(supers[2].address)).toJSON() as any)[0]).to.be.equal(999999);
+
+      const subsAccounts = await getSubIdentityAccounts(helper, supers[2].address);
+      // check sub-identities as account ids
+      expect(subsAccounts).to.include.members(subs[2].map(x => x.address));
+
+      for(let j = 0; j < subsAccounts.length; j++) {
+        // check sub-identities' names
+        expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `discounter #${j}`});
+      }
+    });
+
+    itSub('Removes sub-identities', async ({helper}) => {
+      const crowdSize = 3;
+      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
+      const sup = crowd.pop()!;
+
+      const subsInfo1 = [[
+        sup.address, [
+          1000000n,
+          crowd.map((sub, j) => [
+            sub.address, {Raw: `accounter #${j}`},
+          ]),
+        ],
+      ]];
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any);
+
+      // empty sub-identities should delete the records
+      const subsInfo2 = [[
+        sup.address, [
+          1000000n,
+          [],
+        ],
+      ]];
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any);
+
+      // check deposit
+      expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]);
+
+      for(let j = 0; j < crowd.length; j++) {
+        // check sub-identities' names
+        expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null;
+      }
+    });
+
+    itSub('Removing identities deletes associated sub-identities', async ({helper}) => {
+      const crowd = await helper.arrange.createCrowd(3, 0n, superuser);
+      const sup = crowd.pop()!;
+
+      // insert identity
+      const identities = [[sup.address, {info: {display: {Raw: 'mental'}}}]];
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
+
+      // and its sub-identities
+      const subsInfo = [[
+        sup.address, [
+          1000000n,
+          crowd.map((sub, j) => [
+            sub.address, {Raw: `accounter #${j}`},
+          ]),
+        ],
+      ]];
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any);
+
+      // delete top identity
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [[sup.address]]);
+
+      // check that sub-identities are deleted
+      expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]);
+
+      for(let j = 0; j < crowd.length; j++) {
+        // check sub-identities' names
+        expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null;
+      }
+    });
+  });
+
+  after(async function() {
+    if(!process.env.RUN_COLLATOR_TESTS) return;
+
+    await usingPlaygrounds(async helper => {
+      if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) return;
+
+      const identitiesToRemove: string[] = await getIdentityAccounts(helper);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]);
+    });
+  });
+});
addedjs-packages/tests/config.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/config.ts
@@ -0,0 +1,34 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 process from 'process';
+
+const config = {
+  relayUrl: process.env.RELAY_URL || 'ws://127.0.0.1:9844',
+  substrateUrl: process.env.RELAY_OPAL_URL || process.env.RELAY_QUARTZ_URL || process.env.RELAY_UNIQUE_URL || process.env.RELAY_SAPPHIRE_URL || 'ws://127.0.0.1:9944',
+  acalaUrl: process.env.RELAY_ACALA_URL || 'ws://127.0.0.1:9946',
+  karuraUrl: process.env.RELAY_KARURA_URL || 'ws://127.0.0.1:9946',
+  moonbeamUrl: process.env.RELAY_MOONBEAM_URL || 'ws://127.0.0.1:9947',
+  moonriverUrl: process.env.RELAY_MOONRIVER_URL || 'ws://127.0.0.1:9947',
+  astarUrl: process.env.RELAY_ASTAR_URL || 'ws://127.0.0.1:9949',
+  shidenUrl: process.env.RELAY_SHIDEN_URL || 'ws://127.0.0.1:9949',
+  westmintUrl: process.env.RELAY_WESTMINT_URL || 'ws://127.0.0.1:9948',
+  statemineUrl: process.env.RELAY_STATEMINE_URL || 'ws://127.0.0.1:9948',
+  statemintUrl: process.env.RELAY_STATEMINT_URL || 'ws://127.0.0.1:9948',
+  polkadexUrl: process.env.RELAY_POLKADEX_URL || 'ws://127.0.0.1:9950',
+};
+
+export default config;
addedjs-packages/tests/confirmSponsorship.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/confirmSponsorship.test.ts
@@ -0,0 +1,253 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+async function setSponsorHelper(collection: any, signer: IKeyringPair, sponsorAddress: string) {
+  await collection.setSponsor(signer, sponsorAddress);
+  const raw = (await collection.getData())?.raw;
+  expect(raw.sponsorship.Unconfirmed).to.be.equal(sponsorAddress);
+}
+
+async function confirmSponsorHelper(collection: any, signer: IKeyringPair) {
+  await collection.confirmSponsorship(signer);
+  const raw = (await collection.getData())?.raw;
+  expect(raw.sponsorship.Confirmed).to.be.equal(signer.address);
+}
+
+describe('integration test: ext. confirmSponsorship():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+  let zeroBalance: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie, zeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n], donor);
+    });
+  });
+
+  itSub('Confirm collection sponsorship', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await setSponsorHelper(collection, alice, bob.address);
+    await confirmSponsorHelper(collection, bob);
+  });
+
+  itSub('Add sponsor to a collection after the same sponsor was already added and confirmed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await setSponsorHelper(collection, alice, bob.address);
+    await confirmSponsorHelper(collection, bob);
+    await setSponsorHelper(collection, alice, bob.address);
+  });
+  itSub('Add new sponsor to a collection after another sponsor was already added and confirmed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await setSponsorHelper(collection, alice, bob.address);
+    await confirmSponsorHelper(collection, bob);
+    await setSponsorHelper(collection, alice, charlie.address);
+  });
+
+  itSub('NFT: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    const token = await collection.mintToken(alice, {Substrate: zeroBalance.address});
+    await token.transfer(zeroBalance, {Substrate: alice.address});
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
+  });
+
+  itSub('Fungible: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    await collection.mint(alice, 100n, {Substrate: zeroBalance.address});
+    await collection.transfer(zeroBalance, {Substrate: alice.address}, 1n);
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
+  });
+
+  itSub.ifWithPallets('ReFungible: Transfer fees are paid by the sponsor after confirmation', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address});
+    await token.transfer(zeroBalance, {Substrate: alice.address}, 1n);
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
+  });
+
+  itSub('CreateItem fees are paid by the sponsor after confirmation', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
+    await collection.addToAllowList(alice, {Substrate: zeroBalance.address});
+
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
+  });
+
+  itSub('NFT: Sponsoring of transfers is rate limited', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
+      sponsorTransferTimeout: 1000,
+    }});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    await token.transfer(alice, {Substrate: zeroBalance.address});
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address});
+    await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
+  });
+
+  itSub('Fungible: Sponsoring is rate limited', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
+      sponsorTransferTimeout: 1000,
+    }});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+
+    await collection.mint(alice, 100n, {Substrate: zeroBalance.address});
+    await collection.transfer(zeroBalance, {Substrate: zeroBalance.address}, 1n);
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    const transferTx = () => collection.transfer(zeroBalance, {Substrate: zeroBalance.address});
+    await expect(transferTx()).to.be.rejected;
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
+  });
+
+  itSub.ifWithPallets('ReFungible: Sponsoring is rate limited', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
+      sponsorTransferTimeout: 1000,
+    }});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+
+    const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address});
+    await token.transfer(zeroBalance, {Substrate: alice.address});
+
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address});
+    await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
+  });
+
+  itSub('NFT: Sponsoring of createItem is rate limited', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
+      sponsoredDataRateLimit: {blocks: 1000},
+      sponsorTransferTimeout: 1000,
+    }});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    await collection.setPermissions(alice, {mintMode: true, access: 'AllowList'});
+    await collection.addToAllowList(alice, {Substrate: zeroBalance.address});
+
+    await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
+
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    const mintTx = () => collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
+    await expect(mintTx()).to.be.rejectedWith('Inability to pay some fees');
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
+  });
+});
+
+describe('(!negative test!) integration test: ext. confirmSponsorship():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+  let ownerZeroBalance: IKeyringPair;
+  let senderZeroBalance: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie, ownerZeroBalance, senderZeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n, 0n], donor);
+    });
+  });
+
+  itSub('(!negative test!) Confirm sponsorship for a collection that never existed', async ({helper}) => {
+    const collectionId = NON_EXISTENT_COLLECTION_ID;
+    const confirmSponsorshipTx = () => helper.collection.confirmSponsorship(bob, collectionId);
+    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('(!negative test!) Confirm sponsorship using a non-sponsor address', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
+    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
+  });
+
+  itSub('(!negative test!) Confirm sponsorship using owner address', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    const confirmSponsorshipTx = () => collection.confirmSponsorship(alice);
+    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
+  });
+
+  itSub('(!negative test!) Confirm sponsorship by collection admin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.addAdmin(alice, {Substrate: charlie.address});
+    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
+    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
+  });
+
+  itSub('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
+    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
+  });
+
+  itSub('(!negative test!) Confirm sponsorship in a collection that was destroyed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.burn(alice);
+    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
+    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    const token = await collection.mintToken(alice, {Substrate: ownerZeroBalance.address});
+    const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    const transferTx = () =>  token.transfer(senderZeroBalance, {Substrate: alice.address});
+    await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
+    const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address);
+    expect(sponsorBalanceAfter).to.equal(sponsorBalanceBefore);
+  });
+});
addedjs-packages/tests/connection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/connection.test.ts
@@ -0,0 +1,32 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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';
+
+describe('Connection smoke test', () => {
+  itSub('Connection can be established', async ({helper}) => {
+    const health = (await helper.callRpc('api.rpc.system.health')).toJSON();
+    expect(health).to.be.not.empty;
+  });
+
+  it('Cannot connect to 255.255.255.255', async () => {
+    await expect((async () => {
+      await usingPlaygrounds(async helper => {
+        await helper.callRpc('api.rpc.system.health');
+      }, 'ws://255.255.255.255:9944');
+    })()).to.be.eventually.rejected;
+  });
+});
addedjs-packages/tests/createCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/createCollection.test.ts
@@ -0,0 +1,194 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+async function mintCollectionHelper(helper: UniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') {
+  let collection;
+  if(type === 'nft') {
+    collection = await helper.nft.mintCollection(signer, options);
+  } else if(type === 'fungible') {
+    collection = await helper.ft.mintCollection(signer, options, 0);
+  } else {
+    collection = await helper.rft.mintCollection(signer, options);
+  }
+  const data = await collection.getData();
+  expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(signer.address));
+  expect(data?.name).to.be.equal(options.name);
+  expect(data?.description).to.be.equal(options.description);
+  expect(data?.raw.tokenPrefix).to.be.equal(options.tokenPrefix);
+  if(options.properties) {
+    expect(data?.raw.properties).to.be.deep.equal(options.properties);
+  }
+  if(options.adminList) {
+    expect(data?.admins).to.be.deep.equal(options.adminList);
+  }
+
+  if(options.flags) {
+    if((options.flags[0] & 64) != 0)
+      expect(data?.raw.flags.erc721metadata).to.be.true;
+    if((options.flags[0] & 128) != 0)
+      expect(data?.raw.flags.foreign).to.be.false;
+  }
+
+  if(options.tokenPropertyPermissions) {
+    expect(data?.raw.tokenPropertyPermissions).to.be.deep.equal(options.tokenPropertyPermissions);
+  }
+
+  return collection;
+}
+
+describe('integration test: ext. createCollection():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+  itSub('Create new NFT collection', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 'nft');
+  });
+  itSub('Create new NFT collection whith collection_name of maximum length (64 bytes)', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {name: 'A'.repeat(64), description: 'descr', tokenPrefix: 'COL'}, 'nft');
+  });
+  itSub('Create new NFT collection whith collection_description of maximum length (256 bytes)', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {name: 'name', description: 'A'.repeat(256), tokenPrefix: 'COL'}, 'nft');
+  });
+  itSub('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(16)}, 'nft');
+  });
+
+  itSub('Create new Fungible collection', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
+  });
+
+  itSub.ifWithPallets('Create new ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'refungible');
+  });
+
+  itSub('create new collection with properties', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {
+      name: 'name', description: 'descr', tokenPrefix: 'COL',
+      properties: [{key: 'key1', value: 'val1'}],
+      tokenPropertyPermissions: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}],
+    }, 'nft');
+  });
+
+  itSub('create new collection with admin', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {
+      name: 'name', description: 'descr', tokenPrefix: 'COL',
+      adminList: [{Substrate: bob.address}],
+    }, 'nft');
+  });
+
+  itSub('create new collection with flags', async ({helper}) => {
+    await mintCollectionHelper(helper, alice, {
+      name: 'name', description: 'descr', tokenPrefix: 'COL',
+      flags: [CollectionFlag.Erc721metadata],
+    }, 'nft');
+
+    // User can not set Foreign flag itself
+
+    await expect(mintCollectionHelper(helper, alice, {
+      name: 'name', description: 'descr', tokenPrefix: 'COL',
+      flags: [CollectionFlag.Foreign],
+    }, 'nft')).to.be.rejectedWith(/common.NoPermission/);
+
+    await expect(mintCollectionHelper(helper, alice, {
+      name: 'name', description: 'descr', tokenPrefix: 'COL',
+      flags: [CollectionFlag.Erc721metadata, CollectionFlag.Foreign],
+    }, 'nft')).to.be.rejectedWith(/common.NoPermission/);
+  });
+
+  itSub('Create new collection with extra fields', async ({helper}) => {
+    const collection = await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
+    await collection.setPermissions(alice, {access: 'AllowList'});
+    await collection.setLimits(alice, {accountTokenOwnershipLimit: 3});
+    const data = await collection.getData();
+    const limits = await collection.getEffectiveLimits();
+    const raw = data?.raw;
+
+    expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
+    expect(data?.name).to.be.equal('name');
+    expect(data?.description).to.be.equal('descr');
+    expect(raw.permissions.access).to.be.equal('AllowList');
+    expect(raw.mode).to.be.deep.equal({Fungible: '0'});
+    expect(limits.accountTokenOwnershipLimit).to.be.equal(3);
+  });
+
+  itSub('New collection is not external', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
+    const data = await collection.getData();
+    expect(data?.raw.readOnly).to.be.false;
+  });
+});
+
+describe('(!negative test!) integration test: ext. createCollection():', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  itSub('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async ({helper}) => {
+    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'A'.repeat(65), description: 'descr', tokenPrefix: 'COL'});
+    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
+  });
+  itSub('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async ({helper}) => {
+    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'A'.repeat(257), tokenPrefix: 'COL'});
+    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
+  });
+  itSub('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async ({helper}) => {
+    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)});
+    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
+  });
+
+  itSub('(!negative test!) fails when bad limits are set', async ({helper}) => {
+    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}});
+    await expect(mintCollectionTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
+  });
+
+  itSub('(!negative test!) create collection with incorrect property limit (64 elements)', async ({helper}) => {
+    const props: IProperty[] = [];
+
+    for(let i = 0; i < 65; i++) {
+      props.push({key: `key${i}`, value: `value${i}`});
+    }
+    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
+    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
+  });
+
+  itSub('(!negative test!) create collection with incorrect property limit (40 kb)', async ({helper}) => {
+    const props: IProperty[] = [];
+
+    for(let i = 0; i < 32; i++) {
+      props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)});
+    }
+
+    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
+    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
+  });
+});
addedjs-packages/tests/createItem.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/createItem.test.ts
@@ -0,0 +1,272 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+async function mintTokenHelper(helper: UniqueHelper, collection: any, signer: IKeyringPair, owner: ICrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) {
+  let token;
+  const itemCountBefore = await helper.collection.getLastTokenId(collection.collectionId);
+  const itemBalanceBefore = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
+  if(type === 'nft') {
+    token = await collection.mintToken(signer, owner, properties);
+  } else if(type === 'fungible') {
+    await collection.mint(signer, 10n, owner);
+  } else {
+    token = await collection.mintToken(signer, 100n, owner, properties);
+  }
+
+  const itemCountAfter = await helper.collection.getLastTokenId(collection.collectionId);
+  const itemBalanceAfter = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
+
+  if(type === 'fungible') {
+    expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);
+  } else {
+    expect(itemCountAfter).to.be.equal(itemCountBefore + 1);
+  }
+
+  return token;
+}
+
+
+describe('integration test: ext. ():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Create new item in NFT collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await mintTokenHelper(helper, collection, alice, {Substrate: alice.address});
+  });
+  itSub('Create new item in Fungible collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+    await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'fungible');
+  });
+  itSub('Check events on create new item in Fungible collection', async ({helper}) => {
+    const {collectionId} = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
+    const to = {Substrate: alice.address};
+    {
+      const createData = {fungible: {value: 100}};
+      const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
+      const result = helper.util.extractTokensFromCreationResult(events);
+      expect(result.tokens[0].amount).to.be.equal(100n);
+      expect(result.tokens[0].collectionId).to.be.equal(collectionId);
+      expect(result.tokens[0].owner).to.be.deep.equal(to);
+    }
+    {
+      const createData = {fungible: {value: 50}};
+      const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
+      const result = helper.util.extractTokensFromCreationResult(events);
+      expect(result.tokens[0].amount).to.be.equal(50n);
+      expect(result.tokens[0].collectionId).to.be.equal(collectionId);
+      expect(result.tokens[0].owner).to.be.deep.equal(to);
+    }
+  });
+  itSub.ifWithPallets('Create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) =>  {
+    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'refungible');
+  });
+  itSub('Create new item in NFT collection with collection admin permissions', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await mintTokenHelper(helper, collection, bob, {Substrate: alice.address});
+  });
+  itSub('Create new item in Fungible collection with collection admin permissions', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'fungible');
+  });
+  itSub.ifWithPallets('Create new item in ReFungible collection with collection admin permissions', [Pallets.ReFungible], async ({helper}) =>  {
+    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'refungible');
+  });
+
+  itSub('Set property Admin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
+      properties: [{key: 'k', value: 'v'}],
+      tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}}],
+    });
+    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
+  });
+
+  itSub('Set property AdminConst', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
+      properties: [{key: 'k', value: 'v'}],
+      tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}],
+    });
+    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
+  });
+
+  itSub('Set property itemOwnerOrAdmin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
+      properties: [{key: 'k', value: 'v'}],
+      tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}],
+    });
+    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
+  });
+
+  itSub('Check total pieces of Fungible token', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+    const amount = 10n;
+    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'fungible');
+    {
+      const totalPieces = await collection.getTotalPieces();
+      expect(totalPieces).to.be.equal(amount);
+    }
+    await collection.transfer(bob, {Substrate: alice.address}, 1n);
+    {
+      const totalPieces = await collection.getTotalPieces();
+      expect(totalPieces).to.be.equal(amount);
+    }
+  });
+
+  itSub('Check total pieces of NFT token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const amount = 1n;
+    const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address});
+    {
+      const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
+      expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
+    }
+    await token.transfer(bob, {Substrate: alice.address});
+    {
+      const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
+      expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
+    }
+  });
+
+  itSub.ifWithPallets('Check total pieces of ReFungible token', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const amount = 100n;
+    const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'refungible');
+    {
+      const totalPieces = await token.getTotalPieces();
+      expect(totalPieces).to.be.equal(amount);
+    }
+    await token.transfer(bob, {Substrate: alice.address}, 60n);
+    {
+      const totalPieces = await token.getTotalPieces();
+      expect(totalPieces).to.be.equal(amount);
+    }
+  });
+});
+
+describe('Negative integration test: ext. createItem():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Regular user cannot create new item in NFT collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const mintTx = () => collection.mintToken(bob, {Substrate: bob.address});
+    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
+  });
+  itSub('Regular user cannot create new item in Fungible collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+    const mintTx = () => collection.mint(bob, 10n, {Substrate: bob.address});
+    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
+  });
+  itSub.ifWithPallets('Regular user cannot create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const mintTx = () => collection.mintToken(bob, 100n, {Substrate: bob.address});
+    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
+  });
+
+  itSub('No editing rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
+      tokenPropertyPermissions: [{key: 'k', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}],
+    });
+    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
+    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('User doesnt have editing rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
+      tokenPropertyPermissions: [{key: 'k', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}],
+    });
+    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
+    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Adding property without access rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
+    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Adding more than 64 prps', async ({helper}) => {
+    const props: IProperty[] = [];
+
+    for(let i = 0; i < 65; i++) {
+      props.push({key: `key${i}`, value: `value${i}`});
+    }
+
+
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, props);
+    await expect(mintTx()).to.be.rejectedWith('Verification Error');
+  });
+
+  itSub('Trying to add bigger property than allowed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'k1', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
+        {key: 'k2', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
+      ],
+    });
+    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [
+      {key: 'k1', value: 'vvvvvv'.repeat(5000)},
+      {key: 'k2', value: 'vvv'.repeat(5000)},
+    ]);
+    await expect(mintTx()).to.be.rejectedWith(/common\.NoSpaceForProperty/);
+  });
+
+  itSub('Check total pieces for invalid Fungible token', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
+    const invalidTokenId = 1_000_000;
+    expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
+    expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
+  });
+
+  itSub('Check total pieces for invalid NFT token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const invalidTokenId = 1_000_000;
+    expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
+    expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
+  });
+
+  itSub.ifWithPallets('Check total pieces for invalid Refungible token', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
+    const invalidTokenId = 1_000_000;
+    expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
+    expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
+  });
+});
addedjs-packages/tests/createMultipleItems.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/createMultipleItems.test.ts
@@ -0,0 +1,374 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js';
+
+describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  itSub('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: '1'}]},
+      {properties: [{key: 'data', value: '2'}]},
+      {properties: [{key: 'data', value: '3'}]},
+    ];
+    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    for(const [i, token] of tokens.entries()) {
+      const tokenData = await token.getData();
+      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
+      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
+    }
+  });
+
+  itSub('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+    const args = [
+      {value: 1n},
+      {value: 2n},
+      {value: 3n},
+    ];
+    await helper.ft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, args, {Substrate: alice.address});
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(6n);
+  });
+
+  itSub.ifWithPallets('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+    const args = [
+      {pieces: 1n},
+      {pieces: 2n},
+      {pieces: 3n},
+    ];
+    const tokens = await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+
+    for(const [i, token] of tokens.entries()) {
+      expect(await token.getBalance({Substrate: alice.address})).to.be.equal(BigInt(i + 1));
+    }
+  });
+
+  itSub('Can mint amount of items equals to collection limits', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      limits: {
+        tokenLimit: 2,
+      },
+    });
+    const args = [{}, {}];
+    await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+  });
+
+  itSub('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}},
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: '1'}]},
+      {properties: [{key: 'data', value: '2'}]},
+      {properties: [{key: 'data', value: '3'}]},
+    ];
+    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    for(const [i, token] of tokens.entries()) {
+      const tokenData = await token.getData();
+      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
+      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
+    }
+  });
+
+  itSub('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}},
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: '1'}]},
+      {properties: [{key: 'data', value: '2'}]},
+      {properties: [{key: 'data', value: '3'}]},
+    ];
+    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    for(const [i, token] of tokens.entries()) {
+      const tokenData = await token.getData();
+      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
+      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
+    }
+  });
+
+  itSub('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: '1'}]},
+      {properties: [{key: 'data', value: '2'}]},
+      {properties: [{key: 'data', value: '3'}]},
+    ];
+    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    for(const [i, token] of tokens.entries()) {
+      const tokenData = await token.getData();
+      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
+      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
+    }
+  });
+});
+
+describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Regular user cannot create items in active NFT collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+    const args = [
+      {},
+      {},
+    ];
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
+  });
+
+  itSub('Regular user cannot create items in active Fungible collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+    const args = [
+      {value: 1n},
+      {value: 2n},
+      {value: 3n},
+    ];
+    const mintTx = () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, args, {Substrate: alice.address});
+    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
+  });
+
+  itSub.ifWithPallets('Regular user cannot create items in active ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+    const args = [
+      {pieces: 1n},
+      {pieces: 1n},
+      {pieces: 1n},
+    ];
+    const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
+  });
+
+  itSub('Create token in not existing collection', async ({helper}) => {
+    const collectionId = 1_000_000;
+    const args = [
+      {},
+      {},
+    ];
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('Create NFTs that has reached the maximum data limit', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: 'A'.repeat(32769)}]},
+      {properties: [{key: 'data', value: 'B'.repeat(32769)}]},
+      {properties: [{key: 'data', value: 'C'.repeat(32769)}]},
+    ];
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith('Verification Error');
+  });
+
+  itSub.ifWithPallets('Create Refungible tokens that has reached the maximum data limit', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+      ],
+    });
+    const args = [
+      {pieces: 10n, properties: [{key: 'data', value: 'A'.repeat(32769)}]},
+      {pieces: 10n, properties: [{key: 'data', value: 'B'.repeat(32769)}]},
+      {pieces: 10n, properties: [{key: 'data', value: 'C'.repeat(32769)}]},
+    ];
+    const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith('Verification Error');
+  });
+
+  itSub.ifWithPallets('Create tokens with different types', [Pallets.ReFungible], async ({helper}) => {
+    const {collectionId} = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+
+    const types = ['NFT', 'Fungible', 'ReFungible'];
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.createMultipleItems',
+      [collectionId, {Substrate: alice.address}, types],
+    )).to.be.rejectedWith(/nonfungible\.NotNonfungibleDataUsedToMintFungibleCollectionToken/);
+  });
+
+  itSub('Create tokens with different data limits <> maximum data limit', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: 'A'}]},
+      {properties: [{key: 'data', value: 'B'.repeat(32769)}]},
+    ];
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith('Verification Error');
+  });
+
+  itSub('Fails when minting tokens exceeds collectionLimits amount', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+      ],
+      limits: {
+        tokenLimit: 1,
+      },
+    });
+    const args = [
+      {},
+      {},
+    ];
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
+  });
+
+  itSub('User doesnt have editing rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: false}},
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: 'A'}]},
+    ];
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Adding property without access rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      properties: [
+        {
+          key: 'data',
+          value: 'v',
+        },
+      ],
+    });
+    const args = [
+      {properties: [{key: 'data', value: 'A'}]},
+    ];
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Adding more than 64 prps', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+    const prps = [];
+
+    for(let i = 0; i < 65; i++) {
+      prps.push({key: `key${i}`, value: `value${i}`});
+    }
+
+    const args = [
+      {properties: prps},
+      {properties: prps},
+      {properties: prps},
+    ];
+
+    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+    await expect(mintTx()).to.be.rejectedWith('Verification Error');
+  });
+});
addedjs-packages/tests/createMultipleItemsEx.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/createMultipleItemsEx.test.ts
@@ -0,0 +1,442 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js';
+import type {IProperty} from '@unique/playgrounds/types.js';
+
+describe('Integration Test: createMultipleItemsEx', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub('can initialize multiple NFT with different owners', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+      },
+      {
+        owner: {Substrate: bob.address},
+      },
+      {
+        owner: {Substrate: charlie.address},
+      },
+    ];
+
+    const tokens = await collection.mintMultipleTokens(alice, args);
+    for(const [i, token] of tokens.entries()) {
+      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
+    }
+  });
+
+  itSub('createMultipleItemsEx with property Admin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {
+          key: 'k',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+            tokenOwner: false,
+          },
+        },
+      ],
+    });
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: [{key: 'k', value: 'v1'}],
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: [{key: 'k', value: 'v2'}],
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: [{key: 'k', value: 'v3'}],
+      },
+    ];
+
+    const tokens = await collection.mintMultipleTokens(alice, args);
+    for(const [i, token] of tokens.entries()) {
+      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
+      expect(await token.getData()).to.not.be.empty;
+    }
+  });
+
+  itSub('createMultipleItemsEx with property AdminConst', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {
+          key: 'k',
+          permission: {
+            mutable: false,
+            collectionAdmin: true,
+            tokenOwner: false,
+          },
+        },
+      ],
+    });
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: [{key: 'k', value: 'v1'}],
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: [{key: 'k', value: 'v2'}],
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: [{key: 'k', value: 'v3'}],
+      },
+    ];
+
+    const tokens = await collection.mintMultipleTokens(alice, args);
+    for(const [i, token] of tokens.entries()) {
+      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
+      expect(await token.getData()).to.not.be.empty;
+    }
+  });
+
+  itSub('createMultipleItemsEx with property itemOwnerOrAdmin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {
+          key: 'k',
+          permission: {
+            mutable: false,
+            collectionAdmin: true,
+            tokenOwner: true,
+          },
+        },
+      ],
+    });
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: [{key: 'k', value: 'v1'}],
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: [{key: 'k', value: 'v2'}],
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: [{key: 'k', value: 'v3'}],
+      },
+    ];
+
+    const tokens = await collection.mintMultipleTokens(alice, args);
+    for(const [i, token] of tokens.entries()) {
+      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
+      expect(await token.getData()).to.not.be.empty;
+    }
+  });
+
+  itSub('can initialize fungible with multiple owners', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    }, 0);
+
+    await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, {
+      Fungible: new Map([
+        [JSON.stringify({Substrate: alice.address}), 50],
+        [JSON.stringify({Substrate: bob.address}), 100],
+      ]),
+    }], true);
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(50n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(100n);
+  });
+
+  itSub.ifWithPallets('can initialize an RFT with multiple owners', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}},
+      ],
+    });
+
+    await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, {
+      RefungibleMultipleOwners: {
+        users: new Map([
+          [JSON.stringify({Substrate: alice.address}), 1],
+          [JSON.stringify({Substrate: bob.address}), 2],
+        ]),
+        properties: [
+          {key: 'k', value: 'v'},
+        ],
+      },
+    }], true);
+    const tokenId = await collection.getLastTokenId();
+    expect(tokenId).to.be.equal(1);
+    expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n);
+    expect(await collection.getTokenBalance(1, {Substrate: bob.address})).to.be.equal(2n);
+  });
+
+  itSub.ifWithPallets('can initialize multiple RFTs with the same owner', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {key: 'k', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},
+      ],
+    });
+
+    await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, {
+      RefungibleMultipleItems: [
+        {
+          user: {Substrate: alice.address}, pieces: 1,
+          properties: [
+            {key: 'k', value: 'v1'},
+          ],
+        },
+        {
+          user: {Substrate: alice.address}, pieces: 3,
+          properties: [
+            {key: 'k', value: 'v2'},
+          ],
+        },
+      ],
+    }], true);
+
+    expect(await collection.getLastTokenId()).to.be.equal(2);
+    expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n);
+    expect(await collection.getTokenBalance(2, {Substrate: alice.address})).to.be.equal(3n);
+
+    const tokenData1 = await helper.rft.getToken(collection.collectionId, 1);
+    expect(tokenData1).to.not.be.null;
+    expect(tokenData1?.properties[0]).to.be.deep.equal({key: 'k', value: 'v1'});
+
+    const tokenData2 = await helper.rft.getToken(collection.collectionId, 2);
+    expect(tokenData2).to.not.be.null;
+    expect(tokenData2?.properties[0]).to.be.deep.equal({key: 'k', value: 'v2'});
+  });
+});
+
+describe('Negative test: createMultipleItemsEx', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub('No editing rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {
+          key: 'k',
+          permission: {
+            mutable: true,
+            collectionAdmin: false,
+            tokenOwner: false,
+          },
+        },
+      ],
+    });
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: [{key: 'k', value: 'v1'}],
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: [{key: 'k', value: 'v2'}],
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: [{key: 'k', value: 'v3'}],
+      },
+    ];
+
+    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('User doesnt have editing rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {
+          key: 'k',
+          permission: {
+            mutable: false,
+            collectionAdmin: false,
+            tokenOwner: false,
+          },
+        },
+      ],
+    });
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: [{key: 'k', value: 'v1'}],
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: [{key: 'k', value: 'v2'}],
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: [{key: 'k', value: 'v3'}],
+      },
+    ];
+
+    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Adding property without access rights', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+    });
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: [{key: 'k', value: 'v1'}],
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: [{key: 'k', value: 'v2'}],
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: [{key: 'k', value: 'v3'}],
+      },
+    ];
+
+    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Adding more than 64 properties', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {
+          key: 'k',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+            tokenOwner: true,
+          },
+        },
+      ],
+    });
+
+    const properties: IProperty[] = [];
+
+    for(let i = 0; i < 65; i++) {
+      properties.push({key: `k${i}`, value: `v${i}`});
+    }
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: properties,
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: properties,
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: properties,
+      },
+    ];
+
+    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error');
+  });
+
+  itSub('Trying to add bigger property than allowed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'name',
+      description: 'descr',
+      tokenPrefix: 'COL',
+      tokenPropertyPermissions: [
+        {
+          key: 'k',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+            tokenOwner: true,
+          },
+        },
+      ],
+    });
+
+    const args = [
+      {
+        owner: {Substrate: alice.address},
+        properties: [{key: 'k', value: 'A'.repeat(32769)}],
+      },
+      {
+        owner: {Substrate: bob.address},
+        properties: [{key: 'k', value: 'A'.repeat(32769)}],
+      },
+      {
+        owner: {Substrate: charlie.address},
+        properties: [{key: 'k', value: 'A'.repeat(32769)}],
+      },
+    ];
+
+    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error');
+  });
+});
addedjs-packages/tests/creditFeesToTreasury.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/creditFeesToTreasury.seqtest.ts
@@ -0,0 +1,166 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {ApiPromise} from '@polkadot/api';
+import {usingPlaygrounds, expect, itSub} from './util/index.js';
+import type {u32} from '@polkadot/types-codec';
+
+const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z';
+const saneMinimumFee = 0.05;
+const saneMaximumFee = 0.5;
+const createCollectionDeposit = 100;
+
+// Skip the inflation block pauses if the block is close to inflation block
+// until the inflation happens
+/*eslint no-async-promise-executor: "off"*/
+function skipInflationBlock(api: ApiPromise): Promise<void> {
+  const promise = new Promise<void>(async (resolve) => {
+    const inflationBlockInterval = api.consts.inflation.inflationBlockInterval as u32;
+    const blockInterval = inflationBlockInterval.toNumber();
+    const unsubscribe = await api.rpc.chain.subscribeNewHeads(head => {
+      const currentBlock = head.number.toNumber();
+      if(currentBlock % blockInterval < blockInterval - 10) {
+        unsubscribe();
+        resolve();
+      } else {
+        console.log(`Skipping inflation block, current block: ${currentBlock}`);
+      }
+    });
+  });
+
+  return promise;
+}
+
+describe('integration test: Fees must be credited to Treasury:', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Total issuance does not change', async ({helper}) => {
+    const api = helper.getApi();
+    await skipInflationBlock(api);
+    await helper.wait.newBlocks(1);
+
+    const totalBefore = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt();
+
+    await helper.balance.transferToSubstrate(alice, bob.address, 1n);
+
+    const totalAfter = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt();
+
+    expect(totalAfter).to.be.equal(totalBefore);
+  });
+
+  itSub('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async ({helper}) => {
+    await skipInflationBlock(helper.getApi());
+    await helper.wait.newBlocks(1);
+
+    const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+
+    const amount = 1n;
+    await helper.balance.transferToSubstrate(alice, bob.address, amount);
+
+    const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+
+    const fee = aliceBalanceBefore - aliceBalanceAfter - amount;
+    const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
+
+    expect(treasuryIncrease).to.be.equal(fee);
+  });
+
+  itSub('Treasury balance increased by failed tx fee', async ({helper}) => {
+    const api = helper.getApi();
+    await helper.wait.newBlocks(1);
+
+    const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await expect(helper.signTransaction(bob, api.tx.balances.forceSetBalance(alice.address, 0))).to.be.rejected;
+
+    const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    const fee = bobBalanceBefore - bobBalanceAfter;
+    const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
+
+    expect(treasuryIncrease).to.be.equal(fee);
+  });
+
+  itSub('NFT Transactions also send fees to Treasury', async ({helper}) => {
+    await skipInflationBlock(helper.getApi());
+    await helper.wait.newBlocks(1);
+
+    const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+
+    await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+    const fee = aliceBalanceBefore - aliceBalanceAfter;
+    const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
+
+    expect(treasuryIncrease).to.be.equal(fee);
+  });
+
+  itSub('Fees are sane', async ({helper}) => {
+    const unique = helper.balance.getOneTokenNominal();
+    await skipInflationBlock(helper.getApi());
+    await helper.wait.newBlocks(1);
+
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+
+    await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+    const fee = aliceBalanceBefore - aliceBalanceAfter;
+
+    expect(fee / unique < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true;
+    expect(fee / unique < BigInt(Math.ceil(saneMinimumFee  + createCollectionDeposit))).to.be.true;
+  });
+
+  itSub('NFT Transfer fee is close to 0.1 Unique', async ({helper}) => {
+    await skipInflationBlock(helper.getApi());
+    await helper.wait.newBlocks(1);
+
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    });
+    // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+    await token.transfer(alice, {Substrate: bob.address});
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+
+    const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal());
+    const expectedTransferFee = 0.1;
+    // fee drifts because of NextFeeMultiplier
+    const tolerance = 0.001;
+
+    expect(Math.abs(fee - expectedTransferFee)).to.be.lessThan(tolerance);
+  });
+});
addedjs-packages/tests/destroyCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/destroyCollection.test.ts
@@ -0,0 +1,120 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, expect, usingPlaygrounds, Pallets} from './util/index.js';
+
+describe('integration test: ext. destroyCollection():', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  itSub('NFT collection can be destroyed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    });
+    await collection.burn(alice);
+    expect(await collection.getData()).to.be.null;
+  });
+  itSub('Fungible collection can be destroyed', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    }, 0);
+    await collection.burn(alice);
+    expect(await collection.getData()).to.be.null;
+  });
+  itSub.ifWithPallets('ReFungible collection can be destroyed', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    });
+    await collection.burn(alice);
+    expect(await collection.getData()).to.be.null;
+  });
+});
+
+describe('(!negative test!) integration test: ext. destroyCollection():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('(!negative test!) Destroy a collection that never existed', async ({helper}) => {
+    const collectionId = 1_000_000;
+    await expect(helper.collection.burn(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+  itSub('(!negative test!) Destroy a collection that has already been destroyed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    });
+    await collection.burn(alice);
+    await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+  itSub('(!negative test!) Destroy a collection using non-owner account', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    });
+    await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/);
+  });
+  itSub('(!negative test!) Destroy a collection using collection admin account', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    });
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/);
+  });
+  itSub('fails when OwnerCanDestroy == false', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+      limits: {
+        ownerCanDestroy: false,
+      },
+    });
+    await expect(collection.burn(alice)).to.be.rejectedWith(/common\.NoPermission/);
+  });
+  itSub('fails when a collection still has a token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+    });
+    await collection.mintToken(alice, {Substrate: alice.address});
+    await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CantDestroyNotEmptyCollection/);
+  });
+});
addedjs-packages/tests/enableDisableTransfer.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/enableDisableTransfer.test.ts
@@ -0,0 +1,82 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+
+describe('Enable/Disable Transfers', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('User can transfer token with enabled transfer flag', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+      limits: {
+        transfersEnabled: true,
+      },
+    });
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    await token.transfer(alice, {Substrate: bob.address});
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSub('User can\'n transfer token with disabled transfer flag', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+      limits: {
+        transfersEnabled: false,
+      },
+    });
+    const token = await collection.mintToken(alice, {Substrate: alice.address});
+    await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TransferNotAllowed/);
+  });
+});
+
+describe('Negative Enable/Disable Transfers', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('Non-owner cannot change transfer flag', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test',
+      description: 'test',
+      tokenPrefix: 'test',
+      limits: {
+        transfersEnabled: true,
+      },
+    });
+
+    await expect(collection.setLimits(bob, {transfersEnabled: false})).to.be.rejectedWith(/common\.NoPermission/);
+  });
+});
addedjs-packages/tests/eth/abi/collectionHelpers.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/collectionHelpers.json
@@ -0,0 +1,267 @@
+[
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "collectionId",
+        "type": "address"
+      }
+    ],
+    "name": "CollectionChanged",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "collectionId",
+        "type": "address"
+      }
+    ],
+    "name": "CollectionCreated",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "collectionId",
+        "type": "address"
+      }
+    ],
+    "name": "CollectionDestroyed",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint32", "name": "collectionId", "type": "uint32" }
+    ],
+    "name": "collectionAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionCreationFee",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "collectionAddress",
+        "type": "address"
+      }
+    ],
+    "name": "collectionId",
+    "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "name", "type": "string" },
+          { "internalType": "string", "name": "description", "type": "string" },
+          {
+            "internalType": "string",
+            "name": "token_prefix",
+            "type": "string"
+          },
+          {
+            "internalType": "enum CollectionMode",
+            "name": "mode",
+            "type": "uint8"
+          },
+          { "internalType": "uint8", "name": "decimals", "type": "uint8" },
+          {
+            "components": [
+              { "internalType": "string", "name": "key", "type": "string" },
+              { "internalType": "bytes", "name": "value", "type": "bytes" }
+            ],
+            "internalType": "struct Property[]",
+            "name": "properties",
+            "type": "tuple[]"
+          },
+          {
+            "components": [
+              { "internalType": "string", "name": "key", "type": "string" },
+              {
+                "components": [
+                  {
+                    "internalType": "enum TokenPermissionField",
+                    "name": "code",
+                    "type": "uint8"
+                  },
+                  { "internalType": "bool", "name": "value", "type": "bool" }
+                ],
+                "internalType": "struct PropertyPermission[]",
+                "name": "permissions",
+                "type": "tuple[]"
+              }
+            ],
+            "internalType": "struct TokenPropertyPermission[]",
+            "name": "token_property_permissions",
+            "type": "tuple[]"
+          },
+          {
+            "components": [
+              { "internalType": "address", "name": "eth", "type": "address" },
+              { "internalType": "uint256", "name": "sub", "type": "uint256" }
+            ],
+            "internalType": "struct CrossAddress[]",
+            "name": "admin_list",
+            "type": "tuple[]"
+          },
+          {
+            "components": [
+              { "internalType": "bool", "name": "token_owner", "type": "bool" },
+              {
+                "internalType": "bool",
+                "name": "collection_admin",
+                "type": "bool"
+              },
+              {
+                "internalType": "address[]",
+                "name": "restricted",
+                "type": "address[]"
+              }
+            ],
+            "internalType": "struct CollectionNestingAndPermission",
+            "name": "nesting_settings",
+            "type": "tuple"
+          },
+          {
+            "components": [
+              {
+                "internalType": "enum CollectionLimitField",
+                "name": "field",
+                "type": "uint8"
+              },
+              { "internalType": "uint256", "name": "value", "type": "uint256" }
+            ],
+            "internalType": "struct CollectionLimitValue[]",
+            "name": "limits",
+            "type": "tuple[]"
+          },
+          {
+            "components": [
+              { "internalType": "address", "name": "eth", "type": "address" },
+              { "internalType": "uint256", "name": "sub", "type": "uint256" }
+            ],
+            "internalType": "struct CrossAddress",
+            "name": "pending_sponsor",
+            "type": "tuple"
+          },
+          {
+            "internalType": "CollectionFlags",
+            "name": "flags",
+            "type": "uint8"
+          }
+        ],
+        "internalType": "struct CreateCollectionData",
+        "name": "data",
+        "type": "tuple"
+      }
+    ],
+    "name": "createCollection",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "payable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "name", "type": "string" },
+      { "internalType": "uint8", "name": "decimals", "type": "uint8" },
+      { "internalType": "string", "name": "description", "type": "string" },
+      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
+    ],
+    "name": "createFTCollection",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "payable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "name", "type": "string" },
+      { "internalType": "string", "name": "description", "type": "string" },
+      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
+    ],
+    "name": "createNFTCollection",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "payable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "name", "type": "string" },
+      { "internalType": "string", "name": "description", "type": "string" },
+      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
+    ],
+    "name": "createRFTCollection",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "payable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "collectionAddress",
+        "type": "address"
+      }
+    ],
+    "name": "destroyCollection",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "collectionAddress",
+        "type": "address"
+      }
+    ],
+    "name": "isCollectionExist",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "collection", "type": "address" },
+      { "internalType": "string", "name": "baseUri", "type": "string" }
+    ],
+    "name": "makeCollectionERC721MetadataCompatible",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/contractHelpers.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/contractHelpers.json
@@ -0,0 +1,326 @@
+[
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "ContractSponsorRemoved",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "address",
+        "name": "sponsor",
+        "type": "address"
+      }
+    ],
+    "name": "ContractSponsorSet",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "address",
+        "name": "sponsor",
+        "type": "address"
+      }
+    ],
+    "name": "ContractSponsorshipConfirmed",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "allowed",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "allowlistEnabled",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "confirmSponsorship",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "contractOwner",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "hasPendingSponsor",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "hasSponsor",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "removeSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "selfSponsoredEnable",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "address", "name": "sponsor", "type": "address" }
+    ],
+    "name": "setSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "uint256", "name": "feeLimit", "type": "uint256" }
+    ],
+    "name": "setSponsoringFeeLimit",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      {
+        "internalType": "enum SponsoringModeT",
+        "name": "mode",
+        "type": "uint8"
+      }
+    ],
+    "name": "setSponsoringMode",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "uint32", "name": "rateLimit", "type": "uint32" }
+    ],
+    "name": "setSponsoringRateLimit",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "sponsor",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "status", "type": "bool" },
+          {
+            "components": [
+              { "internalType": "address", "name": "eth", "type": "address" },
+              { "internalType": "uint256", "name": "sub", "type": "uint256" }
+            ],
+            "internalType": "struct CrossAddress",
+            "name": "value",
+            "type": "tuple"
+          }
+        ],
+        "internalType": "struct OptionCrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "sponsoringEnabled",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "sponsoringFeeLimit",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "sponsoringRateLimit",
+    "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "address", "name": "user", "type": "address" },
+      { "internalType": "bool", "name": "isAllowed", "type": "bool" }
+    ],
+    "name": "toggleAllowed",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "bool", "name": "enabled", "type": "bool" }
+    ],
+    "name": "toggleAllowlist",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/fungible.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/fungible.json
@@ -0,0 +1,722 @@
+[
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "spender",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "from",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "to",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "newAdmin",
+        "type": "tuple"
+      }
+    ],
+    "name": "addCollectionAdminCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "addToCollectionAllowListCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "address", "name": "spender", "type": "address" }
+    ],
+    "name": "allowance",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "spender",
+        "type": "tuple"
+      }
+    ],
+    "name": "allowanceCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "allowlistedCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "spender", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "spender",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "approveCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" }
+    ],
+    "name": "balanceOf",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "burnFromCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "newOwner",
+        "type": "tuple"
+      }
+    ],
+    "name": "changeCollectionOwnerCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionAdmins",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionHelperAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionLimits",
+    "outputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionLimitField",
+            "name": "field",
+            "type": "uint8"
+          },
+          {
+            "components": [
+              { "internalType": "bool", "name": "status", "type": "bool" },
+              { "internalType": "uint256", "name": "value", "type": "uint256" }
+            ],
+            "internalType": "struct OptionUint256",
+            "name": "value",
+            "type": "tuple"
+          }
+        ],
+        "internalType": "struct CollectionLimit[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNesting",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          {
+            "internalType": "bool",
+            "name": "collection_admin",
+            "type": "bool"
+          },
+          {
+            "internalType": "address[]",
+            "name": "restricted",
+            "type": "address[]"
+          }
+        ],
+        "internalType": "struct CollectionNestingAndPermission",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionOwner",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "collectionProperties",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "collectionProperty",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionSponsor",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "confirmCollectionSponsorship",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "contractAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "decimals",
+    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "deleteCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "description",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "hasCollectionPendingSponsor",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "isOwnerOrAdminCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "mint",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "to", "type": "address" },
+          { "internalType": "uint256", "name": "amount", "type": "uint256" }
+        ],
+        "internalType": "struct AmountForAddress[]",
+        "name": "amounts",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintBulk",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "mintCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "admin",
+        "type": "tuple"
+      }
+    ],
+    "name": "removeCollectionAdminCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "removeCollectionSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "removeFromCollectionAllowListCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
+    ],
+    "name": "setCollectionAccess",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionLimitField",
+            "name": "field",
+            "type": "uint8"
+          },
+          {
+            "components": [
+              { "internalType": "bool", "name": "status", "type": "bool" },
+              { "internalType": "uint256", "name": "value", "type": "uint256" }
+            ],
+            "internalType": "struct OptionUint256",
+            "name": "value",
+            "type": "tuple"
+          }
+        ],
+        "internalType": "struct CollectionLimit",
+        "name": "limit",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionLimit",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],
+    "name": "setCollectionMintMode",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          {
+            "internalType": "bool",
+            "name": "collection_admin",
+            "type": "bool"
+          },
+          {
+            "internalType": "address[]",
+            "name": "restricted",
+            "type": "address[]"
+          }
+        ],
+        "internalType": "struct CollectionNestingAndPermission",
+        "name": "collectionNestingAndPermissions",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "properties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "setCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "sponsor",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionSponsorCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferFromCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "uniqueCollectionType",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/fungibleDeprecated.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/fungibleDeprecated.json
@@ -0,0 +1,151 @@
+[
+  {
+    "inputs": [
+      { "internalType": "address", "name": "newAdmin", "type": "address" }
+    ],
+    "name": "addCollectionAdmin",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "addToCollectionAllowList",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "burnFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "newOwner", "type": "address" }
+    ],
+    "name": "changeCollectionOwner",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "deleteCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "isOwnerOrAdmin",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "admin", "type": "address" }
+    ],
+    "name": "removeCollectionAdmin",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "removeFromCollectionAllowList",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "sponsor", "type": "address" }
+    ],
+    "name": "setCollectionSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNestingPermissions",
+    "outputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionPermissionField",
+            "name": "field",
+            "type": "uint8"
+          },
+          { "internalType": "bool", "name": "value", "type": "bool" }
+        ],
+        "internalType": "struct CollectionNestingPermission[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNestingRestrictedCollectionIds",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }
+        ],
+        "internalType": "struct CollectionNesting",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bool", "name": "enable", "type": "bool" },
+      {
+        "internalType": "address[]",
+        "name": "collections",
+        "type": "address[]"
+      }
+    ],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/nativeFungible.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/nativeFungible.json
@@ -0,0 +1,201 @@
+[
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "spender",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "from",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "to",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "address", "name": "spender", "type": "address" }
+    ],
+    "name": "allowance",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "spender", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" }
+    ],
+    "name": "balanceOf",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "decimals",
+    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferFromCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/nonFungible.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/nonFungible.json
@@ -0,0 +1,988 @@
+[
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "approved",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "operator",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "bool",
+        "name": "approved",
+        "type": "bool"
+      }
+    ],
+    "name": "ApprovalForAll",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "TokenChanged",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "from",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "to",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "newAdmin",
+        "type": "tuple"
+      }
+    ],
+    "name": "addCollectionAdminCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "addToCollectionAllowListCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "allowlistedCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "approved", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "approved",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "approveCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" }
+    ],
+    "name": "balanceOf",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burn",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burnFromCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "newOwner",
+        "type": "tuple"
+      }
+    ],
+    "name": "changeCollectionOwnerCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionAdmins",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionHelperAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionLimits",
+    "outputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionLimitField",
+            "name": "field",
+            "type": "uint8"
+          },
+          {
+            "components": [
+              { "internalType": "bool", "name": "status", "type": "bool" },
+              { "internalType": "uint256", "name": "value", "type": "uint256" }
+            ],
+            "internalType": "struct OptionUint256",
+            "name": "value",
+            "type": "tuple"
+          }
+        ],
+        "internalType": "struct CollectionLimit[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNesting",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          {
+            "internalType": "bool",
+            "name": "collection_admin",
+            "type": "bool"
+          },
+          {
+            "internalType": "address[]",
+            "name": "restricted",
+            "type": "address[]"
+          }
+        ],
+        "internalType": "struct CollectionNestingAndPermission",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionOwner",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "collectionProperties",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "collectionProperty",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionSponsor",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "confirmCollectionSponsorship",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "contractAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "deleteCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "deleteProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "description",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "getApproved",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "hasCollectionPendingSponsor",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "address", "name": "operator", "type": "address" }
+    ],
+    "name": "isApprovedForAll",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "isOwnerOrAdminCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],
+    "name": "mint",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          {
+            "components": [
+              { "internalType": "address", "name": "eth", "type": "address" },
+              { "internalType": "uint256", "name": "sub", "type": "uint256" }
+            ],
+            "internalType": "struct CrossAddress",
+            "name": "owner",
+            "type": "tuple"
+          },
+          {
+            "components": [
+              { "internalType": "string", "name": "key", "type": "string" },
+              { "internalType": "bytes", "name": "value", "type": "bytes" }
+            ],
+            "internalType": "struct Property[]",
+            "name": "properties",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct MintTokenData[]",
+        "name": "data",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintBulkCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "properties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "string", "name": "tokenUri", "type": "string" }
+    ],
+    "name": "mintWithTokenURI",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "nextTokenId",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "ownerOf",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "ownerOfCross",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "properties",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" }
+    ],
+    "name": "property",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "admin",
+        "type": "tuple"
+      }
+    ],
+    "name": "removeCollectionAdminCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "removeCollectionSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "removeFromCollectionAllowListCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "safeTransferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "bytes", "name": "data", "type": "bytes" }
+    ],
+    "name": "safeTransferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "operator", "type": "address" },
+      { "internalType": "bool", "name": "approved", "type": "bool" }
+    ],
+    "name": "setApprovalForAll",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
+    ],
+    "name": "setCollectionAccess",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionLimitField",
+            "name": "field",
+            "type": "uint8"
+          },
+          {
+            "components": [
+              { "internalType": "bool", "name": "status", "type": "bool" },
+              { "internalType": "uint256", "name": "value", "type": "uint256" }
+            ],
+            "internalType": "struct OptionUint256",
+            "name": "value",
+            "type": "tuple"
+          }
+        ],
+        "internalType": "struct CollectionLimit",
+        "name": "limit",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionLimit",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],
+    "name": "setCollectionMintMode",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          {
+            "internalType": "bool",
+            "name": "collection_admin",
+            "type": "bool"
+          },
+          {
+            "internalType": "address[]",
+            "name": "restricted",
+            "type": "address[]"
+          }
+        ],
+        "internalType": "struct CollectionNestingAndPermission",
+        "name": "collectionNestingAndPermissions",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "properties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "setCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "sponsor",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionSponsorCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "properties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "setProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum TokenPermissionField",
+                "name": "code",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "value", "type": "bool" }
+            ],
+            "internalType": "struct PropertyPermission[]",
+            "name": "permissions",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct TokenPropertyPermission[]",
+        "name": "permissions",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "setTokenPropertyPermissions",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "index", "type": "uint256" }
+    ],
+    "name": "tokenByIndex",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "uint256", "name": "index", "type": "uint256" }
+    ],
+    "name": "tokenOfOwnerByIndex",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "tokenPropertyPermissions",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum TokenPermissionField",
+                "name": "code",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "value", "type": "bool" }
+            ],
+            "internalType": "struct PropertyPermission[]",
+            "name": "permissions",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct TokenPropertyPermission[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "tokenURI",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transferCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transferFromCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "uniqueCollectionType",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/nonFungibleDeprecated.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/nonFungibleDeprecated.json
@@ -0,0 +1,172 @@
+[
+  {
+    "inputs": [
+      { "internalType": "address", "name": "newAdmin", "type": "address" }
+    ],
+    "name": "addCollectionAdmin",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "addToCollectionAllowList",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burnFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "deleteCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "isOwnerOrAdmin",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "admin", "type": "address" }
+    ],
+    "name": "removeCollectionAdmin",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "removeFromCollectionAllowList",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "sponsor", "type": "address" }
+    ],
+    "name": "setCollectionSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "newOwner", "type": "address" }
+    ],
+    "name": "changeCollectionOwner",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" }
+    ],
+    "name": "deleteProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNestingPermissions",
+    "outputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionPermissionField",
+            "name": "field",
+            "type": "uint8"
+          },
+          { "internalType": "bool", "name": "value", "type": "bool" }
+        ],
+        "internalType": "struct CollectionNestingPermission[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNestingRestrictedCollectionIds",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }
+        ],
+        "internalType": "struct CollectionNesting",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bool", "name": "enable", "type": "bool" },
+      {
+        "internalType": "address[]",
+        "name": "collections",
+        "type": "address[]"
+      }
+    ],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/reFungible.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/reFungible.json
@@ -0,0 +1,995 @@
+[
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "approved",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "operator",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "bool",
+        "name": "approved",
+        "type": "bool"
+      }
+    ],
+    "name": "ApprovalForAll",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "TokenChanged",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "from",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "to",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "newAdmin",
+        "type": "tuple"
+      }
+    ],
+    "name": "addCollectionAdminCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "addToCollectionAllowListCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "allowlistedCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "approved", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" }
+    ],
+    "name": "balanceOf",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burn",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burnFromCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "newOwner",
+        "type": "tuple"
+      }
+    ],
+    "name": "changeCollectionOwnerCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionAdmins",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionHelperAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionLimits",
+    "outputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionLimitField",
+            "name": "field",
+            "type": "uint8"
+          },
+          {
+            "components": [
+              { "internalType": "bool", "name": "status", "type": "bool" },
+              { "internalType": "uint256", "name": "value", "type": "uint256" }
+            ],
+            "internalType": "struct OptionUint256",
+            "name": "value",
+            "type": "tuple"
+          }
+        ],
+        "internalType": "struct CollectionLimit[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNesting",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          {
+            "internalType": "bool",
+            "name": "collection_admin",
+            "type": "bool"
+          },
+          {
+            "internalType": "address[]",
+            "name": "restricted",
+            "type": "address[]"
+          }
+        ],
+        "internalType": "struct CollectionNestingAndPermission",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionOwner",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "collectionProperties",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "collectionProperty",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionSponsor",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "confirmCollectionSponsorship",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "contractAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "deleteCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "deleteProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "description",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "getApproved",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "hasCollectionPendingSponsor",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "address", "name": "operator", "type": "address" }
+    ],
+    "name": "isApprovedForAll",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "isOwnerOrAdminCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],
+    "name": "mint",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          {
+            "components": [
+              {
+                "components": [
+                  {
+                    "internalType": "address",
+                    "name": "eth",
+                    "type": "address"
+                  },
+                  {
+                    "internalType": "uint256",
+                    "name": "sub",
+                    "type": "uint256"
+                  }
+                ],
+                "internalType": "struct CrossAddress",
+                "name": "owner",
+                "type": "tuple"
+              },
+              { "internalType": "uint128", "name": "pieces", "type": "uint128" }
+            ],
+            "internalType": "struct OwnerPieces[]",
+            "name": "owners",
+            "type": "tuple[]"
+          },
+          {
+            "components": [
+              { "internalType": "string", "name": "key", "type": "string" },
+              { "internalType": "bytes", "name": "value", "type": "bytes" }
+            ],
+            "internalType": "struct Property[]",
+            "name": "properties",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct MintTokenData[]",
+        "name": "tokenProperties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintBulkCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "properties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "string", "name": "tokenUri", "type": "string" }
+    ],
+    "name": "mintWithTokenURI",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "nextTokenId",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "ownerOf",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "ownerOfCross",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "properties",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" }
+    ],
+    "name": "property",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "admin",
+        "type": "tuple"
+      }
+    ],
+    "name": "removeCollectionAdminCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "removeCollectionSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "user",
+        "type": "tuple"
+      }
+    ],
+    "name": "removeFromCollectionAllowListCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "safeTransferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "bytes", "name": "data", "type": "bytes" }
+    ],
+    "name": "safeTransferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "operator", "type": "address" },
+      { "internalType": "bool", "name": "approved", "type": "bool" }
+    ],
+    "name": "setApprovalForAll",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
+    ],
+    "name": "setCollectionAccess",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionLimitField",
+            "name": "field",
+            "type": "uint8"
+          },
+          {
+            "components": [
+              { "internalType": "bool", "name": "status", "type": "bool" },
+              { "internalType": "uint256", "name": "value", "type": "uint256" }
+            ],
+            "internalType": "struct OptionUint256",
+            "name": "value",
+            "type": "tuple"
+          }
+        ],
+        "internalType": "struct CollectionLimit",
+        "name": "limit",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionLimit",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],
+    "name": "setCollectionMintMode",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          {
+            "internalType": "bool",
+            "name": "collection_admin",
+            "type": "bool"
+          },
+          {
+            "internalType": "address[]",
+            "name": "restricted",
+            "type": "address[]"
+          }
+        ],
+        "internalType": "struct CollectionNestingAndPermission",
+        "name": "collectionNestingAndPermissions",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "properties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "setCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "sponsor",
+        "type": "tuple"
+      }
+    ],
+    "name": "setCollectionSponsorCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          { "internalType": "bytes", "name": "value", "type": "bytes" }
+        ],
+        "internalType": "struct Property[]",
+        "name": "properties",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "setProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum TokenPermissionField",
+                "name": "code",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "value", "type": "bool" }
+            ],
+            "internalType": "struct PropertyPermission[]",
+            "name": "permissions",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct TokenPropertyPermission[]",
+        "name": "permissions",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "setTokenPropertyPermissions",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "index", "type": "uint256" }
+    ],
+    "name": "tokenByIndex",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "token", "type": "uint256" }
+    ],
+    "name": "tokenContractAddress",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "uint256", "name": "index", "type": "uint256" }
+    ],
+    "name": "tokenOfOwnerByIndex",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "tokenPropertyPermissions",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "key", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum TokenPermissionField",
+                "name": "code",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "value", "type": "bool" }
+            ],
+            "internalType": "struct PropertyPermission[]",
+            "name": "permissions",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct TokenPropertyPermission[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "tokenURI",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transferCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transferFromCross",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "uniqueCollectionType",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/reFungibleDeprecated.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/reFungibleDeprecated.json
@@ -0,0 +1,200 @@
+[
+  {
+    "inputs": [
+      { "internalType": "address", "name": "newAdmin", "type": "address" }
+    ],
+    "name": "addCollectionAdmin",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "addToCollectionAllowList",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burnFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "deleteCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "isOwnerOrAdmin",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "admin", "type": "address" }
+    ],
+    "name": "removeCollectionAdmin",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "removeFromCollectionAllowList",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "sponsor", "type": "address" }
+    ],
+    "name": "setCollectionSponsor",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "newOwner", "type": "address" }
+    ],
+    "name": "changeCollectionOwner",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" }
+    ],
+    "name": "deleteProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" }
+    ],
+    "name": "mintBulk",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      {
+        "components": [
+          { "internalType": "uint256", "name": "field_0", "type": "uint256" },
+          { "internalType": "string", "name": "field_1", "type": "string" }
+        ],
+        "internalType": "struct Tuple0[]",
+        "name": "tokens",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintBulkWithTokenURI",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNestingPermissions",
+    "outputs": [
+      {
+        "components": [
+          {
+            "internalType": "enum CollectionPermissionField",
+            "name": "field",
+            "type": "uint8"
+          },
+          { "internalType": "bool", "name": "value", "type": "bool" }
+        ],
+        "internalType": "struct CollectionNestingPermission[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "collectionNestingRestrictedCollectionIds",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "bool", "name": "token_owner", "type": "bool" },
+          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }
+        ],
+        "internalType": "struct CollectionNesting",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bool", "name": "enable", "type": "bool" },
+      {
+        "internalType": "address[]",
+        "name": "collections",
+        "type": "address[]"
+      }
+    ],
+    "name": "setCollectionNesting",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/reFungibleToken.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/reFungibleToken.json
@@ -0,0 +1,286 @@
+[
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "spender",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "from",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "to",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "address", "name": "spender", "type": "address" }
+    ],
+    "name": "allowance",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "spender",
+        "type": "tuple"
+      }
+    ],
+    "name": "allowanceCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "spender", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "spender",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "approveCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" }
+    ],
+    "name": "balanceOf",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "burnFromCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "decimals",
+    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "parentToken",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "parentTokenId",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "repartition",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "from",
+        "type": "tuple"
+      },
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "to",
+        "type": "tuple"
+      },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferFromCross",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/abi/reFungibleTokenDeprecated.jsondiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/abi/reFungibleTokenDeprecated.json
@@ -0,0 +1,12 @@
+[
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "burnFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
addedjs-packages/tests/eth/allowlist.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/allowlist.test.ts
@@ -0,0 +1,212 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../util/index.js';
+import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from './util/index.js';
+import {CreateCollectionData} from './util/playgrounds/types.js';
+
+describe('EVM contract allowlist', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Contract allowlist can be toggled', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const flipper = await helper.eth.deployFlipper(owner);
+    const helpers = helper.ethNativeContract.contractHelpers(owner);
+
+    // Any user is allowed by default
+    expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false;
+
+    // Enable
+    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+    expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.true;
+
+    // Disable
+    await helpers.methods.toggleAllowlist(flipper.options.address, false).send({from: owner});
+    expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false;
+  });
+
+  itEth('Non-allowlisted user can\'t call contract with allowlist enabled', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const flipper = await helper.eth.deployFlipper(owner);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+
+    // User can flip with allowlist disabled
+    await flipper.methods.flip().send({from: caller});
+    expect(await flipper.methods.getValue().call()).to.be.true;
+
+    // Tx will be reverted if user is not in allowlist
+    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+    await expect(flipper.methods.flip().send({from: caller})).to.rejected;
+    expect(await flipper.methods.getValue().call()).to.be.true;
+
+    // Adding caller to allowlist will make contract callable again
+    await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
+    await flipper.methods.flip().send({from: caller});
+    expect(await flipper.methods.getValue().call()).to.be.false;
+  });
+});
+
+describe('EVM collection allowlist', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  // Soft-deprecated
+  itEth('Collection allowlist can be added and removed by [eth] address', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const user = helper.eth.createAccount();
+    const crossUser = helper.ethCrossAccount.fromAddress(user);
+
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;
+    await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
+    expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.true;
+
+    await collectionEvm.methods.removeFromCollectionAllowList(user).send({from: owner});
+    expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;
+  });
+
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase =>
+    itEth.ifWithPallets(`Collection allowlist can be added and removed by [cross] address for ${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {
+      const owner = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
+      const [userSub] = await helper.arrange.createAccounts([10n], donor);
+      const userEth = await helper.eth.createAccountWithBalance(donor);
+      const mintParams = testCase.mode === 'ft' ? [userEth, 100] : [userEth];
+
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+      const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);
+      const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);
+      const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);
+
+      // Can addToCollectionAllowListCross:
+      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;
+      await collectionEvm.methods.addToCollectionAllowListCross(userCrossSub).send({from: owner});
+      await collectionEvm.methods.addToCollectionAllowListCross(userCrossEth).send({from: owner});
+      await collectionEvm.methods.addToCollectionAllowListCross(ownerCrossEth).send({from: owner});
+
+      // Accounts are in allowed list:
+      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;
+      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;
+      expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.true;
+      expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.true;
+
+      await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #1
+      await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #2
+      await collectionEvm.methods.setCollectionAccess(SponsoringMode.Allowlisted).send({from: owner});
+
+      // allowlisted account can transfer and transferCross from eth:
+      await collectionEvm.methods.transfer(owner, 1).send({from: userEth});
+      await collectionEvm.methods.transferCross(userCrossSub, 2).send({from: userEth});
+
+      if(testCase.mode === 'ft') {
+        expect(await helper.ft.getBalance(collectionId, {Ethereum: owner})).to.eq(1n);
+        expect(await helper.ft.getBalance(collectionId, {Substrate: userSub.address})).to.eq(2n);
+      } else {
+        expect(await helper.nft.getTokenOwner(collectionId, 1)).to.deep.eq({Ethereum: owner});
+        expect(await helper.nft.getTokenOwner(collectionId, 2)).to.deep.eq({Substrate: userSub.address});
+      }
+
+      // allowlisted cross substrate accounts can transfer from Substrate:
+      testCase.mode === 'ft'
+        ? await helper.ft.transfer(userSub, collectionId, {Ethereum: userEth}, 2n)
+        : await helper.collection.transferToken(userSub, collectionId, 2, {Ethereum: userEth});
+
+      // can removeFromCollectionAllowListCross:
+      await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossSub).send({from: owner});
+      await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossEth).send({from: owner});
+      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;
+      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false;
+      expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.false;
+      expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.false;
+
+      // cannot transfer anymore
+      await collectionEvm.methods.mint(...mintParams).send({from: owner});
+      await expect(collectionEvm.methods.transfer(owner, 2).send({from: userEth})).to.be.rejectedWith(/Transaction has been reverted/);
+    }));
+
+  [
+    // cross-methods
+    {mode: 'nft' as const, cross: true, requiredPallets: []},
+    {mode: 'rft' as const, cross: true, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, cross: true, requiredPallets: []},
+    // soft-deprecated
+    {mode: 'nft' as const, cross: false, requiredPallets: []},
+    {mode: 'rft' as const, cross: false, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, cross: false, requiredPallets: []},
+  ].map(testCase =>
+    itEth.ifWithPallets(`Non-owner cannot add or remove from collection allowlist ${testCase.cross ? 'cross ' : ''}${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {
+      // Select methods:
+      const addToAllowList = testCase.cross ? 'addToCollectionAllowListCross' : 'addToCollectionAllowList';
+      const removeFromAllowList = testCase.cross ? 'removeFromCollectionAllowListCross' : 'removeFromCollectionAllowList';
+
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const notOwner = await helper.eth.createAccountWithBalance(donor);
+      const userSub = donor;
+      const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);
+      const userEth = helper.eth.createAccount();
+      const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);
+
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, !testCase.cross);
+
+      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;
+      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false;
+
+      // 1. notOwner cannot add to allow list:
+      // 1.1 plain ethereum or cross address:
+      await expect(collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+      // 1.2 cross-substrate address:
+      if(testCase.cross)
+        await expect(collectionEvm.methods[addToAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+
+      // 2. owner can add to allow list:
+      // 2.1 plain ethereum or cross address:
+      await collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).send({from: owner});
+      // 2.2 cross-substrate address:
+      if(testCase.cross) {
+        await collectionEvm.methods[addToAllowList](userCrossSub).send({from: owner});
+        expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;
+      }
+      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;
+
+      // 3. notOwner cannot remove from allow list:
+      // 3.1 plain ethereum or cross address:
+      await expect(collectionEvm.methods[removeFromAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+      // 3.2 cross-substrate address:
+      if(testCase.cross)
+        await expect(collectionEvm.methods[removeFromAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    }));
+});
addedjs-packages/tests/eth/api/CollectionHelpers.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/api/CollectionHelpers.sol
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @dev common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+	function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+/// @dev inlined interface
+interface CollectionHelpersEvents {
+	event CollectionCreated(address indexed owner, address indexed collectionId);
+	event CollectionDestroyed(address indexed collectionId);
+	event CollectionChanged(address indexed collectionId);
+}
+
+/// @title Contract, which allows users to operate with collections
+/// @dev the ERC-165 identifier for this interface is 0x94e5af0d
+interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
+	/// Create a collection
+	/// @return address Address of the newly created collection
+	/// @dev EVM selector for this function is: 0x72b5bea7,
+	///  or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8))
+	function createCollection(CreateCollectionData memory data) external payable returns (address);
+
+	/// Create an NFT collection
+	/// @param name Name of the collection
+	/// @param description Informative description of the collection
+	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
+	/// @return address Address of the newly created collection
+	/// @dev EVM selector for this function is: 0x844af658,
+	///  or in textual repr: createNFTCollection(string,string,string)
+	function createNFTCollection(
+		string memory name,
+		string memory description,
+		string memory tokenPrefix
+	) external payable returns (address);
+
+	// /// Create an NFT collection
+	// /// @param name Name of the collection
+	// /// @param description Informative description of the collection
+	// /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
+	// /// @return address Address of the newly created collection
+	// /// @dev EVM selector for this function is: 0xe34a6844,
+	// ///  or in textual repr: createNonfungibleCollection(string,string,string)
+	// function createNonfungibleCollection(string memory name, string memory description, string memory tokenPrefix) external payable returns (address);
+
+	/// @dev EVM selector for this function is: 0xab173450,
+	///  or in textual repr: createRFTCollection(string,string,string)
+	function createRFTCollection(
+		string memory name,
+		string memory description,
+		string memory tokenPrefix
+	) external payable returns (address);
+
+	/// @dev EVM selector for this function is: 0x7335b79f,
+	///  or in textual repr: createFTCollection(string,uint8,string,string)
+	function createFTCollection(
+		string memory name,
+		uint8 decimals,
+		string memory description,
+		string memory tokenPrefix
+	) external payable returns (address);
+
+	/// @dev EVM selector for this function is: 0x85624258,
+	///  or in textual repr: makeCollectionERC721MetadataCompatible(address,string)
+	function makeCollectionERC721MetadataCompatible(address collection, string memory baseUri) external;
+
+	/// @dev EVM selector for this function is: 0x564e321f,
+	///  or in textual repr: destroyCollection(address)
+	function destroyCollection(address collectionAddress) external;
+
+	/// Check if a collection exists
+	/// @param collectionAddress Address of the collection in question
+	/// @return bool Does the collection exist?
+	/// @dev EVM selector for this function is: 0xc3de1494,
+	///  or in textual repr: isCollectionExist(address)
+	function isCollectionExist(address collectionAddress) external view returns (bool);
+
+	/// @dev EVM selector for this function is: 0xd23a7ab1,
+	///  or in textual repr: collectionCreationFee()
+	function collectionCreationFee() external view returns (uint256);
+
+	/// Returns address of a collection.
+	/// @param collectionId  - CollectionId  of the collection
+	/// @return eth mirror address of the collection
+	/// @dev EVM selector for this function is: 0x2e716683,
+	///  or in textual repr: collectionAddress(uint32)
+	function collectionAddress(uint32 collectionId) external view returns (address);
+
+	/// Returns collectionId of a collection.
+	/// @param collectionAddress  - Eth address of the collection
+	/// @return collectionId of the collection
+	/// @dev EVM selector for this function is: 0xb5cb7498,
+	///  or in textual repr: collectionId(address)
+	function collectionId(address collectionAddress) external view returns (uint32);
+}
+
+/// Collection properties
+struct CreateCollectionData {
+	/// Collection name
+	string name;
+	/// Collection description
+	string description;
+	/// Token prefix
+	string token_prefix;
+	/// Token type (NFT, FT or RFT)
+	CollectionMode mode;
+	/// Fungible token precision
+	uint8 decimals;
+	/// Custom Properties
+	Property[] properties;
+	/// Permissions for token properties
+	TokenPropertyPermission[] token_property_permissions;
+	/// Collection admins
+	CrossAddress[] admin_list;
+	/// Nesting settings
+	CollectionNestingAndPermission nesting_settings;
+	/// Collection limits
+	CollectionLimitValue[] limits;
+	/// Collection sponsor
+	CrossAddress pending_sponsor;
+	/// Extra collection flags
+	CollectionFlags flags;
+}
+
+type CollectionFlags is uint8;
+
+library CollectionFlagsLib {
+	/// Tokens in foreign collections can be transferred, but not burnt
+	CollectionFlags constant foreignField = CollectionFlags.wrap(128);
+	/// Supports ERC721Metadata
+	CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);
+	/// External collections can't be managed using `unique` api
+	CollectionFlags constant externalField = CollectionFlags.wrap(1);
+
+	/// Reserved flags
+	function reservedField(uint8 value) public pure returns (CollectionFlags) {
+		require(value < 1 << 5, "out of bound value");
+		return CollectionFlags.wrap(value << 1);
+	}
+}
+
+/// Cross account struct
+struct CrossAddress {
+	address eth;
+	uint256 sub;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
+struct CollectionLimitValue {
+	CollectionLimitField field;
+	uint256 value;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
+enum CollectionLimitField {
+	/// How many tokens can a user have on one account.
+	AccountTokenOwnership,
+	/// How many bytes of data are available for sponsorship.
+	SponsoredDataSize,
+	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
+	SponsoredDataRateLimit,
+	/// How many tokens can be mined into this collection.
+	TokenLimit,
+	/// Timeouts for transfer sponsoring.
+	SponsorTransferTimeout,
+	/// Timeout for sponsoring an approval in passed blocks.
+	SponsorApproveTimeout,
+	/// Whether the collection owner of the collection can send tokens (which belong to other users).
+	OwnerCanTransfer,
+	/// Can the collection owner burn other people's tokens.
+	OwnerCanDestroy,
+	/// Is it possible to send tokens from this collection between users.
+	TransferEnabled
+}
+
+/// Nested collections and permissions
+struct CollectionNestingAndPermission {
+	/// Owner of token can nest tokens under it.
+	bool token_owner;
+	/// Admin of token collection can nest tokens under token.
+	bool collection_admin;
+	/// If set - only tokens from specified collections can be nested.
+	address[] restricted;
+}
+
+/// Ethereum representation of Token Property Permissions.
+struct TokenPropertyPermission {
+	/// Token property key.
+	string key;
+	/// Token property permissions.
+	PropertyPermission[] permissions;
+}
+
+/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.
+struct PropertyPermission {
+	/// TokenPermission field.
+	TokenPermissionField code;
+	/// TokenPermission value.
+	bool value;
+}
+
+/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.
+enum TokenPermissionField {
+	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]
+	Mutable,
+	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]
+	TokenOwner,
+	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]
+	CollectionAdmin
+}
+
+/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
+struct Property {
+	string key;
+	bytes value;
+}
+
+/// Type of tokens in collection
+enum CollectionMode {
+	/// Nonfungible
+	Nonfungible,
+	/// Fungible
+	Fungible,
+	/// Refungible
+	Refungible
+}
addedjs-packages/tests/eth/api/ContractHelpers.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/api/ContractHelpers.sol
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @dev common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+	function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+/// @dev inlined interface
+interface ContractHelpersEvents {
+	event ContractSponsorSet(address indexed contractAddress, address sponsor);
+	event ContractSponsorshipConfirmed(address indexed contractAddress, address sponsor);
+	event ContractSponsorRemoved(address indexed contractAddress);
+}
+
+/// @title Magic contract, which allows users to reconfigure other contracts
+/// @dev the ERC-165 identifier for this interface is 0x30afad04
+interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents {
+	/// Get user, which deployed specified contract
+	/// @dev May return zero address in case if contract is deployed
+	///  using uniquenetwork evm-migration pallet, or using other terms not
+	///  intended by pallet-evm
+	/// @dev Returns zero address if contract does not exists
+	/// @param contractAddress Contract to get owner of
+	/// @return address Owner of contract
+	/// @dev EVM selector for this function is: 0x5152b14c,
+	///  or in textual repr: contractOwner(address)
+	function contractOwner(address contractAddress) external view returns (address);
+
+	/// Set sponsor.
+	/// @param contractAddress Contract for which a sponsor is being established.
+	/// @param sponsor User address who set as pending sponsor.
+	/// @dev EVM selector for this function is: 0xf01fba93,
+	///  or in textual repr: setSponsor(address,address)
+	function setSponsor(address contractAddress, address sponsor) external;
+
+	/// Set contract as self sponsored.
+	///
+	/// @param contractAddress Contract for which a self sponsoring is being enabled.
+	/// @dev EVM selector for this function is: 0x89f7d9ae,
+	///  or in textual repr: selfSponsoredEnable(address)
+	function selfSponsoredEnable(address contractAddress) external;
+
+	/// Remove sponsor.
+	///
+	/// @param contractAddress Contract for which a sponsorship is being removed.
+	/// @dev EVM selector for this function is: 0xef784250,
+	///  or in textual repr: removeSponsor(address)
+	function removeSponsor(address contractAddress) external;
+
+	/// Confirm sponsorship.
+	///
+	/// @dev Caller must be same that set via [`setSponsor`].
+	///
+	/// @param contractAddress Сontract for which need to confirm sponsorship.
+	/// @dev EVM selector for this function is: 0xabc00001,
+	///  or in textual repr: confirmSponsorship(address)
+	function confirmSponsorship(address contractAddress) external;
+
+	/// Get current sponsor.
+	///
+	/// @param contractAddress The contract for which a sponsor is requested.
+	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	/// @dev EVM selector for this function is: 0x766c4f37,
+	///  or in textual repr: sponsor(address)
+	function sponsor(address contractAddress) external view returns (OptionCrossAddress memory);
+
+	/// Check tat contract has confirmed sponsor.
+	///
+	/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.
+	/// @return **true** if contract has confirmed sponsor.
+	/// @dev EVM selector for this function is: 0x97418603,
+	///  or in textual repr: hasSponsor(address)
+	function hasSponsor(address contractAddress) external view returns (bool);
+
+	/// Check tat contract has pending sponsor.
+	///
+	/// @param contractAddress The contract for which the presence of a pending sponsor is checked.
+	/// @return **true** if contract has pending sponsor.
+	/// @dev EVM selector for this function is: 0x39b9b242,
+	///  or in textual repr: hasPendingSponsor(address)
+	function hasPendingSponsor(address contractAddress) external view returns (bool);
+
+	/// @dev EVM selector for this function is: 0x6027dc61,
+	///  or in textual repr: sponsoringEnabled(address)
+	function sponsoringEnabled(address contractAddress) external view returns (bool);
+
+	/// @dev EVM selector for this function is: 0xfde8a560,
+	///  or in textual repr: setSponsoringMode(address,uint8)
+	function setSponsoringMode(address contractAddress, SponsoringModeT mode) external;
+
+	/// Get current contract sponsoring rate limit
+	/// @param contractAddress Contract to get sponsoring rate limit of
+	/// @return uint32 Amount of blocks between two sponsored transactions
+	/// @dev EVM selector for this function is: 0xf29694d8,
+	///  or in textual repr: sponsoringRateLimit(address)
+	function sponsoringRateLimit(address contractAddress) external view returns (uint32);
+
+	/// Set contract sponsoring rate limit
+	/// @dev Sponsoring rate limit - is a minimum amount of blocks that should
+	///  pass between two sponsored transactions
+	/// @param contractAddress Contract to change sponsoring rate limit of
+	/// @param rateLimit Target rate limit
+	/// @dev Only contract owner can change this setting
+	/// @dev EVM selector for this function is: 0x77b6c908,
+	///  or in textual repr: setSponsoringRateLimit(address,uint32)
+	function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) external;
+
+	/// Set contract sponsoring fee limit
+	/// @dev Sponsoring fee limit - is maximum fee that could be spent by
+	///  single transaction
+	/// @param contractAddress Contract to change sponsoring fee limit of
+	/// @param feeLimit Fee limit
+	/// @dev Only contract owner can change this setting
+	/// @dev EVM selector for this function is: 0x03aed665,
+	///  or in textual repr: setSponsoringFeeLimit(address,uint256)
+	function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) external;
+
+	/// Get current contract sponsoring fee limit
+	/// @param contractAddress Contract to get sponsoring fee limit of
+	/// @return uint256 Maximum amount of fee that could be spent by single
+	///  transaction
+	/// @dev EVM selector for this function is: 0x75b73606,
+	///  or in textual repr: sponsoringFeeLimit(address)
+	function sponsoringFeeLimit(address contractAddress) external view returns (uint256);
+
+	/// Is specified user present in contract allow list
+	/// @dev Contract owner always implicitly included
+	/// @param contractAddress Contract to check allowlist of
+	/// @param user User to check
+	/// @return bool Is specified users exists in contract allowlist
+	/// @dev EVM selector for this function is: 0x5c658165,
+	///  or in textual repr: allowed(address,address)
+	function allowed(address contractAddress, address user) external view returns (bool);
+
+	/// Toggle user presence in contract allowlist
+	/// @param contractAddress Contract to change allowlist of
+	/// @param user Which user presence should be toggled
+	/// @param isAllowed `true` if user should be allowed to be sponsored
+	///  or call this contract, `false` otherwise
+	/// @dev Only contract owner can change this setting
+	/// @dev EVM selector for this function is: 0x4706cc1c,
+	///  or in textual repr: toggleAllowed(address,address,bool)
+	function toggleAllowed(
+		address contractAddress,
+		address user,
+		bool isAllowed
+	) external;
+
+	/// Is this contract has allowlist access enabled
+	/// @dev Allowlist always can have users, and it is used for two purposes:
+	///  in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist
+	///  in case of allowlist access enabled, only users from allowlist may call this contract
+	/// @param contractAddress Contract to get allowlist access of
+	/// @return bool Is specified contract has allowlist access enabled
+	/// @dev EVM selector for this function is: 0xc772ef6c,
+	///  or in textual repr: allowlistEnabled(address)
+	function allowlistEnabled(address contractAddress) external view returns (bool);
+
+	/// Toggle contract allowlist access
+	/// @param contractAddress Contract to change allowlist access of
+	/// @param enabled Should allowlist access to be enabled?
+	/// @dev EVM selector for this function is: 0x36de20f5,
+	///  or in textual repr: toggleAllowlist(address,bool)
+	function toggleAllowlist(address contractAddress, bool enabled) external;
+}
+
+/// Available contract sponsoring modes
+enum SponsoringModeT {
+	/// Sponsoring is disabled
+	Disabled,
+	/// Only users from allowlist will be sponsored
+	Allowlisted,
+	/// All users will be sponsored
+	Generous
+}
+
+/// Optional value
+struct OptionCrossAddress {
+	/// Shows the status of accessibility of value
+	bool status;
+	/// Actual value if `status` is true
+	CrossAddress value;
+}
+
+/// Cross account struct
+struct CrossAddress {
+	address eth;
+	uint256 sub;
+}
addedjs-packages/tests/eth/api/UniqueFungible.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/api/UniqueFungible.sol
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @dev common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+	function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xb34d97e9
+interface Collection is Dummy, ERC165 {
+	// /// Set collection property.
+	// ///
+	// /// @param key Property key.
+	// /// @param value Propery value.
+	// /// @dev EVM selector for this function is: 0x2f073f66,
+	// ///  or in textual repr: setCollectionProperty(string,bytes)
+	// function setCollectionProperty(string memory key, bytes memory value) external;
+
+	/// Set collection properties.
+	///
+	/// @param properties Vector of properties key/value pair.
+	/// @dev EVM selector for this function is: 0x50b26b2a,
+	///  or in textual repr: setCollectionProperties((string,bytes)[])
+	function setCollectionProperties(Property[] memory properties) external;
+
+	// /// Delete collection property.
+	// ///
+	// /// @param key Property key.
+	// /// @dev EVM selector for this function is: 0x7b7debce,
+	// ///  or in textual repr: deleteCollectionProperty(string)
+	// function deleteCollectionProperty(string memory key) external;
+
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) external;
+
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
+	function collectionProperty(string memory key) external view returns (bytes memory);
+
+	/// Get collection properties.
+	///
+	/// @param keys Properties keys. Empty keys for all propertyes.
+	/// @return Vector of properties key/value pairs.
+	/// @dev EVM selector for this function is: 0x285fb8e6,
+	///  or in textual repr: collectionProperties(string[])
+	function collectionProperties(string[] memory keys) external view returns (Property[] memory);
+
+	// /// Set the sponsor of the collection.
+	// ///
+	// /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	// ///
+	// /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	// /// @dev EVM selector for this function is: 0x7623402e,
+	// ///  or in textual repr: setCollectionSponsor(address)
+	// function setCollectionSponsor(address sponsor) external;
+
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x84a1d5a8,
+	///  or in textual repr: setCollectionSponsorCross((address,uint256))
+	function setCollectionSponsorCross(CrossAddress memory sponsor) external;
+
+	/// Whether there is a pending sponsor.
+	/// @dev EVM selector for this function is: 0x058ac185,
+	///  or in textual repr: hasCollectionPendingSponsor()
+	function hasCollectionPendingSponsor() external view returns (bool);
+
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
+	function confirmCollectionSponsorship() external;
+
+	/// Remove collection sponsor.
+	/// @dev EVM selector for this function is: 0x6e0326a3,
+	///  or in textual repr: removeCollectionSponsor()
+	function removeCollectionSponsor() external;
+
+	/// Get current sponsor.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	/// @dev EVM selector for this function is: 0x6ec0a9f1,
+	///  or in textual repr: collectionSponsor()
+	function collectionSponsor() external view returns (CrossAddress memory);
+
+	/// Get current collection limits.
+	///
+	/// @return Array of collection limits
+	/// @dev EVM selector for this function is: 0xf63bc572,
+	///  or in textual repr: collectionLimits()
+	function collectionLimits() external view returns (CollectionLimit[] memory);
+
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Some limit.
+	/// @dev EVM selector for this function is: 0x2316ee74,
+	///  or in textual repr: setCollectionLimit((uint8,(bool,uint256)))
+	function setCollectionLimit(CollectionLimit memory limit) external;
+
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
+	function contractAddress() external view returns (address);
+
+	/// Add collection admin.
+	/// @param newAdmin Cross account administrator address.
+	/// @dev EVM selector for this function is: 0x859aa7d6,
+	///  or in textual repr: addCollectionAdminCross((address,uint256))
+	function addCollectionAdminCross(CrossAddress memory newAdmin) external;
+
+	/// Remove collection admin.
+	/// @param admin Cross account administrator address.
+	/// @dev EVM selector for this function is: 0x6c0cd173,
+	///  or in textual repr: removeCollectionAdminCross((address,uint256))
+	function removeCollectionAdminCross(CrossAddress memory admin) external;
+
+	// /// Add collection admin.
+	// /// @param newAdmin Address of the added administrator.
+	// /// @dev EVM selector for this function is: 0x92e462c7,
+	// ///  or in textual repr: addCollectionAdmin(address)
+	// function addCollectionAdmin(address newAdmin) external;
+
+	// /// Remove collection admin.
+	// ///
+	// /// @param admin Address of the removed administrator.
+	// /// @dev EVM selector for this function is: 0xfafd7b42,
+	// ///  or in textual repr: removeCollectionAdmin(address)
+	// function removeCollectionAdmin(address admin) external;
+
+	/// @dev EVM selector for this function is: 0x0b9f3890,
+	///  or in textual repr: setCollectionNesting((bool,bool,address[]))
+	function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external;
+
+	// /// Toggle accessibility of collection nesting.
+	// ///
+	// /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	// /// @dev EVM selector for this function is: 0x112d4586,
+	// ///  or in textual repr: setCollectionNesting(bool)
+	// function setCollectionNesting(bool enable) external;
+
+	// /// Toggle accessibility of collection nesting.
+	// ///
+	// /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	// /// @param collections Addresses of collections that will be available for nesting.
+	// /// @dev EVM selector for this function is: 0x64872396,
+	// ///  or in textual repr: setCollectionNesting(bool,address[])
+	// function setCollectionNesting(bool enable, address[] memory collections) external;
+
+	/// @dev EVM selector for this function is: 0x92c660a8,
+	///  or in textual repr: collectionNesting()
+	function collectionNesting() external view returns (CollectionNestingAndPermission memory);
+
+	// /// Returns nesting for a collection
+	// /// @dev EVM selector for this function is: 0x22d25bfe,
+	// ///  or in textual repr: collectionNestingRestrictedCollectionIds()
+	// function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory);
+
+	// /// Returns permissions for a collection
+	// /// @dev EVM selector for this function is: 0x5b2eaf4b,
+	// ///  or in textual repr: collectionNestingPermissions()
+	// function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory);
+
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
+	function setCollectionAccess(AccessMode mode) external;
+
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0x91b6df49,
+	///  or in textual repr: allowlistedCross((address,uint256))
+	function allowlistedCross(CrossAddress memory user) external view returns (bool);
+
+	// /// Add the user to the allowed list.
+	// ///
+	// /// @param user Address of a trusted user.
+	// /// @dev EVM selector for this function is: 0x67844fe6,
+	// ///  or in textual repr: addToCollectionAllowList(address)
+	// function addToCollectionAllowList(address user) external;
+
+	/// Add user to allowed list.
+	///
+	/// @param user User cross account address.
+	/// @dev EVM selector for this function is: 0xa0184a3a,
+	///  or in textual repr: addToCollectionAllowListCross((address,uint256))
+	function addToCollectionAllowListCross(CrossAddress memory user) external;
+
+	// /// Remove the user from the allowed list.
+	// ///
+	// /// @param user Address of a removed user.
+	// /// @dev EVM selector for this function is: 0x85c51acb,
+	// ///  or in textual repr: removeFromCollectionAllowList(address)
+	// function removeFromCollectionAllowList(address user) external;
+
+	/// Remove user from allowed list.
+	///
+	/// @param user User cross account address.
+	/// @dev EVM selector for this function is: 0x09ba452a,
+	///  or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+	function removeFromCollectionAllowListCross(CrossAddress memory user) external;
+
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
+	function setCollectionMintMode(bool mode) external;
+
+	// /// Check that account is the owner or admin of the collection
+	// ///
+	// /// @param user account to verify
+	// /// @return "true" if account is the owner or admin
+	// /// @dev EVM selector for this function is: 0x9811b0c7,
+	// ///  or in textual repr: isOwnerOrAdmin(address)
+	// function isOwnerOrAdmin(address user) external view returns (bool);
+
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user User cross account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x3e75a905,
+	///  or in textual repr: isOwnerOrAdminCross((address,uint256))
+	function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool);
+
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
+	function uniqueCollectionType() external view returns (string memory);
+
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() external view returns (CrossAddress memory);
+
+	// /// Changes collection owner to another account
+	// ///
+	// /// @dev Owner can be changed only by current owner
+	// /// @param newOwner new owner account
+	// /// @dev EVM selector for this function is: 0x4f53e226,
+	// ///  or in textual repr: changeCollectionOwner(address)
+	// function changeCollectionOwner(address newOwner) external;
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() external view returns (CrossAddress[] memory);
+
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner cross account
+	/// @dev EVM selector for this function is: 0x6496c497,
+	///  or in textual repr: changeCollectionOwnerCross((address,uint256))
+	function changeCollectionOwnerCross(CrossAddress memory newOwner) external;
+}
+
+/// Cross account struct
+struct CrossAddress {
+	address eth;
+	uint256 sub;
+}
+
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+	/// Access grant for owner and admins. Used as default.
+	Normal,
+	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+	AllowList
+}
+
+/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
+struct CollectionNestingPermission {
+	CollectionPermissionField field;
+	bool value;
+}
+
+/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.
+enum CollectionPermissionField {
+	/// Owner of token can nest tokens under it.
+	TokenOwner,
+	/// Admin of token collection can nest tokens under token.
+	CollectionAdmin
+}
+
+/// Nested collections.
+struct CollectionNesting {
+	bool token_owner;
+	uint256[] ids;
+}
+
+/// Nested collections and permissions
+struct CollectionNestingAndPermission {
+	/// Owner of token can nest tokens under it.
+	bool token_owner;
+	/// Admin of token collection can nest tokens under token.
+	bool collection_admin;
+	/// If set - only tokens from specified collections can be nested.
+	address[] restricted;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
+struct CollectionLimit {
+	CollectionLimitField field;
+	OptionUint256 value;
+}
+
+/// Optional value
+struct OptionUint256 {
+	/// Shows the status of accessibility of value
+	bool status;
+	/// Actual value if `status` is true
+	uint256 value;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
+enum CollectionLimitField {
+	/// How many tokens can a user have on one account.
+	AccountTokenOwnership,
+	/// How many bytes of data are available for sponsorship.
+	SponsoredDataSize,
+	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
+	SponsoredDataRateLimit,
+	/// How many tokens can be mined into this collection.
+	TokenLimit,
+	/// Timeouts for transfer sponsoring.
+	SponsorTransferTimeout,
+	/// Timeout for sponsoring an approval in passed blocks.
+	SponsorApproveTimeout,
+	/// Whether the collection owner of the collection can send tokens (which belong to other users).
+	OwnerCanTransfer,
+	/// Can the collection owner burn other people's tokens.
+	OwnerCanDestroy,
+	/// Is it possible to send tokens from this collection between users.
+	TransferEnabled
+}
+
+/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
+struct Property {
+	string key;
+	bytes value;
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x69d14d3e
+interface ERC20UniqueExtensions is Dummy, ERC165 {
+	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
+	/// @param owner crossAddress The address which owns the funds.
+	/// @param spender crossAddress The address which will spend the funds.
+	/// @return A uint256 specifying the amount of tokens still available for the spender.
+	/// @dev EVM selector for this function is: 0xe0af4bd7,
+	///  or in textual repr: allowanceCross((address,uint256),(address,uint256))
+	function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256);
+
+	/// @notice A description for the collection.
+	/// @dev EVM selector for this function is: 0x7284e416,
+	///  or in textual repr: description()
+	function description() external view returns (string memory);
+
+	/// @dev EVM selector for this function is: 0x269e6158,
+	///  or in textual repr: mintCross((address,uint256),uint256)
+	function mintCross(CrossAddress memory to, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x0ecd0ab0,
+	///  or in textual repr: approveCross((address,uint256),uint256)
+	function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool);
+
+	// /// Burn tokens from account
+	// /// @dev Function that burns an `amount` of the tokens of a given account,
+	// /// deducting from the sender's allowance for said account.
+	// /// @param from The account whose tokens will be burnt.
+	// /// @param amount The amount that will be burnt.
+	// /// @dev EVM selector for this function is: 0x79cc6790,
+	// ///  or in textual repr: burnFrom(address,uint256)
+	// function burnFrom(address from, uint256 amount) external returns (bool);
+
+	/// Burn tokens from account
+	/// @dev Function that burns an `amount` of the tokens of a given account,
+	/// deducting from the sender's allowance for said account.
+	/// @param from The account whose tokens will be burnt.
+	/// @param amount The amount that will be burnt.
+	/// @dev EVM selector for this function is: 0xbb2f5a58,
+	///  or in textual repr: burnFromCross((address,uint256),uint256)
+	function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool);
+
+	/// Mint tokens for multiple accounts.
+	/// @param amounts array of pairs of account address and amount
+	/// @dev EVM selector for this function is: 0x1acf2d55,
+	///  or in textual repr: mintBulk((address,uint256)[])
+	function mintBulk(AmountForAddress[] memory amounts) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x2ada85ff,
+	///  or in textual repr: transferCross((address,uint256),uint256)
+	function transferCross(CrossAddress memory to, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0xd5cf430b,
+	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+	function transferFromCross(
+		CrossAddress memory from,
+		CrossAddress memory to,
+		uint256 amount
+	) external returns (bool);
+
+	/// @notice Returns collection helper contract address
+	/// @dev EVM selector for this function is: 0x1896cce6,
+	///  or in textual repr: collectionHelperAddress()
+	function collectionHelperAddress() external view returns (address);
+
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
+}
+
+struct AmountForAddress {
+	address to;
+	uint256 amount;
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x40c10f19
+interface ERC20Mintable is Dummy, ERC165 {
+	/// Mint tokens for `to` account.
+	/// @param to account that will receive minted tokens
+	/// @param amount amount of tokens to mint
+	/// @dev EVM selector for this function is: 0x40c10f19,
+	///  or in textual repr: mint(address,uint256)
+	function mint(address to, uint256 amount) external returns (bool);
+}
+
+/// @dev inlined interface
+interface ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(address indexed owner, address indexed spender, uint256 value);
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x942e8b22
+interface ERC20 is Dummy, ERC165, ERC20Events {
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0x313ce567,
+	///  or in textual repr: decimals()
+	function decimals() external view returns (uint8);
+
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
+	function transfer(address to, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address spender, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0xdd62ed3e,
+	///  or in textual repr: allowance(address,address)
+	function allowance(address owner, address spender) external view returns (uint256);
+}
+
+interface UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {}
addedjs-packages/tests/eth/api/UniqueNFT.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/api/UniqueNFT.sol
@@ -0,0 +1,855 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @dev common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+	function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+/// @dev inlined interface
+interface ERC721TokenEvent {
+	event TokenChanged(uint256 indexed tokenId);
+}
+
+/// @title A contract that allows to set and delete token properties and change token property permissions.
+/// @dev the ERC-165 identifier for this interface is 0xde0695c2
+interface TokenProperties is Dummy, ERC165, ERC721TokenEvent {
+	// /// @notice Set permissions for token property.
+	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	// /// @param key Property key.
+	// /// @param isMutable Permission to mutate property.
+	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	// /// @dev EVM selector for this function is: 0x222d97fa,
+	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
+	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external;
+
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param permissions Permissions for keys.
+	/// @dev EVM selector for this function is: 0xbd92983a,
+	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
+	function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external;
+
+	/// @notice Get permissions for token properties.
+	/// @dev EVM selector for this function is: 0xf23d7790,
+	///  or in textual repr: tokenPropertyPermissions()
+	function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory);
+
+	// /// @notice Set token property value.
+	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
+	// /// @param tokenId ID of the token.
+	// /// @param key Property key.
+	// /// @param value Property value.
+	// /// @dev EVM selector for this function is: 0x1752d67b,
+	// ///  or in textual repr: setProperty(uint256,string,bytes)
+	// function setProperty(uint256 tokenId, string memory key, bytes memory value) external;
+
+	/// @notice Set token properties value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param properties settable properties
+	/// @dev EVM selector for this function is: 0x14ed3a6e,
+	///  or in textual repr: setProperties(uint256,(string,bytes)[])
+	function setProperties(uint256 tokenId, Property[] memory properties) external;
+
+	// /// @notice Delete token property value.
+	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
+	// /// @param tokenId ID of the token.
+	// /// @param key Property key.
+	// /// @dev EVM selector for this function is: 0x066111d1,
+	// ///  or in textual repr: deleteProperty(uint256,string)
+	// function deleteProperty(uint256 tokenId, string memory key) external;
+
+	/// @notice Delete token properties value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param keys Properties key.
+	/// @dev EVM selector for this function is: 0xc472d371,
+	///  or in textual repr: deleteProperties(uint256,string[])
+	function deleteProperties(uint256 tokenId, string[] memory keys) external;
+
+	/// @notice Get token property value.
+	/// @dev Throws error if key not found
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @return Property value bytes
+	/// @dev EVM selector for this function is: 0x7228c327,
+	///  or in textual repr: property(uint256,string)
+	function property(uint256 tokenId, string memory key) external view returns (bytes memory);
+}
+
+/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
+struct Property {
+	string key;
+	bytes value;
+}
+
+/// Ethereum representation of Token Property Permissions.
+struct TokenPropertyPermission {
+	/// Token property key.
+	string key;
+	/// Token property permissions.
+	PropertyPermission[] permissions;
+}
+
+/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.
+struct PropertyPermission {
+	/// TokenPermission field.
+	TokenPermissionField code;
+	/// TokenPermission value.
+	bool value;
+}
+
+/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.
+enum TokenPermissionField {
+	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]
+	Mutable,
+	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]
+	TokenOwner,
+	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]
+	CollectionAdmin
+}
+
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xb34d97e9
+interface Collection is Dummy, ERC165 {
+	// /// Set collection property.
+	// ///
+	// /// @param key Property key.
+	// /// @param value Propery value.
+	// /// @dev EVM selector for this function is: 0x2f073f66,
+	// ///  or in textual repr: setCollectionProperty(string,bytes)
+	// function setCollectionProperty(string memory key, bytes memory value) external;
+
+	/// Set collection properties.
+	///
+	/// @param properties Vector of properties key/value pair.
+	/// @dev EVM selector for this function is: 0x50b26b2a,
+	///  or in textual repr: setCollectionProperties((string,bytes)[])
+	function setCollectionProperties(Property[] memory properties) external;
+
+	// /// Delete collection property.
+	// ///
+	// /// @param key Property key.
+	// /// @dev EVM selector for this function is: 0x7b7debce,
+	// ///  or in textual repr: deleteCollectionProperty(string)
+	// function deleteCollectionProperty(string memory key) external;
+
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) external;
+
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
+	function collectionProperty(string memory key) external view returns (bytes memory);
+
+	/// Get collection properties.
+	///
+	/// @param keys Properties keys. Empty keys for all propertyes.
+	/// @return Vector of properties key/value pairs.
+	/// @dev EVM selector for this function is: 0x285fb8e6,
+	///  or in textual repr: collectionProperties(string[])
+	function collectionProperties(string[] memory keys) external view returns (Property[] memory);
+
+	// /// Set the sponsor of the collection.
+	// ///
+	// /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	// ///
+	// /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	// /// @dev EVM selector for this function is: 0x7623402e,
+	// ///  or in textual repr: setCollectionSponsor(address)
+	// function setCollectionSponsor(address sponsor) external;
+
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x84a1d5a8,
+	///  or in textual repr: setCollectionSponsorCross((address,uint256))
+	function setCollectionSponsorCross(CrossAddress memory sponsor) external;
+
+	/// Whether there is a pending sponsor.
+	/// @dev EVM selector for this function is: 0x058ac185,
+	///  or in textual repr: hasCollectionPendingSponsor()
+	function hasCollectionPendingSponsor() external view returns (bool);
+
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
+	function confirmCollectionSponsorship() external;
+
+	/// Remove collection sponsor.
+	/// @dev EVM selector for this function is: 0x6e0326a3,
+	///  or in textual repr: removeCollectionSponsor()
+	function removeCollectionSponsor() external;
+
+	/// Get current sponsor.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	/// @dev EVM selector for this function is: 0x6ec0a9f1,
+	///  or in textual repr: collectionSponsor()
+	function collectionSponsor() external view returns (CrossAddress memory);
+
+	/// Get current collection limits.
+	///
+	/// @return Array of collection limits
+	/// @dev EVM selector for this function is: 0xf63bc572,
+	///  or in textual repr: collectionLimits()
+	function collectionLimits() external view returns (CollectionLimit[] memory);
+
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Some limit.
+	/// @dev EVM selector for this function is: 0x2316ee74,
+	///  or in textual repr: setCollectionLimit((uint8,(bool,uint256)))
+	function setCollectionLimit(CollectionLimit memory limit) external;
+
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
+	function contractAddress() external view returns (address);
+
+	/// Add collection admin.
+	/// @param newAdmin Cross account administrator address.
+	/// @dev EVM selector for this function is: 0x859aa7d6,
+	///  or in textual repr: addCollectionAdminCross((address,uint256))
+	function addCollectionAdminCross(CrossAddress memory newAdmin) external;
+
+	/// Remove collection admin.
+	/// @param admin Cross account administrator address.
+	/// @dev EVM selector for this function is: 0x6c0cd173,
+	///  or in textual repr: removeCollectionAdminCross((address,uint256))
+	function removeCollectionAdminCross(CrossAddress memory admin) external;
+
+	// /// Add collection admin.
+	// /// @param newAdmin Address of the added administrator.
+	// /// @dev EVM selector for this function is: 0x92e462c7,
+	// ///  or in textual repr: addCollectionAdmin(address)
+	// function addCollectionAdmin(address newAdmin) external;
+
+	// /// Remove collection admin.
+	// ///
+	// /// @param admin Address of the removed administrator.
+	// /// @dev EVM selector for this function is: 0xfafd7b42,
+	// ///  or in textual repr: removeCollectionAdmin(address)
+	// function removeCollectionAdmin(address admin) external;
+
+	/// @dev EVM selector for this function is: 0x0b9f3890,
+	///  or in textual repr: setCollectionNesting((bool,bool,address[]))
+	function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external;
+
+	// /// Toggle accessibility of collection nesting.
+	// ///
+	// /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	// /// @dev EVM selector for this function is: 0x112d4586,
+	// ///  or in textual repr: setCollectionNesting(bool)
+	// function setCollectionNesting(bool enable) external;
+
+	// /// Toggle accessibility of collection nesting.
+	// ///
+	// /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	// /// @param collections Addresses of collections that will be available for nesting.
+	// /// @dev EVM selector for this function is: 0x64872396,
+	// ///  or in textual repr: setCollectionNesting(bool,address[])
+	// function setCollectionNesting(bool enable, address[] memory collections) external;
+
+	/// @dev EVM selector for this function is: 0x92c660a8,
+	///  or in textual repr: collectionNesting()
+	function collectionNesting() external view returns (CollectionNestingAndPermission memory);
+
+	// /// Returns nesting for a collection
+	// /// @dev EVM selector for this function is: 0x22d25bfe,
+	// ///  or in textual repr: collectionNestingRestrictedCollectionIds()
+	// function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory);
+
+	// /// Returns permissions for a collection
+	// /// @dev EVM selector for this function is: 0x5b2eaf4b,
+	// ///  or in textual repr: collectionNestingPermissions()
+	// function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory);
+
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
+	function setCollectionAccess(AccessMode mode) external;
+
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0x91b6df49,
+	///  or in textual repr: allowlistedCross((address,uint256))
+	function allowlistedCross(CrossAddress memory user) external view returns (bool);
+
+	// /// Add the user to the allowed list.
+	// ///
+	// /// @param user Address of a trusted user.
+	// /// @dev EVM selector for this function is: 0x67844fe6,
+	// ///  or in textual repr: addToCollectionAllowList(address)
+	// function addToCollectionAllowList(address user) external;
+
+	/// Add user to allowed list.
+	///
+	/// @param user User cross account address.
+	/// @dev EVM selector for this function is: 0xa0184a3a,
+	///  or in textual repr: addToCollectionAllowListCross((address,uint256))
+	function addToCollectionAllowListCross(CrossAddress memory user) external;
+
+	// /// Remove the user from the allowed list.
+	// ///
+	// /// @param user Address of a removed user.
+	// /// @dev EVM selector for this function is: 0x85c51acb,
+	// ///  or in textual repr: removeFromCollectionAllowList(address)
+	// function removeFromCollectionAllowList(address user) external;
+
+	/// Remove user from allowed list.
+	///
+	/// @param user User cross account address.
+	/// @dev EVM selector for this function is: 0x09ba452a,
+	///  or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+	function removeFromCollectionAllowListCross(CrossAddress memory user) external;
+
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
+	function setCollectionMintMode(bool mode) external;
+
+	// /// Check that account is the owner or admin of the collection
+	// ///
+	// /// @param user account to verify
+	// /// @return "true" if account is the owner or admin
+	// /// @dev EVM selector for this function is: 0x9811b0c7,
+	// ///  or in textual repr: isOwnerOrAdmin(address)
+	// function isOwnerOrAdmin(address user) external view returns (bool);
+
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user User cross account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x3e75a905,
+	///  or in textual repr: isOwnerOrAdminCross((address,uint256))
+	function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool);
+
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
+	function uniqueCollectionType() external view returns (string memory);
+
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() external view returns (CrossAddress memory);
+
+	// /// Changes collection owner to another account
+	// ///
+	// /// @dev Owner can be changed only by current owner
+	// /// @param newOwner new owner account
+	// /// @dev EVM selector for this function is: 0x4f53e226,
+	// ///  or in textual repr: changeCollectionOwner(address)
+	// function changeCollectionOwner(address newOwner) external;
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() external view returns (CrossAddress[] memory);
+
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner cross account
+	/// @dev EVM selector for this function is: 0x6496c497,
+	///  or in textual repr: changeCollectionOwnerCross((address,uint256))
+	function changeCollectionOwnerCross(CrossAddress memory newOwner) external;
+}
+
+/// Cross account struct
+struct CrossAddress {
+	address eth;
+	uint256 sub;
+}
+
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+	/// Access grant for owner and admins. Used as default.
+	Normal,
+	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+	AllowList
+}
+
+/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
+struct CollectionNestingPermission {
+	CollectionPermissionField field;
+	bool value;
+}
+
+/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.
+enum CollectionPermissionField {
+	/// Owner of token can nest tokens under it.
+	TokenOwner,
+	/// Admin of token collection can nest tokens under token.
+	CollectionAdmin
+}
+
+/// Nested collections.
+struct CollectionNesting {
+	bool token_owner;
+	uint256[] ids;
+}
+
+/// Nested collections and permissions
+struct CollectionNestingAndPermission {
+	/// Owner of token can nest tokens under it.
+	bool token_owner;
+	/// Admin of token collection can nest tokens under token.
+	bool collection_admin;
+	/// If set - only tokens from specified collections can be nested.
+	address[] restricted;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
+struct CollectionLimit {
+	CollectionLimitField field;
+	OptionUint256 value;
+}
+
+/// Optional value
+struct OptionUint256 {
+	/// Shows the status of accessibility of value
+	bool status;
+	/// Actual value if `status` is true
+	uint256 value;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
+enum CollectionLimitField {
+	/// How many tokens can a user have on one account.
+	AccountTokenOwnership,
+	/// How many bytes of data are available for sponsorship.
+	SponsoredDataSize,
+	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
+	SponsoredDataRateLimit,
+	/// How many tokens can be mined into this collection.
+	TokenLimit,
+	/// Timeouts for transfer sponsoring.
+	SponsorTransferTimeout,
+	/// Timeout for sponsoring an approval in passed blocks.
+	SponsorApproveTimeout,
+	/// Whether the collection owner of the collection can send tokens (which belong to other users).
+	OwnerCanTransfer,
+	/// Can the collection owner burn other people's tokens.
+	OwnerCanDestroy,
+	/// Is it possible to send tokens from this collection between users.
+	TransferEnabled
+}
+
+/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+interface ERC721Metadata is Dummy, ERC165 {
+	// /// @notice A descriptive name for a collection of NFTs in this contract
+	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
+	// /// @dev EVM selector for this function is: 0x06fdde03,
+	// ///  or in textual repr: name()
+	// function name() external view returns (string memory);
+
+	// /// @notice An abbreviated name for NFTs in this contract
+	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
+	// /// @dev EVM selector for this function is: 0x95d89b41,
+	// ///  or in textual repr: symbol()
+	// function symbol() external view returns (string memory);
+
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) external view returns (string memory);
+}
+
+/// @title ERC721 Token that can be irreversibly burned (destroyed).
+/// @dev the ERC-165 identifier for this interface is 0x42966c68
+interface ERC721Burnable is Dummy, ERC165 {
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param tokenId The NFT to approve
+	/// @dev EVM selector for this function is: 0x42966c68,
+	///  or in textual repr: burn(uint256)
+	function burn(uint256 tokenId) external;
+}
+
+/// @title ERC721 minting logic.
+/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
+interface ERC721UniqueMintable is Dummy, ERC165 {
+	/// @notice Function to mint a token.
+	/// @param to The new owner
+	/// @return uint256 The id of the newly minted token
+	/// @dev EVM selector for this function is: 0x6a627842,
+	///  or in textual repr: mint(address)
+	function mint(address to) external returns (uint256);
+
+	// /// @notice Function to mint a token.
+	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
+	// ///  unlike standard, you can't specify it manually
+	// /// @param to The new owner
+	// /// @param tokenId ID of the minted NFT
+	// /// @dev EVM selector for this function is: 0x40c10f19,
+	// ///  or in textual repr: mint(address,uint256)
+	// function mint(address to, uint256 tokenId) external returns (bool);
+
+	/// @notice Function to mint token with the given tokenUri.
+	/// @param to The new owner
+	/// @param tokenUri Token URI that would be stored in the NFT properties
+	/// @return uint256 The id of the newly minted token
+	/// @dev EVM selector for this function is: 0x45c17782,
+	///  or in textual repr: mintWithTokenURI(address,string)
+	function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);
+	// /// @notice Function to mint token with the given tokenUri.
+	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
+	// ///  unlike standard, you can't specify it manually
+	// /// @param to The new owner
+	// /// @param tokenId ID of the minted NFT
+	// /// @param tokenUri Token URI that would be stored in the NFT properties
+	// /// @dev EVM selector for this function is: 0x50bb4e7f,
+	// ///  or in textual repr: mintWithTokenURI(address,uint256,string)
+	// function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);
+
+}
+
+/// @title Unique extensions for ERC721.
+/// @dev the ERC-165 identifier for this interface is 0x9b397d16
+interface ERC721UniqueExtensions is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of NFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @notice An abbreviated name for NFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @notice A description for the collection.
+	/// @dev EVM selector for this function is: 0x7284e416,
+	///  or in textual repr: description()
+	function description() external view returns (string memory);
+
+	// /// Returns the owner (in cross format) of the token.
+	// ///
+	// /// @param tokenId Id for the token.
+	// /// @dev EVM selector for this function is: 0x2b29dace,
+	// ///  or in textual repr: crossOwnerOf(uint256)
+	// function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+
+	/// Returns the owner (in cross format) of the token.
+	///
+	/// @param tokenId Id for the token.
+	/// @dev EVM selector for this function is: 0xcaa3a4d0,
+	///  or in textual repr: ownerOfCross(uint256)
+	function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
+
+	/// @notice Count all NFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
+
+	/// Returns the token properties.
+	///
+	/// @param tokenId Id for the token.
+	/// @param keys Properties keys. Empty keys for all propertyes.
+	/// @return Vector of properties key/value pairs.
+	/// @dev EVM selector for this function is: 0xe07ede7e,
+	///  or in textual repr: properties(uint256,string[])
+	function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory);
+
+	/// @notice Set or reaffirm the approved address for an NFT
+	/// @dev The zero address indicates there is no approved address.
+	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param approved The new substrate address approved NFT controller
+	/// @param tokenId The NFT to approve
+	/// @dev EVM selector for this function is: 0x0ecd0ab0,
+	///  or in textual repr: approveCross((address,uint256),uint256)
+	function approveCross(CrossAddress memory approved, uint256 tokenId) external;
+
+	/// @notice Transfer ownership of an NFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
+	function transfer(address to, uint256 tokenId) external;
+
+	/// @notice Transfer ownership of an NFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x2ada85ff,
+	///  or in textual repr: transferCross((address,uint256),uint256)
+	function transferCross(CrossAddress memory to, uint256 tokenId) external;
+
+	/// @notice Transfer ownership of an NFT from cross account address to cross account address
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param from Cross acccount address of current owner
+	/// @param to Cross acccount address of new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0xd5cf430b,
+	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+	function transferFromCross(
+		CrossAddress memory from,
+		CrossAddress memory to,
+		uint256 tokenId
+	) external;
+
+	// /// @notice Burns a specific ERC721 token.
+	// /// @dev Throws unless `msg.sender` is the current owner or an authorized
+	// ///  operator for this NFT. Throws if `from` is not the current owner. Throws
+	// ///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+	// /// @param from The current owner of the NFT
+	// /// @param tokenId The NFT to transfer
+	// /// @dev EVM selector for this function is: 0x79cc6790,
+	// ///  or in textual repr: burnFrom(address,uint256)
+	// function burnFrom(address from, uint256 tokenId) external;
+
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this NFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param from The current owner of the NFT
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0xbb2f5a58,
+	///  or in textual repr: burnFromCross((address,uint256),uint256)
+	function burnFromCross(CrossAddress memory from, uint256 tokenId) external;
+
+	/// @notice Returns next free NFT ID.
+	/// @dev EVM selector for this function is: 0x75794a3c,
+	///  or in textual repr: nextTokenId()
+	function nextTokenId() external view returns (uint256);
+
+	// /// @notice Function to mint multiple tokens.
+	// /// @dev `tokenIds` should be an array of consecutive numbers and first number
+	// ///  should be obtained with `nextTokenId` method
+	// /// @param to The new owner
+	// /// @param tokenIds IDs of the minted NFTs
+	// /// @dev EVM selector for this function is: 0x44a9945e,
+	// ///  or in textual repr: mintBulk(address,uint256[])
+	// function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);
+
+	/// @notice Function to mint a token.
+	/// @param data Array of pairs of token owner and token's properties for minted token
+	/// @dev EVM selector for this function is: 0xab427b0c,
+	///  or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[])
+	function mintBulkCross(MintTokenData[] memory data) external returns (bool);
+
+	// /// @notice Function to mint multiple tokens with the given tokenUris.
+	// /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	// ///  numbers and first number should be obtained with `nextTokenId` method
+	// /// @param to The new owner
+	// /// @param tokens array of pairs of token ID and token URI for minted tokens
+	// /// @dev EVM selector for this function is: 0x36543006,
+	// ///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+	// function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool);
+
+	/// @notice Function to mint a token.
+	/// @param to The new owner crossAccountId
+	/// @param properties Properties of minted token
+	/// @return uint256 The id of the newly minted token
+	/// @dev EVM selector for this function is: 0xb904db03,
+	///  or in textual repr: mintCross((address,uint256),(string,bytes)[])
+	function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256);
+
+	/// @notice Returns collection helper contract address
+	/// @dev EVM selector for this function is: 0x1896cce6,
+	///  or in textual repr: collectionHelperAddress()
+	function collectionHelperAddress() external view returns (address);
+}
+
+/// Data for creation token with uri.
+struct TokenUri {
+	/// Id of new token.
+	uint256 id;
+	/// Uri of new token.
+	string uri;
+}
+
+/// Token minting parameters
+struct MintTokenData {
+	/// Minted token owner
+	CrossAddress owner;
+	/// Minted token properties
+	Property[] properties;
+}
+
+/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+	/// @notice Enumerate valid NFTs
+	/// @param index A counter less than `totalSupply()`
+	/// @return The token identifier for the `index`th NFT,
+	///  (sort order not specified)
+	/// @dev EVM selector for this function is: 0x4f6ccce7,
+	///  or in textual repr: tokenByIndex(uint256)
+	function tokenByIndex(uint256 index) external view returns (uint256);
+
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x2f745c59,
+	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
+	function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
+
+	/// @notice Count NFTs tracked by this contract
+	/// @return A count of valid NFTs tracked by this contract, where each one of
+	///  them has an assigned and queryable owner not equal to the zero address
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+}
+
+/// @dev inlined interface
+interface ERC721Events {
+	event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
+	event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
+	event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
+}
+
+/// @title ERC-721 Non-Fungible Token Standard
+/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
+interface ERC721 is Dummy, ERC165, ERC721Events {
+	/// @notice Count all NFTs assigned to an owner
+	/// @dev NFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
+
+	/// @notice Find the owner of an NFT
+	/// @dev NFTs assigned to zero address are considered invalid, and queries
+	///  about them do throw.
+	/// @param tokenId The identifier for an NFT
+	/// @return The address of the owner of the NFT
+	/// @dev EVM selector for this function is: 0x6352211e,
+	///  or in textual repr: ownerOf(uint256)
+	function ownerOf(uint256 tokenId) external view returns (address);
+
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xb88d4fde,
+	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) external;
+
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x42842e0e,
+	///  or in textual repr: safeTransferFrom(address,address,uint256)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
+
+	/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
+	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+	///  THEY MAY BE PERMANENTLY LOST
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this NFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
+	/// @param from The current owner of the NFT
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
+
+	/// @notice Set or reaffirm the approved address for an NFT
+	/// @dev The zero address indicates there is no approved address.
+	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param approved The new approved NFT controller
+	/// @param tokenId The NFT to approve
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address approved, uint256 tokenId) external;
+
+	/// @notice Sets or unsets the approval of a given operator.
+	/// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
+	/// @param operator Operator
+	/// @param approved Should operator status be granted or revoked?
+	/// @dev EVM selector for this function is: 0xa22cb465,
+	///  or in textual repr: setApprovalForAll(address,bool)
+	function setApprovalForAll(address operator, bool approved) external;
+
+	/// @notice Get the approved address for a single NFT
+	/// @dev Throws if `tokenId` is not a valid NFT
+	/// @param tokenId The NFT to find the approved address for
+	/// @return The approved address for this NFT, or the zero address if there is none
+	/// @dev EVM selector for this function is: 0x081812fc,
+	///  or in textual repr: getApproved(uint256)
+	function getApproved(uint256 tokenId) external view returns (address);
+
+	/// @notice Tells whether the given `owner` approves the `operator`.
+	/// @dev EVM selector for this function is: 0xe985e9c5,
+	///  or in textual repr: isApprovedForAll(address,address)
+	function isApprovedForAll(address owner, address operator) external view returns (bool);
+}
+
+interface UniqueNFT is
+	Dummy,
+	ERC165,
+	ERC721,
+	ERC721Enumerable,
+	ERC721UniqueExtensions,
+	ERC721UniqueMintable,
+	ERC721Burnable,
+	ERC721Metadata,
+	Collection,
+	TokenProperties
+{}
addedjs-packages/tests/eth/api/UniqueNativeFungible.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/api/UniqueNativeFungible.sol
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @dev common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+	function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x1313556c
+interface ERC20UniqueExtensions is Dummy, ERC165 {
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0x2ada85ff,
+	///  or in textual repr: transferCross((address,uint256),uint256)
+	function transferCross(CrossAddress memory to, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0xd5cf430b,
+	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+	function transferFromCross(
+		CrossAddress memory from,
+		CrossAddress memory to,
+		uint256 amount
+	) external returns (bool);
+}
+
+/// Cross account struct
+struct CrossAddress {
+	address eth;
+	uint256 sub;
+}
+
+/// @dev inlined interface
+interface ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(address indexed owner, address indexed spender, uint256 value);
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x942e8b22
+interface ERC20 is Dummy, ERC165, ERC20Events {
+	/// @dev EVM selector for this function is: 0xdd62ed3e,
+	///  or in textual repr: allowance(address,address)
+	function allowance(address owner, address spender) external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address spender, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0x313ce567,
+	///  or in textual repr: decimals()
+	function decimals() external view returns (uint8);
+
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
+	function transfer(address to, uint256 amount) external returns (bool);
+
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) external returns (bool);
+}
+
+interface UniqueNativeFungible is Dummy, ERC165, ERC20, ERC20UniqueExtensions {}
addedjs-packages/tests/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/api/UniqueRefungible.sol
@@ -0,0 +1,859 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @dev common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+	function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+/// @dev inlined interface
+interface ERC721TokenEvent {
+	event TokenChanged(uint256 indexed tokenId);
+}
+
+/// @title A contract that allows to set and delete token properties and change token property permissions.
+/// @dev the ERC-165 identifier for this interface is 0xde0695c2
+interface TokenProperties is Dummy, ERC165, ERC721TokenEvent {
+	// /// @notice Set permissions for token property.
+	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	// /// @param key Property key.
+	// /// @param isMutable Permission to mutate property.
+	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	// /// @dev EVM selector for this function is: 0x222d97fa,
+	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
+	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external;
+
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param permissions Permissions for keys.
+	/// @dev EVM selector for this function is: 0xbd92983a,
+	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
+	function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external;
+
+	/// @notice Get permissions for token properties.
+	/// @dev EVM selector for this function is: 0xf23d7790,
+	///  or in textual repr: tokenPropertyPermissions()
+	function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory);
+
+	// /// @notice Set token property value.
+	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
+	// /// @param tokenId ID of the token.
+	// /// @param key Property key.
+	// /// @param value Property value.
+	// /// @dev EVM selector for this function is: 0x1752d67b,
+	// ///  or in textual repr: setProperty(uint256,string,bytes)
+	// function setProperty(uint256 tokenId, string memory key, bytes memory value) external;
+
+	/// @notice Set token properties value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param properties settable properties
+	/// @dev EVM selector for this function is: 0x14ed3a6e,
+	///  or in textual repr: setProperties(uint256,(string,bytes)[])
+	function setProperties(uint256 tokenId, Property[] memory properties) external;
+
+	// /// @notice Delete token property value.
+	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
+	// /// @param tokenId ID of the token.
+	// /// @param key Property key.
+	// /// @dev EVM selector for this function is: 0x066111d1,
+	// ///  or in textual repr: deleteProperty(uint256,string)
+	// function deleteProperty(uint256 tokenId, string memory key) external;
+
+	/// @notice Delete token properties value.
+	/// @dev Throws error if `msg.sender` has no permission to edit the property.
+	/// @param tokenId ID of the token.
+	/// @param keys Properties key.
+	/// @dev EVM selector for this function is: 0xc472d371,
+	///  or in textual repr: deleteProperties(uint256,string[])
+	function deleteProperties(uint256 tokenId, string[] memory keys) external;
+
+	/// @notice Get token property value.
+	/// @dev Throws error if key not found
+	/// @param tokenId ID of the token.
+	/// @param key Property key.
+	/// @return Property value bytes
+	/// @dev EVM selector for this function is: 0x7228c327,
+	///  or in textual repr: property(uint256,string)
+	function property(uint256 tokenId, string memory key) external view returns (bytes memory);
+}
+
+/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
+struct Property {
+	string key;
+	bytes value;
+}
+
+/// Ethereum representation of Token Property Permissions.
+struct TokenPropertyPermission {
+	/// Token property key.
+	string key;
+	/// Token property permissions.
+	PropertyPermission[] permissions;
+}
+
+/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.
+struct PropertyPermission {
+	/// TokenPermission field.
+	TokenPermissionField code;
+	/// TokenPermission value.
+	bool value;
+}
+
+/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.
+enum TokenPermissionField {
+	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]
+	Mutable,
+	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]
+	TokenOwner,
+	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]
+	CollectionAdmin
+}
+
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xb34d97e9
+interface Collection is Dummy, ERC165 {
+	// /// Set collection property.
+	// ///
+	// /// @param key Property key.
+	// /// @param value Propery value.
+	// /// @dev EVM selector for this function is: 0x2f073f66,
+	// ///  or in textual repr: setCollectionProperty(string,bytes)
+	// function setCollectionProperty(string memory key, bytes memory value) external;
+
+	/// Set collection properties.
+	///
+	/// @param properties Vector of properties key/value pair.
+	/// @dev EVM selector for this function is: 0x50b26b2a,
+	///  or in textual repr: setCollectionProperties((string,bytes)[])
+	function setCollectionProperties(Property[] memory properties) external;
+
+	// /// Delete collection property.
+	// ///
+	// /// @param key Property key.
+	// /// @dev EVM selector for this function is: 0x7b7debce,
+	// ///  or in textual repr: deleteCollectionProperty(string)
+	// function deleteCollectionProperty(string memory key) external;
+
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) external;
+
+	/// Get collection property.
+	///
+	/// @dev Throws error if key not found.
+	///
+	/// @param key Property key.
+	/// @return bytes The property corresponding to the key.
+	/// @dev EVM selector for this function is: 0xcf24fd6d,
+	///  or in textual repr: collectionProperty(string)
+	function collectionProperty(string memory key) external view returns (bytes memory);
+
+	/// Get collection properties.
+	///
+	/// @param keys Properties keys. Empty keys for all propertyes.
+	/// @return Vector of properties key/value pairs.
+	/// @dev EVM selector for this function is: 0x285fb8e6,
+	///  or in textual repr: collectionProperties(string[])
+	function collectionProperties(string[] memory keys) external view returns (Property[] memory);
+
+	// /// Set the sponsor of the collection.
+	// ///
+	// /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	// ///
+	// /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
+	// /// @dev EVM selector for this function is: 0x7623402e,
+	// ///  or in textual repr: setCollectionSponsor(address)
+	// function setCollectionSponsor(address sponsor) external;
+
+	/// Set the sponsor of the collection.
+	///
+	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
+	///
+	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
+	/// @dev EVM selector for this function is: 0x84a1d5a8,
+	///  or in textual repr: setCollectionSponsorCross((address,uint256))
+	function setCollectionSponsorCross(CrossAddress memory sponsor) external;
+
+	/// Whether there is a pending sponsor.
+	/// @dev EVM selector for this function is: 0x058ac185,
+	///  or in textual repr: hasCollectionPendingSponsor()
+	function hasCollectionPendingSponsor() external view returns (bool);
+
+	/// Collection sponsorship confirmation.
+	///
+	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
+	/// @dev EVM selector for this function is: 0x3c50e97a,
+	///  or in textual repr: confirmCollectionSponsorship()
+	function confirmCollectionSponsorship() external;
+
+	/// Remove collection sponsor.
+	/// @dev EVM selector for this function is: 0x6e0326a3,
+	///  or in textual repr: removeCollectionSponsor()
+	function removeCollectionSponsor() external;
+
+	/// Get current sponsor.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
+	/// @dev EVM selector for this function is: 0x6ec0a9f1,
+	///  or in textual repr: collectionSponsor()
+	function collectionSponsor() external view returns (CrossAddress memory);
+
+	/// Get current collection limits.
+	///
+	/// @return Array of collection limits
+	/// @dev EVM selector for this function is: 0xf63bc572,
+	///  or in textual repr: collectionLimits()
+	function collectionLimits() external view returns (CollectionLimit[] memory);
+
+	/// Set limits for the collection.
+	/// @dev Throws error if limit not found.
+	/// @param limit Some limit.
+	/// @dev EVM selector for this function is: 0x2316ee74,
+	///  or in textual repr: setCollectionLimit((uint8,(bool,uint256)))
+	function setCollectionLimit(CollectionLimit memory limit) external;
+
+	/// Get contract address.
+	/// @dev EVM selector for this function is: 0xf6b4dfb4,
+	///  or in textual repr: contractAddress()
+	function contractAddress() external view returns (address);
+
+	/// Add collection admin.
+	/// @param newAdmin Cross account administrator address.
+	/// @dev EVM selector for this function is: 0x859aa7d6,
+	///  or in textual repr: addCollectionAdminCross((address,uint256))
+	function addCollectionAdminCross(CrossAddress memory newAdmin) external;
+
+	/// Remove collection admin.
+	/// @param admin Cross account administrator address.
+	/// @dev EVM selector for this function is: 0x6c0cd173,
+	///  or in textual repr: removeCollectionAdminCross((address,uint256))
+	function removeCollectionAdminCross(CrossAddress memory admin) external;
+
+	// /// Add collection admin.
+	// /// @param newAdmin Address of the added administrator.
+	// /// @dev EVM selector for this function is: 0x92e462c7,
+	// ///  or in textual repr: addCollectionAdmin(address)
+	// function addCollectionAdmin(address newAdmin) external;
+
+	// /// Remove collection admin.
+	// ///
+	// /// @param admin Address of the removed administrator.
+	// /// @dev EVM selector for this function is: 0xfafd7b42,
+	// ///  or in textual repr: removeCollectionAdmin(address)
+	// function removeCollectionAdmin(address admin) external;
+
+	/// @dev EVM selector for this function is: 0x0b9f3890,
+	///  or in textual repr: setCollectionNesting((bool,bool,address[]))
+	function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external;
+
+	// /// Toggle accessibility of collection nesting.
+	// ///
+	// /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
+	// /// @dev EVM selector for this function is: 0x112d4586,
+	// ///  or in textual repr: setCollectionNesting(bool)
+	// function setCollectionNesting(bool enable) external;
+
+	// /// Toggle accessibility of collection nesting.
+	// ///
+	// /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
+	// /// @param collections Addresses of collections that will be available for nesting.
+	// /// @dev EVM selector for this function is: 0x64872396,
+	// ///  or in textual repr: setCollectionNesting(bool,address[])
+	// function setCollectionNesting(bool enable, address[] memory collections) external;
+
+	/// @dev EVM selector for this function is: 0x92c660a8,
+	///  or in textual repr: collectionNesting()
+	function collectionNesting() external view returns (CollectionNestingAndPermission memory);
+
+	// /// Returns nesting for a collection
+	// /// @dev EVM selector for this function is: 0x22d25bfe,
+	// ///  or in textual repr: collectionNestingRestrictedCollectionIds()
+	// function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory);
+
+	// /// Returns permissions for a collection
+	// /// @dev EVM selector for this function is: 0x5b2eaf4b,
+	// ///  or in textual repr: collectionNestingPermissions()
+	// function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory);
+
+	/// Set the collection access method.
+	/// @param mode Access mode
+	/// @dev EVM selector for this function is: 0x41835d4c,
+	///  or in textual repr: setCollectionAccess(uint8)
+	function setCollectionAccess(AccessMode mode) external;
+
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0x91b6df49,
+	///  or in textual repr: allowlistedCross((address,uint256))
+	function allowlistedCross(CrossAddress memory user) external view returns (bool);
+
+	// /// Add the user to the allowed list.
+	// ///
+	// /// @param user Address of a trusted user.
+	// /// @dev EVM selector for this function is: 0x67844fe6,
+	// ///  or in textual repr: addToCollectionAllowList(address)
+	// function addToCollectionAllowList(address user) external;
+
+	/// Add user to allowed list.
+	///
+	/// @param user User cross account address.
+	/// @dev EVM selector for this function is: 0xa0184a3a,
+	///  or in textual repr: addToCollectionAllowListCross((address,uint256))
+	function addToCollectionAllowListCross(CrossAddress memory user) external;
+
+	// /// Remove the user from the allowed list.
+	// ///
+	// /// @param user Address of a removed user.
+	// /// @dev EVM selector for this function is: 0x85c51acb,
+	// ///  or in textual repr: removeFromCollectionAllowList(address)
+	// function removeFromCollectionAllowList(address user) external;
+
+	/// Remove user from allowed list.
+	///
+	/// @param user User cross account address.
+	/// @dev EVM selector for this function is: 0x09ba452a,
+	///  or in textual repr: removeFromCollectionAllowListCross((address,uint256))
+	function removeFromCollectionAllowListCross(CrossAddress memory user) external;
+
+	/// Switch permission for minting.
+	///
+	/// @param mode Enable if "true".
+	/// @dev EVM selector for this function is: 0x00018e84,
+	///  or in textual repr: setCollectionMintMode(bool)
+	function setCollectionMintMode(bool mode) external;
+
+	// /// Check that account is the owner or admin of the collection
+	// ///
+	// /// @param user account to verify
+	// /// @return "true" if account is the owner or admin
+	// /// @dev EVM selector for this function is: 0x9811b0c7,
+	// ///  or in textual repr: isOwnerOrAdmin(address)
+	// function isOwnerOrAdmin(address user) external view returns (bool);
+
+	/// Check that account is the owner or admin of the collection
+	///
+	/// @param user User cross account to verify
+	/// @return "true" if account is the owner or admin
+	/// @dev EVM selector for this function is: 0x3e75a905,
+	///  or in textual repr: isOwnerOrAdminCross((address,uint256))
+	function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool);
+
+	/// Returns collection type
+	///
+	/// @return `Fungible` or `NFT` or `ReFungible`
+	/// @dev EVM selector for this function is: 0xd34b55b8,
+	///  or in textual repr: uniqueCollectionType()
+	function uniqueCollectionType() external view returns (string memory);
+
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() external view returns (CrossAddress memory);
+
+	// /// Changes collection owner to another account
+	// ///
+	// /// @dev Owner can be changed only by current owner
+	// /// @param newOwner new owner account
+	// /// @dev EVM selector for this function is: 0x4f53e226,
+	// ///  or in textual repr: changeCollectionOwner(address)
+	// function changeCollectionOwner(address newOwner) external;
+
+	/// Get collection administrators
+	///
+	/// @return Vector of tuples with admins address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0x5813216b,
+	///  or in textual repr: collectionAdmins()
+	function collectionAdmins() external view returns (CrossAddress[] memory);
+
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner cross account
+	/// @dev EVM selector for this function is: 0x6496c497,
+	///  or in textual repr: changeCollectionOwnerCross((address,uint256))
+	function changeCollectionOwnerCross(CrossAddress memory newOwner) external;
+}
+
+/// Cross account struct
+struct CrossAddress {
+	address eth;
+	uint256 sub;
+}
+
+/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
+enum AccessMode {
+	/// Access grant for owner and admins. Used as default.
+	Normal,
+	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
+	AllowList
+}
+
+/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
+struct CollectionNestingPermission {
+	CollectionPermissionField field;
+	bool value;
+}
+
+/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.
+enum CollectionPermissionField {
+	/// Owner of token can nest tokens under it.
+	TokenOwner,
+	/// Admin of token collection can nest tokens under token.
+	CollectionAdmin
+}
+
+/// Nested collections.
+struct CollectionNesting {
+	bool token_owner;
+	uint256[] ids;
+}
+
+/// Nested collections and permissions
+struct CollectionNestingAndPermission {
+	/// Owner of token can nest tokens under it.
+	bool token_owner;
+	/// Admin of token collection can nest tokens under token.
+	bool collection_admin;
+	/// If set - only tokens from specified collections can be nested.
+	address[] restricted;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
+struct CollectionLimit {
+	CollectionLimitField field;
+	OptionUint256 value;
+}
+
+/// Optional value
+struct OptionUint256 {
+	/// Shows the status of accessibility of value
+	bool status;
+	/// Actual value if `status` is true
+	uint256 value;
+}
+
+/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
+enum CollectionLimitField {
+	/// How many tokens can a user have on one account.
+	AccountTokenOwnership,
+	/// How many bytes of data are available for sponsorship.
+	SponsoredDataSize,
+	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
+	SponsoredDataRateLimit,
+	/// How many tokens can be mined into this collection.
+	TokenLimit,
+	/// Timeouts for transfer sponsoring.
+	SponsorTransferTimeout,
+	/// Timeout for sponsoring an approval in passed blocks.
+	SponsorApproveTimeout,
+	/// Whether the collection owner of the collection can send tokens (which belong to other users).
+	OwnerCanTransfer,
+	/// Can the collection owner burn other people's tokens.
+	OwnerCanDestroy,
+	/// Is it possible to send tokens from this collection between users.
+	TransferEnabled
+}
+
+/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+interface ERC721Metadata is Dummy, ERC165 {
+	// /// @notice A descriptive name for a collection of NFTs in this contract
+	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
+	// /// @dev EVM selector for this function is: 0x06fdde03,
+	// ///  or in textual repr: name()
+	// function name() external view returns (string memory);
+
+	// /// @notice An abbreviated name for NFTs in this contract
+	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
+	// /// @dev EVM selector for this function is: 0x95d89b41,
+	// ///  or in textual repr: symbol()
+	// function symbol() external view returns (string memory);
+
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) external view returns (string memory);
+}
+
+/// @title ERC721 Token that can be irreversibly burned (destroyed).
+/// @dev the ERC-165 identifier for this interface is 0x42966c68
+interface ERC721Burnable is Dummy, ERC165 {
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
+	///  operator of the current owner.
+	/// @param tokenId The RFT to approve
+	/// @dev EVM selector for this function is: 0x42966c68,
+	///  or in textual repr: burn(uint256)
+	function burn(uint256 tokenId) external;
+}
+
+/// @title ERC721 minting logic.
+/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
+interface ERC721UniqueMintable is Dummy, ERC165 {
+	/// @notice Function to mint a token.
+	/// @param to The new owner
+	/// @return uint256 The id of the newly minted token
+	/// @dev EVM selector for this function is: 0x6a627842,
+	///  or in textual repr: mint(address)
+	function mint(address to) external returns (uint256);
+
+	// /// @notice Function to mint a token.
+	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
+	// ///  unlike standard, you can't specify it manually
+	// /// @param to The new owner
+	// /// @param tokenId ID of the minted RFT
+	// /// @dev EVM selector for this function is: 0x40c10f19,
+	// ///  or in textual repr: mint(address,uint256)
+	// function mint(address to, uint256 tokenId) external returns (bool);
+
+	/// @notice Function to mint token with the given tokenUri.
+	/// @param to The new owner
+	/// @param tokenUri Token URI that would be stored in the NFT properties
+	/// @return uint256 The id of the newly minted token
+	/// @dev EVM selector for this function is: 0x45c17782,
+	///  or in textual repr: mintWithTokenURI(address,string)
+	function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);
+	// /// @notice Function to mint token with the given tokenUri.
+	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
+	// ///  unlike standard, you can't specify it manually
+	// /// @param to The new owner
+	// /// @param tokenId ID of the minted RFT
+	// /// @param tokenUri Token URI that would be stored in the RFT properties
+	// /// @dev EVM selector for this function is: 0x50bb4e7f,
+	// ///  or in textual repr: mintWithTokenURI(address,uint256,string)
+	// function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);
+
+}
+
+/// @title Unique extensions for ERC721.
+/// @dev the ERC-165 identifier for this interface is 0x4abaabdb
+interface ERC721UniqueExtensions is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of NFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @notice An abbreviated name for NFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @notice A description for the collection.
+	/// @dev EVM selector for this function is: 0x7284e416,
+	///  or in textual repr: description()
+	function description() external view returns (string memory);
+
+	// /// Returns the owner (in cross format) of the token.
+	// ///
+	// /// @param tokenId Id for the token.
+	// /// @dev EVM selector for this function is: 0x2b29dace,
+	// ///  or in textual repr: crossOwnerOf(uint256)
+	// function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+
+	/// Returns the owner (in cross format) of the token.
+	///
+	/// @param tokenId Id for the token.
+	/// @dev EVM selector for this function is: 0xcaa3a4d0,
+	///  or in textual repr: ownerOfCross(uint256)
+	function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
+
+	/// @notice Count all RFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
+
+	/// Returns the token properties.
+	///
+	/// @param tokenId Id for the token.
+	/// @param keys Properties keys. Empty keys for all propertyes.
+	/// @return Vector of properties key/value pairs.
+	/// @dev EVM selector for this function is: 0xe07ede7e,
+	///  or in textual repr: properties(uint256,string[])
+	function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory);
+
+	/// @notice Transfer ownership of an RFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param to The new owner
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
+	function transfer(address to, uint256 tokenId) external;
+
+	/// @notice Transfer ownership of an RFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param to The new owner
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0x2ada85ff,
+	///  or in textual repr: transferCross((address,uint256),uint256)
+	function transferCross(CrossAddress memory to, uint256 tokenId) external;
+
+	/// @notice Transfer ownership of an RFT
+	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	///  is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param to The new owner
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0xd5cf430b,
+	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+	function transferFromCross(
+		CrossAddress memory from,
+		CrossAddress memory to,
+		uint256 tokenId
+	) external;
+
+	// /// @notice Burns a specific ERC721 token.
+	// /// @dev Throws unless `msg.sender` is the current owner or an authorized
+	// ///  operator for this RFT. Throws if `from` is not the current owner. Throws
+	// ///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	// ///  Throws if RFT pieces have multiple owners.
+	// /// @param from The current owner of the RFT
+	// /// @param tokenId The RFT to transfer
+	// /// @dev EVM selector for this function is: 0x79cc6790,
+	// ///  or in textual repr: burnFrom(address,uint256)
+	// function burnFrom(address from, uint256 tokenId) external;
+
+	/// @notice Burns a specific ERC721 token.
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this RFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param from The current owner of the RFT
+	/// @param tokenId The RFT to transfer
+	/// @dev EVM selector for this function is: 0xbb2f5a58,
+	///  or in textual repr: burnFromCross((address,uint256),uint256)
+	function burnFromCross(CrossAddress memory from, uint256 tokenId) external;
+
+	/// @notice Returns next free RFT ID.
+	/// @dev EVM selector for this function is: 0x75794a3c,
+	///  or in textual repr: nextTokenId()
+	function nextTokenId() external view returns (uint256);
+
+	// /// @notice Function to mint multiple tokens.
+	// /// @dev `tokenIds` should be an array of consecutive numbers and first number
+	// ///  should be obtained with `nextTokenId` method
+	// /// @param to The new owner
+	// /// @param tokenIds IDs of the minted RFTs
+	// /// @dev EVM selector for this function is: 0x44a9945e,
+	// ///  or in textual repr: mintBulk(address,uint256[])
+	// function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);
+
+	/// @notice Function to mint a token.
+	/// @param tokenProperties Properties of minted token
+	/// @dev EVM selector for this function is: 0xdf7a5db7,
+	///  or in textual repr: mintBulkCross((((address,uint256),uint128)[],(string,bytes)[])[])
+	function mintBulkCross(MintTokenData[] memory tokenProperties) external returns (bool);
+
+	// /// @notice Function to mint multiple tokens with the given tokenUris.
+	// /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	// ///  numbers and first number should be obtained with `nextTokenId` method
+	// /// @param to The new owner
+	// /// @param tokens array of pairs of token ID and token URI for minted tokens
+	// /// @dev EVM selector for this function is: 0x36543006,
+	// ///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
+	// function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool);
+
+	/// @notice Function to mint a token.
+	/// @param to The new owner crossAccountId
+	/// @param properties Properties of minted token
+	/// @return uint256 The id of the newly minted token
+	/// @dev EVM selector for this function is: 0xb904db03,
+	///  or in textual repr: mintCross((address,uint256),(string,bytes)[])
+	function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256);
+
+	/// Returns EVM address for refungible token
+	///
+	/// @param token ID of the token
+	/// @dev EVM selector for this function is: 0xab76fac6,
+	///  or in textual repr: tokenContractAddress(uint256)
+	function tokenContractAddress(uint256 token) external view returns (address);
+
+	/// @notice Returns collection helper contract address
+	/// @dev EVM selector for this function is: 0x1896cce6,
+	///  or in textual repr: collectionHelperAddress()
+	function collectionHelperAddress() external view returns (address);
+}
+
+/// Data for creation token with uri.
+struct TokenUri {
+	/// Id of new token.
+	uint256 id;
+	/// Uri of new token.
+	string uri;
+}
+
+/// Token minting parameters
+struct MintTokenData {
+	/// Minted token owner and number of pieces
+	OwnerPieces[] owners;
+	/// Minted token properties
+	Property[] properties;
+}
+
+/// Token minting parameters
+struct OwnerPieces {
+	/// Minted token owner
+	CrossAddress owner;
+	/// Number of token pieces
+	uint128 pieces;
+}
+
+/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+	/// @notice Enumerate valid RFTs
+	/// @param index A counter less than `totalSupply()`
+	/// @return The token identifier for the `index`th NFT,
+	///  (sort order not specified)
+	/// @dev EVM selector for this function is: 0x4f6ccce7,
+	///  or in textual repr: tokenByIndex(uint256)
+	function tokenByIndex(uint256 index) external view returns (uint256);
+
+	/// Not implemented
+	/// @dev EVM selector for this function is: 0x2f745c59,
+	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
+	function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
+
+	/// @notice Count RFTs tracked by this contract
+	/// @return A count of valid RFTs tracked by this contract, where each one of
+	///  them has an assigned and queryable owner not equal to the zero address
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+}
+
+/// @dev inlined interface
+interface ERC721Events {
+	event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
+	event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
+	event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
+}
+
+/// @title ERC-721 Non-Fungible Token Standard
+/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
+interface ERC721 is Dummy, ERC165, ERC721Events {
+	/// @notice Count all RFTs assigned to an owner
+	/// @dev RFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
+
+	/// @notice Find the owner of an RFT
+	/// @dev RFTs assigned to zero address are considered invalid, and queries
+	///  about them do throw.
+	///  Returns special 0xffffffffffffffffffffffffffffffffffffffff address for
+	///  the tokens that are partially owned.
+	/// @param tokenId The identifier for an RFT
+	/// @return The address of the owner of the RFT
+	/// @dev EVM selector for this function is: 0x6352211e,
+	///  or in textual repr: ownerOf(uint256)
+	function ownerOf(uint256 tokenId) external view returns (address);
+
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0xb88d4fde,
+	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId,
+		bytes memory data
+	) external;
+
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x42842e0e,
+	///  or in textual repr: safeTransferFrom(address,address,uint256)
+	function safeTransferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
+
+	/// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
+	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+	///  THEY MAY BE PERMANENTLY LOST
+	/// @dev Throws unless `msg.sender` is the current owner or an authorized
+	///  operator for this RFT. Throws if `from` is not the current owner. Throws
+	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	///  Throws if RFT pieces have multiple owners.
+	/// @param from The current owner of the NFT
+	/// @param to The new owner
+	/// @param tokenId The NFT to transfer
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 tokenId
+	) external;
+
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address approved, uint256 tokenId) external;
+
+	/// @notice Sets or unsets the approval of a given operator.
+	///  The `operator` is allowed to transfer all token pieces of the `caller` on their behalf.
+	/// @param operator Operator
+	/// @param approved Should operator status be granted or revoked?
+	/// @dev EVM selector for this function is: 0xa22cb465,
+	///  or in textual repr: setApprovalForAll(address,bool)
+	function setApprovalForAll(address operator, bool approved) external;
+
+	/// @dev Not implemented
+	/// @dev EVM selector for this function is: 0x081812fc,
+	///  or in textual repr: getApproved(uint256)
+	function getApproved(uint256 tokenId) external view returns (address);
+
+	/// @notice Tells whether the given `owner` approves the `operator`.
+	/// @dev EVM selector for this function is: 0xe985e9c5,
+	///  or in textual repr: isApprovedForAll(address,address)
+	function isApprovedForAll(address owner, address operator) external view returns (bool);
+}
+
+interface UniqueRefungible is
+	Dummy,
+	ERC165,
+	ERC721,
+	ERC721Enumerable,
+	ERC721UniqueExtensions,
+	ERC721UniqueMintable,
+	ERC721Burnable,
+	ERC721Metadata,
+	Collection,
+	TokenProperties
+{}
addedjs-packages/tests/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/api/UniqueRefungibleToken.sol
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: OTHER
+// This code is automatically generated
+
+pragma solidity >=0.8.0 <0.9.0;
+
+/// @dev common stubs holder
+interface Dummy {
+
+}
+
+interface ERC165 is Dummy {
+	function supportsInterface(bytes4 interfaceID) external view returns (bool);
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x5755c3f2
+interface ERC1633 is Dummy, ERC165 {
+	/// @dev EVM selector for this function is: 0x80a54001,
+	///  or in textual repr: parentToken()
+	function parentToken() external view returns (address);
+
+	/// @dev EVM selector for this function is: 0xd7f083f3,
+	///  or in textual repr: parentTokenId()
+	function parentTokenId() external view returns (uint256);
+}
+
+/// @dev the ERC-165 identifier for this interface is 0xedd3a564
+interface ERC20UniqueExtensions is Dummy, ERC165 {
+	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
+	/// @param owner crossAddress The address which owns the funds.
+	/// @param spender crossAddress The address which will spend the funds.
+	/// @return A uint256 specifying the amount of tokens still available for the spender.
+	/// @dev EVM selector for this function is: 0xe0af4bd7,
+	///  or in textual repr: allowanceCross((address,uint256),(address,uint256))
+	function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256);
+
+	// /// @dev Function that burns an amount of the token of a given account,
+	// /// deducting from the sender's allowance for said account.
+	// /// @param from The account whose tokens will be burnt.
+	// /// @param amount The amount that will be burnt.
+	// /// @dev EVM selector for this function is: 0x79cc6790,
+	// ///  or in textual repr: burnFrom(address,uint256)
+	// function burnFrom(address from, uint256 amount) external returns (bool);
+
+	/// @dev Function that burns an amount of the token of a given account,
+	/// deducting from the sender's allowance for said account.
+	/// @param from The account whose tokens will be burnt.
+	/// @param amount The amount that will be burnt.
+	/// @dev EVM selector for this function is: 0xbb2f5a58,
+	///  or in textual repr: burnFromCross((address,uint256),uint256)
+	function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool);
+
+	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
+	/// Beware that changing an allowance with this method brings the risk that someone may use both the old
+	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
+	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
+	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+	/// @param spender The crossaccount which will spend the funds.
+	/// @param amount The amount of tokens to be spent.
+	/// @dev EVM selector for this function is: 0x0ecd0ab0,
+	///  or in textual repr: approveCross((address,uint256),uint256)
+	function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool);
+
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
+
+	/// @dev Function that changes total amount of the tokens.
+	///  Throws if `msg.sender` doesn't owns all of the tokens.
+	/// @param amount New total amount of the tokens.
+	/// @dev EVM selector for this function is: 0xd2418ca7,
+	///  or in textual repr: repartition(uint256)
+	function repartition(uint256 amount) external returns (bool);
+
+	/// @dev Transfer token for a specified address
+	/// @param to The crossaccount to transfer to.
+	/// @param amount The amount to be transferred.
+	/// @dev EVM selector for this function is: 0x2ada85ff,
+	///  or in textual repr: transferCross((address,uint256),uint256)
+	function transferCross(CrossAddress memory to, uint256 amount) external returns (bool);
+
+	/// @dev Transfer tokens from one address to another
+	/// @param from The address which you want to send tokens from
+	/// @param to The address which you want to transfer to
+	/// @param amount the amount of tokens to be transferred
+	/// @dev EVM selector for this function is: 0xd5cf430b,
+	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
+	function transferFromCross(
+		CrossAddress memory from,
+		CrossAddress memory to,
+		uint256 amount
+	) external returns (bool);
+}
+
+/// Cross account struct
+struct CrossAddress {
+	address eth;
+	uint256 sub;
+}
+
+/// @dev inlined interface
+interface ERC20Events {
+	event Transfer(address indexed from, address indexed to, uint256 value);
+	event Approval(address indexed owner, address indexed spender, uint256 value);
+}
+
+/// @title Standard ERC20 token
+///
+/// @dev Implementation of the basic standard token.
+/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
+/// @dev the ERC-165 identifier for this interface is 0x942e8b22
+interface ERC20 is Dummy, ERC165, ERC20Events {
+	/// @return the name of the token.
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @return the symbol of the token.
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @dev Total number of tokens in existence
+	/// @dev EVM selector for this function is: 0x18160ddd,
+	///  or in textual repr: totalSupply()
+	function totalSupply() external view returns (uint256);
+
+	/// @dev Not supported
+	/// @dev EVM selector for this function is: 0x313ce567,
+	///  or in textual repr: decimals()
+	function decimals() external view returns (uint8);
+
+	/// @dev Gets the balance of the specified address.
+	/// @param owner The address to query the balance of.
+	/// @return An uint256 representing the amount owned by the passed address.
+	/// @dev EVM selector for this function is: 0x70a08231,
+	///  or in textual repr: balanceOf(address)
+	function balanceOf(address owner) external view returns (uint256);
+
+	/// @dev Transfer token for a specified address
+	/// @param to The address to transfer to.
+	/// @param amount The amount to be transferred.
+	/// @dev EVM selector for this function is: 0xa9059cbb,
+	///  or in textual repr: transfer(address,uint256)
+	function transfer(address to, uint256 amount) external returns (bool);
+
+	/// @dev Transfer tokens from one address to another
+	/// @param from address The address which you want to send tokens from
+	/// @param to address The address which you want to transfer to
+	/// @param amount uint256 the amount of tokens to be transferred
+	/// @dev EVM selector for this function is: 0x23b872dd,
+	///  or in textual repr: transferFrom(address,address,uint256)
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) external returns (bool);
+
+	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
+	/// Beware that changing an allowance with this method brings the risk that someone may use both the old
+	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
+	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
+	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+	/// @param spender The address which will spend the funds.
+	/// @param amount The amount of tokens to be spent.
+	/// @dev EVM selector for this function is: 0x095ea7b3,
+	///  or in textual repr: approve(address,uint256)
+	function approve(address spender, uint256 amount) external returns (bool);
+
+	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
+	/// @param owner address The address which owns the funds.
+	/// @param spender address The address which will spend the funds.
+	/// @return A uint256 specifying the amount of tokens still available for the spender.
+	/// @dev EVM selector for this function is: 0xdd62ed3e,
+	///  or in textual repr: allowance(address,address)
+	function allowance(address owner, address spender) external view returns (uint256);
+}
+
+interface UniqueRefungibleToken is Dummy, ERC165, ERC20, ERC20UniqueExtensions, ERC1633 {}
addedjs-packages/tests/eth/base.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/base.test.ts
@@ -0,0 +1,127 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itEth, usingEthPlaygrounds, expect} from './util/index.js';
+import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
+
+
+describe('Contract calls', () => {
+  let donor: IKeyringPair;
+
+  before(async function () {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Call of simple contract fee is less than 0.2 UNQ', async ({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const flipper = await helper.eth.deployFlipper(deployer);
+
+    const cost = await helper.eth.calculateFee({Ethereum: deployer}, () => flipper.methods.flip().send({from: deployer}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true;
+  });
+
+  itEth('Balance transfer fee is less than 0.2 UNQ', async ({helper}) => {
+    const userA = await helper.eth.createAccountWithBalance(donor);
+    const userB = helper.eth.createAccount();
+    const cost = await helper.eth.calculateFee({Ethereum: userA}, () => helper.getWeb3().eth.sendTransaction({
+      from: userA,
+      to: userB,
+      value: '1000000',
+      gas: helper.eth.DEFAULT_GAS,
+    }));
+    const balanceB = await helper.balance.getEthereum(userB);
+    expect(cost - balanceB < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true;
+  });
+
+  itEth('NFT transfer is close to 0.15 UNQ', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const [alice] = await helper.arrange.createAccounts([10n], donor);
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
+
+    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, tokenId).send(caller));
+
+    const fee = Number(cost) / Number(helper.balance.getOneTokenNominal());
+    const expectedFee = 0.15;
+    const tolerance = 0.001;
+
+    expect(Math.abs(fee - expectedFee)).to.be.lessThan(tolerance);
+  });
+});
+
+describe('ERC165 tests', () => {
+  // https://eips.ethereum.org/EIPS/eip-165
+
+  let erc721MetadataCompatibleNftCollectionId: number;
+  let simpleNftCollectionId: number;
+  let minter: string;
+
+  const BASE_URI = 'base/';
+
+  async function checkInterface(helper: EthUniqueHelper, interfaceId: string, simpleResult: boolean, compatibleResult: boolean) {
+    const simple = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(simpleNftCollectionId), 'nft', minter);
+    const compatible = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(erc721MetadataCompatibleNftCollectionId), 'nft', minter);
+
+    expect(await simple.methods.supportsInterface(interfaceId).call()).to.equal(simpleResult, `empty (not ERC721Metadata compatible) NFT collection returns not ${simpleResult}`);
+    expect(await compatible.methods.supportsInterface(interfaceId).call()).to.equal(compatibleResult, `ERC721Metadata compatible NFT collection returns not ${compatibleResult}`);
+  }
+
+  before(async () => {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      const [alice] = await helper.arrange.createAccounts([10n], donor);
+      ({collectionId: simpleNftCollectionId} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}));
+      minter = await helper.eth.createAccountWithBalance(donor);
+      ({collectionId: erc721MetadataCompatibleNftCollectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(minter, 'n', 'd', 'p', BASE_URI));
+    });
+  });
+
+  itEth('nonexistent interfaceID - 0xffffffff - always false', async ({helper}) => {
+    await checkInterface(helper, '0xffffffff', false, false);
+  });
+
+  itEth('ERC721 - 0x780e9d63 - support', async ({helper}) => {
+    await checkInterface(helper, '0x780e9d63', true, true);
+  });
+
+  itEth('ERC721Metadata - 0x5b5e139f - support', async ({helper}) => {
+    await checkInterface(helper, '0x5b5e139f', false, true);
+  });
+
+  itEth('ERC721Enumerable - 0x780e9d63 - support', async ({helper}) => {
+    await checkInterface(helper, '0x780e9d63', true, true);
+  });
+
+  itEth.skip('ERC721UniqueExtensions support', async ({helper}) => {
+    await checkInterface(helper, '0xb74c26b7', true, true);
+  });
+
+  itEth('ERC721Burnable - 0x42966c68 - support', async ({helper}) => {
+    await checkInterface(helper, '0x42966c68', true, true);
+  });
+
+  itEth('ERC165 - 0x01ffc9a7 - support', async ({helper}) => {
+    await checkInterface(helper, '0x01ffc9a7', true, true);
+  });
+});
addedjs-packages/tests/eth/collectionAdmin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/collectionAdmin.test.ts
@@ -0,0 +1,469 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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 {usingEthPlaygrounds, itEth} from './util/index.js';
+import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
+import {CreateCollectionData} from './util/playgrounds/types.js';
+
+async function recordEthFee(helper: EthUniqueHelper, userAddress: string, call: () => Promise<any>) {
+  const before = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress));
+  await call();
+  await helper.wait.newBlocks(1);
+  const after = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress));
+
+  expect(after < before).to.be.true;
+
+  return before - after;
+}
+
+describe('Add collection admins', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {
+      // arrange
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const adminSub = await privateKey('//admin2');
+      const adminEth = helper.eth.createAccount().toLowerCase();
+
+      const adminDeprecated = helper.eth.createAccount().toLowerCase();
+      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
+      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
+
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
+
+      // Check isOwnerOrAdminCross returns false:
+      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.false;
+      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.false;
+      expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.false;
+
+      // Soft-deprecated: can addCollectionAdmin
+      await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();
+      // Can addCollectionAdminCross for substrate and ethereum address
+      await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
+      await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
+
+      // 1. Expect api.rpc.unique.adminlist returns admins:
+      const adminListRpc = await helper.collection.getAdmins(collectionId);
+      expect(adminListRpc).to.has.length(3);
+      expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}, {Ethereum: adminDeprecated}]);
+
+      // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist
+      let adminListEth = await collectionEvm.methods.collectionAdmins().call();
+      adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));
+      expect(adminListRpc).to.be.like(adminListEth);
+
+      // 3. check isOwnerOrAdminCross returns true:
+      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;
+      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;
+      expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.true;
+    });
+  });
+
+  itEth('cross account admin can mint', async ({helper}) => {
+    // arrange: create collection and accounts
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', 'uri');
+    const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
+    const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
+    const [adminSub] = await helper.arrange.createAccounts([100n], donor);
+    const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    // cannot mint while not admin
+    await expect(collectionEvm.methods.mint(owner).send({from: adminEth})).to.be.rejected;
+    await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);
+
+    // admin (sub and eth) can mint token:
+    await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
+    await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
+    await collectionEvm.methods.mint(owner).send({from: adminEth});
+    await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});
+
+    expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);
+  });
+
+  itEth('cannot add invalid cross account admin', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);
+
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    const adminCross = {
+      eth: helper.address.substrateToEth(admin.address),
+      sub: admin.addressRaw,
+    };
+    await expect(collectionEvm.methods.addCollectionAdminCross(adminCross).send()).to.be.rejected;
+  });
+
+  itEth('can verify owner with methods.isOwnerOrAdmin[Cross]', async ({helper, privateKey}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const adminDeprecated = helper.eth.createAccount();
+    const admin1Cross = helper.ethCrossAccount.fromKeyringPair(await privateKey('admin'));
+    const admin2Cross = helper.ethCrossAccount.fromAddress(helper.address.substrateToEth((await privateKey('admin3')).address));
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    // Soft-deprecated:
+    expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.false;
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.false;
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.false;
+
+    await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();
+    await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send();
+    await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send();
+
+    // Soft-deprecated: isOwnerOrAdmin returns true
+    expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.true;
+    // Expect isOwnerOrAdminCross return true
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.true;
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.true;
+  });
+
+  // Soft-deprecated
+  itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const admin = await helper.eth.createAccountWithBalance(donor);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+    await collectionEvm.methods.addCollectionAdmin(admin).send();
+
+    const user = helper.eth.createAccount();
+    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+    expect(adminList.length).to.be.eq(1);
+    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+      .to.be.eq(admin.toLocaleLowerCase());
+  });
+
+  // Soft-deprecated
+  itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const notAdmin = await helper.eth.createAccountWithBalance(donor);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    const user = helper.eth.createAccount();
+    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+    expect(adminList.length).to.be.eq(0);
+  });
+
+  itEth('(!negative tests!) Add [cross] admin by ADMIN is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const [admin, notAdmin] = await helper.arrange.createAccounts([10n, 10n], donor);
+    const adminCross = helper.ethCrossAccount.fromKeyringPair(admin);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    await collectionEvm.methods.addCollectionAdminCross(adminCross).send();
+
+    const notAdminCross = helper.ethCrossAccount.fromKeyringPair(notAdmin);
+    await expect(collectionEvm.methods.addCollectionAdminCross(notAdminCross).call({from: adminCross.eth}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+    expect(adminList.length).to.be.eq(1);
+
+    const admin0Cross = helper.ethCrossAccount.fromKeyringPair(adminList[0]);
+    expect(admin0Cross.eth.toLocaleLowerCase())
+      .to.be.eq(adminCross.eth.toLocaleLowerCase());
+  });
+
+  itEth('(!negative tests!) Add [cross] admin by USER is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const notAdmin0 = await helper.eth.createAccountWithBalance(donor);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    const [notAdmin1] = await helper.arrange.createAccounts([10n], donor);
+    const notAdmin1Cross = helper.ethCrossAccount.fromKeyringPair(notAdmin1);
+    await expect(collectionEvm.methods.addCollectionAdminCross(notAdmin1Cross).call({from: notAdmin0}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+    expect(adminList.length).to.be.eq(0);
+  });
+});
+
+describe('Remove collection admins', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  // Soft-deprecated
+  itEth('Remove admin by owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const newAdmin = helper.eth.createAccount();
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();
+
+    {
+      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+      expect(adminList.length).to.be.eq(1);
+      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+        .to.be.eq(newAdmin.toLocaleLowerCase());
+    }
+
+    await collectionEvm.methods.removeCollectionAdmin(newAdmin).send();
+    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+    expect(adminList.length).to.be.eq(0);
+  });
+
+  itEth('Remove [cross] admin by owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const [adminSub] = await helper.arrange.createAccounts([10n], donor);
+    const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
+    const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
+    const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
+
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
+    await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
+
+    {
+      const adminList = await helper.collection.getAdmins(collectionId);
+      expect(adminList).to.deep.include({Substrate: adminSub.address});
+      expect(adminList).to.deep.include({Ethereum: adminEth});
+    }
+
+    await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();
+    await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();
+    const adminList = await helper.collection.getAdmins(collectionId);
+    expect(adminList.length).to.be.eq(0);
+
+    // Non admin cannot mint:
+    await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);
+    await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;
+  });
+
+  // Soft-deprecated
+  itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    const admin0 = await helper.eth.createAccountWithBalance(donor);
+    await collectionEvm.methods.addCollectionAdmin(admin0).send();
+    const admin1 = await helper.eth.createAccountWithBalance(donor);
+    await collectionEvm.methods.addCollectionAdmin(admin1).send();
+
+    await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0}))
+      .to.be.rejectedWith('NoPermission');
+    {
+      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+      expect(adminList.length).to.be.eq(2);
+      expect(adminList.toString().toLocaleLowerCase())
+        .to.be.deep.contains(admin0.toLocaleLowerCase())
+        .to.be.deep.contains(admin1.toLocaleLowerCase());
+    }
+  });
+
+  // Soft-deprecated
+  itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    const admin = await helper.eth.createAccountWithBalance(donor);
+    await collectionEvm.methods.addCollectionAdmin(admin).send();
+    const notAdmin = helper.eth.createAccount();
+
+    await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin}))
+      .to.be.rejectedWith('NoPermission');
+    {
+      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
+        .to.be.eq(admin.toLocaleLowerCase());
+      expect(adminList.length).to.be.eq(1);
+    }
+  });
+
+  itEth('(!negative tests!) Remove [cross] admin by ADMIN is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const [admin1] = await helper.arrange.createAccounts([10n], donor);
+    const admin1Cross = helper.ethCrossAccount.fromKeyringPair(admin1);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send();
+
+    const [admin2] = await helper.arrange.createAccounts([10n], donor);
+    const admin2Cross = helper.ethCrossAccount.fromKeyringPair(admin2);
+    await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send();
+
+    await expect(collectionEvm.methods.removeCollectionAdminCross(admin1Cross).call({from: admin2Cross.eth}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+    expect(adminList.length).to.be.eq(2);
+    expect(adminList.toString().toLocaleLowerCase())
+      .to.be.deep.contains(admin1.address.toLocaleLowerCase())
+      .to.be.deep.contains(admin2.address.toLocaleLowerCase());
+  });
+
+  itEth('(!negative tests!) Remove [cross] admin by USER is not allowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+    const [adminSub] = await helper.arrange.createAccounts([10n], donor);
+    const adminSubCross = helper.ethCrossAccount.fromKeyringPair(adminSub);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    await collectionEvm.methods.addCollectionAdminCross(adminSubCross).send();
+    const notAdminEth = await helper.eth.createAccountWithBalance(donor);
+
+    await expect(collectionEvm.methods.removeCollectionAdminCross(adminSubCross).call({from: notAdminEth}))
+      .to.be.rejectedWith('NoPermission');
+
+    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
+    expect(adminList.length).to.be.eq(1);
+    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())
+      .to.be.eq(adminSub.address.toLocaleLowerCase());
+  });
+});
+
+// Soft-deprecated
+describe('Change owner tests', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Change owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const newOwner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    await collectionEvm.methods.changeCollectionOwner(newOwner).send();
+
+    expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.false;
+    expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.true;
+  });
+
+  itEth('change owner call fee', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const newOwner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+    const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.changeCollectionOwner(newOwner).send());
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+    expect(cost > 0);
+  });
+
+  itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const newOwner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+    await expect(collectionEvm.methods.changeCollectionOwner(newOwner).send({from: newOwner})).to.be.rejected;
+    expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.false;
+  });
+});
+
+describe('Change substrate owner tests', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Change owner [cross]', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const ownerEth = await helper.eth.createAccountWithBalance(donor);
+    const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);
+    const [ownerSub] = await helper.arrange.createAccounts([10n], donor);
+    const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);
+
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.false;
+
+    // Can set ethereum owner:
+    await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossEth).send({from: owner});
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossEth).call()).to.be.true;
+    expect(await helper.collection.getData(collectionId))
+      .to.have.property('normalizedOwner').that.is.eq(helper.address.ethToSubstrate(ownerEth));
+
+    // Can set Substrate owner:
+    await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossSub).send({from: ownerEth});
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.true;
+    expect(await helper.collection.getData(collectionId))
+      .to.have.property('normalizedOwner').that.is.eq(helper.address.normalizeSubstrate(ownerSub.address));
+  });
+
+  itEth.skip('change owner call fee', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const [newOwner] = await helper.arrange.createAccounts([10n], donor);
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send());
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+    expect(cost > 0);
+  });
+
+  itEth('(!negative tests!) call setOwner by non owner [cross]', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const otherReceiver = await helper.eth.createAccountWithBalance(donor);
+    const [newOwner] = await helper.arrange.createAccounts([10n], donor);
+    const newOwnerCross = helper.ethCrossAccount.fromKeyringPair(newOwner);
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    await expect(collectionEvm.methods.changeCollectionOwnerCross(newOwnerCross).send({from: otherReceiver})).to.be.rejected;
+    expect(await collectionEvm.methods.isOwnerOrAdminCross(newOwnerCross).call()).to.be.false;
+  });
+});
addedjs-packages/tests/eth/collectionHelperAddress.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/collectionHelperAddress.test.ts
@@ -0,0 +1,70 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {itEth, usingEthPlaygrounds, expect} from './util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {COLLECTION_HELPER, Pallets} from '../util/index.js';
+
+describe('[eth]CollectionHelperAddress test: ERC20/ERC721 ', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('NFT', async ({helper}) => {
+    const owner =  await helper.eth.createAccountWithBalance(donor);
+
+    const {collectionAddress: nftCollectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+    const nftCollection = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner);
+
+    expect((await nftCollection.methods.collectionHelperAddress().call())
+      .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER);
+  });
+
+  itEth.ifWithPallets('RFT ', [Pallets.ReFungible], async ({helper}) => {
+    const owner =  await helper.eth.createAccountWithBalance(donor);
+
+    const {collectionAddress: rftCollectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+
+    const rftCollection = await helper.ethNativeContract.collection(rftCollectionAddress, 'rft', owner);
+    expect((await rftCollection.methods.collectionHelperAddress().call())
+      .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER);
+  });
+
+  itEth('FT', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', 18, 'absolutely anything', 'ROC');
+    const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    expect((await collection.methods.collectionHelperAddress().call())
+      .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER);
+  });
+
+  itEth('[collectionHelpers] convert collectionId into address', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionId = 7;
+    const collectionAddress = helper.ethAddress.fromCollectionId(collectionId);
+    const helperContract = await helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await helperContract.methods.collectionAddress(collectionId).call()).to.be.equal(collectionAddress);
+    expect(parseInt(await helperContract.methods.collectionId(collectionAddress).call())).to.be.equal(collectionId);
+  });
+
+});
addedjs-packages/tests/eth/collectionLimits.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/collectionLimits.test.ts
@@ -0,0 +1,145 @@
+import type {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../util/index.js';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import {CollectionLimitField, CreateCollectionData} from './util/playgrounds/types.js';
+
+
+describe('Can set collection limits', () => {
+  let donor: IKeyringPair;
+
+  before(async () => {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  [
+    {case: 'nft' as const},
+    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {case: 'ft' as const},
+  ].map(testCase =>
+    itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send();
+      const limits = {
+        accountTokenOwnershipLimit: 1000,
+        sponsoredDataSize: 1024,
+        sponsoredDataRateLimit: 30,
+        tokenLimit: 1000000,
+        sponsorTransferTimeout: 6,
+        sponsorApproveTimeout: 6,
+        ownerCanTransfer: 1,
+        ownerCanDestroy: 0,
+        transfersEnabled: 0,
+      };
+
+      const expectedLimits = {
+        accountTokenOwnershipLimit: 1000,
+        sponsoredDataSize: 1024,
+        sponsoredDataRateLimit: {blocks: 30},
+        tokenLimit: 1000000,
+        sponsorTransferTimeout: 6,
+        sponsorApproveTimeout: 6,
+        ownerCanTransfer: true,
+        ownerCanDestroy: false,
+        transfersEnabled: false,
+      };
+
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: limits.accountTokenOwnershipLimit}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: limits.sponsoredDataSize}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: limits.sponsoredDataRateLimit}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TokenLimit, value: {status: true, value: limits.tokenLimit}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorTransferTimeout, value: {status: true, value: limits.sponsorTransferTimeout}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorApproveTimeout, value: {status: true, value: limits.sponsorApproveTimeout}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: limits.ownerCanTransfer}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanDestroy, value: {status: true, value: limits.ownerCanDestroy}}).send();
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: limits.transfersEnabled}}).send();
+
+      // Check limits from sub:
+      const data = (await helper.rft.getData(collectionId))!;
+      expect(data.raw.limits).to.deep.eq(expectedLimits);
+      expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits);
+      // Check limits from eth:
+      const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner});
+      expect(limitsEvm).to.have.length(9);
+      expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]);
+      expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]);
+      expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]);
+      expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]);
+      expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]);
+      expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]);
+      expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]);
+      expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]);
+      expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]);
+    }));
+});
+
+describe('Cannot set invalid collection limits', () => {
+  let donor: IKeyringPair;
+
+  before(async () => {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  [
+    {case: 'nft' as const},
+    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {case: 'ft' as const},
+  ].map(testCase =>
+    itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
+      const invalidLimits = {
+        accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER),
+        transfersEnabled: 3,
+      };
+
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'ISNI', testCase.case, 18)).send();
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
+
+      // Cannot set non-existing limit
+      await expect(collectionEvm.methods
+        .setCollectionLimit({field: 9, value: {status: true, value: 1}})
+        .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert value not convertible into enum "CollectionLimitField"');
+
+      // Cannot disable limits
+      await expect(collectionEvm.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: false, value: 0}})
+        .call()).to.be.rejectedWith('user can\'t disable limits');
+
+      await expect(collectionEvm.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: invalidLimits.accountTokenOwnershipLimit}})
+        .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`);
+
+      await expect(collectionEvm.methods
+        .setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: 3}})
+        .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`);
+
+      expect(() => collectionEvm.methods
+        .setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: -1}}).send()).to.throw('value out-of-bounds');
+    }));
+
+  [
+    {case: 'nft' as const, requiredPallets: []},
+    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {case: 'ft' as const, requiredPallets: []},
+  ].map(testCase =>
+    itEth.ifWithPallets(`Non-owner and non-admin cannot set collection limits for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const nonOwner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send();
+
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
+      await expect(collectionEvm.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .call({from: nonOwner}))
+        .to.be.rejectedWith('NoPermission');
+
+      await expect(collectionEvm.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .send({from: nonOwner}))
+        .to.be.rejected;
+    }));
+});
addedjs-packages/tests/eth/collectionProperties.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/collectionProperties.test.ts
@@ -0,0 +1,215 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {itEth, usingEthPlaygrounds, expect} from './util/index.js';
+import {Pallets} from '../util/index.js';
+import type {IProperty, ITokenPropertyPermission} from '@unique/playgrounds/types.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+
+describe('EVM collection properties', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await _helper.arrange.createAccounts([50n], donor);
+    });
+  });
+
+  // Soft-deprecated: setCollectionProperty
+  [
+    {method: 'setCollectionProperties', mode: 'nft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
+    {method: 'setCollectionProperties', mode: 'rft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
+    {method: 'setCollectionProperties', mode: 'ft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
+    {method: 'setCollectionProperty', mode: 'nft' as const, methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`Collection properties can be set: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});
+      await collection.addAdmin(alice, {Ethereum: caller});
+
+      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty');
+
+      // collectionProperties returns an empty array if no properties:
+      expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like([]);
+      expect(await collectionEvm.methods.collectionProperties(['NonExistingKey']).call()).to.be.like([]);
+
+      await collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller});
+
+      const raw = (await collection.getData())?.raw;
+      expect(raw.properties).to.deep.equal(testCase.expectedProps);
+
+      // collectionProperties returns properties:
+      expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));
+    }));
+
+  itEth('Cannot set invalid properties', async({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});
+    await collection.addAdmin(alice, {Ethereum: caller});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
+
+    await expect(contract.methods.setCollectionProperties([{key: '', value: Buffer.from('val1')}]).send({from: caller})).to.be.rejected;
+    await expect(contract.methods.setCollectionProperties([{key: 'déjà vu', value: Buffer.from('hmm...')}]).send({from: caller})).to.be.rejected;
+    await expect(contract.methods.setCollectionProperties([{key: 'a'.repeat(257), value: Buffer.from('val3')}]).send({from: caller})).to.be.rejected;
+    // TODO add more expects
+    const raw = (await collection.getData())?.raw;
+    expect(raw.properties).to.deep.equal([]);
+  });
+
+
+  // Soft-deprecated: deleteCollectionProperty
+  [
+    {method: 'deleteCollectionProperties', mode: 'nft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
+    {method: 'deleteCollectionProperties', mode: 'rft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
+    {method: 'deleteCollectionProperties', mode: 'ft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
+    {method: 'deleteCollectionProperty', mode: 'nft' as const, methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`Collection properties can be deleted: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
+      const properties = [
+        {key: 'testKey1', value: 'testValue1'},
+        {key: 'testKey2', value: 'testValue2'},
+        {key: 'testKey3', value: 'testValue3'}];
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});
+
+      await collection.addAdmin(alice, {Ethereum: caller});
+
+      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty');
+
+      await contract.methods[testCase.method](...testCase.methodParams).send({from: caller});
+
+      const raw = (await collection.getData())?.raw;
+
+      expect(raw.properties.length).to.equal(testCase.expectedProps.length);
+      expect(raw.properties).to.deep.equal(testCase.expectedProps);
+    }));
+
+  [
+    {method: 'deleteCollectionProperties', methodParams: [['testKey2']]},
+    {method: 'deleteCollectionProperty', methodParams: ['testKey2']},
+  ].map(testCase =>
+    itEth(`cannot ${testCase.method}() of non-owned collections`, async ({helper}) => {
+      const properties = [
+        {key: 'testKey1', value: 'testValue1'},
+        {key: 'testKey2', value: 'testValue2'},
+      ];
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});
+
+      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty');
+
+      await expect(collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller})).to.be.rejected;
+      expect(await collection.getProperties()).to.deep.eq(properties);
+    }));
+
+  itEth('Can be read', async({helper}) => {
+    const caller = helper.eth.createAccount();
+    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: [{key: 'testKey', value: 'testValue'}]});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
+
+    const value = await contract.methods.collectionProperty('testKey').call();
+    expect(value).to.equal(helper.getWeb3().utils.toHex('testValue'));
+  });
+});
+
+describe('Supports ERC721Metadata', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  [
+    {case: 'nft' as const},
+    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`ERC721Metadata property can be set for ${testCase.case} collection`, testCase.requiredPallets || [], async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const bruh = await helper.eth.createAccountWithBalance(donor);
+
+      const BASE_URI = 'base/';
+      const SUFFIX = 'suffix1';
+      const URI = 'uri1';
+
+      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(caller);
+      const creatorMethod = testCase.case === 'rft' ? 'createRFTCollection' : 'createNFTCollection';
+
+      const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p');
+      const bruhCross = helper.ethCrossAccount.fromAddress(bruh);
+
+      const contract = await helper.ethNativeContract.collectionById(collectionId, testCase.case, caller);
+      await contract.methods.addCollectionAdminCross(bruhCross).send(); // to check that admin will work too
+
+      const collection1 = helper.nft.getCollectionObject(collectionId);
+      const data1 = await collection1.getData();
+      expect(data1?.raw.flags.erc721metadata).to.be.false;
+      expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false;
+
+      await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI)
+        .send({from: bruh});
+
+      expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true;
+
+      const collection2 = helper.nft.getCollectionObject(collectionId);
+      const data2 = await collection2.getData();
+      expect(data2?.raw.flags.erc721metadata).to.be.true;
+
+      const propertyPermissions = data2?.raw.tokenPropertyPermissions;
+      expect(propertyPermissions?.length).to.equal(2);
+
+      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null;
+
+      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null;
+
+      expect(data2?.raw.properties?.find((property: IProperty) => property.key === 'baseURI' && property.value === BASE_URI)).to.be.not.null;
+
+      const token1Result = await contract.methods.mint(bruh).send();
+      const tokenId1 = token1Result.events.Transfer.returnValues.tokenId;
+
+      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI);
+
+      await contract.methods.setProperties(tokenId1, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send();
+      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);
+
+      await contract.methods.setProperties(tokenId1, [{key: 'URI', value: Buffer.from(URI)}]).send();
+      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI);
+
+      await contract.methods.deleteProperties(tokenId1, ['URI']).send();
+      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);
+
+      const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send();
+      const tokenId2 = token2Result.events.Transfer.returnValues.tokenId;
+
+      expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI);
+
+      await contract.methods.deleteProperties(tokenId2, ['URI']).send();
+      expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI);
+
+      await contract.methods.setProperties(tokenId2, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send();
+      expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX);
+    }));
+});
addedjs-packages/tests/eth/collectionSponsoring.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/collectionSponsoring.test.ts
@@ -0,0 +1,955 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index.js';
+import {itEth, expect} from './util/index.js';
+import {CollectionLimitField, TokenPermissionField} from './util/playgrounds/types.js';
+
+describe('evm nft collection sponsoring', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let nominal: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  // TODO: move to substrate tests
+  itEth('sponsors mint transactions', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}});
+    await collection.setSponsor(alice, alice.address);
+    await collection.confirmSponsorship(alice);
+
+    const minter = helper.eth.createAccount();
+    expect(await helper.balance.getEthereum(minter)).to.equal(0n);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', minter);
+
+    await collection.addToAllowList(alice, {Ethereum: minter});
+
+    const result = await contract.methods.mint(minter).send();
+
+    const events = helper.eth.normalizeEvents(result.events);
+    expect(events).to.be.deep.equal([
+      {
+        address: collectionAddress,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: minter,
+          tokenId: '1',
+        },
+      },
+    ]);
+  });
+
+  // TODO: Temprorary off. Need refactor
+  // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {
+  //   const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  //   const collectionHelpers = evmCollectionHelpers(web3, owner);
+  //   let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send();
+  //   const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+  //   const sponsor = privateKeyWrapper('//Alice');
+  //   const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+
+  //   expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+  //   result = await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
+  //   expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
+
+  //   const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
+  //   await submitTransactionAsync(sponsor, confirmTx);
+  //   expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+
+  //   const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
+  //   expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address);
+  // });
+
+  [
+    'setCollectionSponsorCross',
+    'setCollectionSponsor', // Soft-deprecated
+  ].map(testCase =>
+    itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+
+      let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
+      const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, testCase === 'setCollectionSponsor');
+
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+      result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
+
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
+      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+
+      await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
+
+      sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
+      expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');
+    }));
+
+  [
+    'setCollectionSponsorCross',
+    'setCollectionSponsor', // Soft-deprecated
+  ].map(testCase =>
+    itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
+
+      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');
+
+      const collectionSub = helper.nft.getCollectionObject(collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');
+
+      // Set collection sponsor:
+      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});
+      let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
+      expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
+      // Account cannot confirm sponsorship if it is not set as a sponsor
+      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+      // Sponsor can confirm sponsorship:
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
+      sponsorship = (await collectionSub.getData())!.raw.sponsorship;
+      expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
+
+      // Create user with no balance:
+      const user = helper.ethCrossAccount.createAccount();
+      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+
+      // Set collection permissions:
+      const oldPermissions = (await collectionSub.getData())!.raw.permissions;
+      expect(oldPermissions.mintMode).to.be.false;
+      expect(oldPermissions.access).to.be.equal('Normal');
+
+      await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+      await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});
+      await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send();
+
+      const newPermissions = (await collectionSub.getData())!.raw.permissions;
+      expect(newPermissions.mintMode).to.be.true;
+      expect(newPermissions.access).to.be.equal('AllowList');
+
+      // Set token permissions
+      await collectionEvm.methods.setTokenPropertyPermissions([
+        ['key', [
+          [TokenPermissionField.TokenOwner, true],
+        ],
+        ],
+      ]).send({from: owner});
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
+
+      // User can mint token without balance:
+      {
+        const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});
+        const event = helper.eth.normalizeEvents(result.events)
+          .find(event => event.event === 'Transfer');
+
+        expect(event).to.be.deep.equal({
+          address: collectionAddress,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: user.eth,
+            tokenId: '1',
+          },
+        });
+
+        // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth});
+
+        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+        const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
+
+        expect(await collectionEvm.methods.properties(nextTokenId, []).call())
+          .to.be.like([
+            [
+              'key',
+              '0x' + Buffer.from('Value').toString('hex'),
+            ],
+          ]);
+        expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+        expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+      }
+    }));
+
+  itEth('Can sponsor [set token properties] via access list', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsorEth = await helper.eth.createAccountWithBalance(donor);
+    const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
+
+    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, false);
+
+    // Set collection sponsor:
+    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});
+
+    // Sponsor can confirm sponsorship:
+    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
+
+    // Create user with no balance:
+    const user = helper.ethCrossAccount.createAccount();
+    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+
+    // Set collection permissions:
+    await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+    await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});
+    await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+    await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send();
+
+    // Set token permissions
+    await collectionEvm.methods.setTokenPropertyPermissions([
+      ['key', [
+        [TokenPermissionField.TokenOwner, true],
+      ],
+      ],
+    ]).send({from: owner});
+
+    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+    const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+    const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
+
+    // User can mint token without balance:
+    {
+      const result = await collectionEvm.methods.mintCross(user, []).send({from: user.eth});
+      const event = helper.eth.normalizeEvents(result.events)
+        .find(event => event.event === 'Transfer');
+
+      expect(event).to.be.deep.equal({
+        address: collectionAddress,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: user.eth,
+          tokenId: '1',
+        },
+      });
+
+      await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+      const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
+
+      expect(await collectionEvm.methods.properties(nextTokenId, []).call())
+        .to.be.like([
+          [
+            'key',
+            '0x' + Buffer.from('Value').toString('hex'),
+          ],
+        ]);
+      expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+      expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+    }
+  });
+
+  // TODO: Temprorary off. Need refactor
+  // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {
+  //   const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  //   const collectionHelpers = evmCollectionHelpers(web3, owner);
+  //   const result = await collectionHelpers.methods.createERC721MetadataCompatibleNFTCollection('Sponsor collection', '1', '1', '').send();
+  //   const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+  //   const sponsor = privateKeyWrapper('//Alice');
+  //   const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+
+  //   await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
+
+  //   const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
+  //   await submitTransactionAsync(sponsor, confirmTx);
+
+  //   const user = createEthAccount(web3);
+  //   const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+  //   expect(nextTokenId).to.be.equal('1');
+
+  //   await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+  //   await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
+  //   await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+
+  //   const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
+  //   const sponsorBalanceBefore = (await getBalance(api, [sponsor.address]))[0];
+
+  //   {
+  //     const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+  //     expect(nextTokenId).to.be.equal('1');
+  //     const result = await collectionEvm.methods.mintWithTokenURI(
+  //       user,
+  //       nextTokenId,
+  //       'Test URI',
+  //     ).send({from: user});
+  //     const events = normalizeEvents(result.events);
+
+  //     expect(events).to.be.deep.equal([
+  //       {
+  //         address: collectionIdAddress,
+  //         event: 'Transfer',
+  //         args: {
+  //           from: '0x0000000000000000000000000000000000000000',
+  //           to: user,
+  //           tokenId: nextTokenId,
+  //         },
+  //       },
+  //     ]);
+
+  //     const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
+  //     const sponsorBalanceAfter = (await getBalance(api, [sponsor.address]))[0];
+
+  //     expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+  //     expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+  //     expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+  //   }
+  // });
+
+  [
+    'setCollectionSponsorCross',
+    'setCollectionSponsor', // Soft-deprecated
+  ].map(testCase =>
+    itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');
+
+      const collectionSub = helper.nft.getCollectionObject(collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');
+      // Set collection sponsor:
+      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();
+      let collectionData = (await collectionSub.getData())!;
+      expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      collectionData = (await collectionSub.getData())!;
+      expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+
+      const user = helper.eth.createAccount();
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+
+      const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+
+      const event = helper.eth.normalizeEvents(mintingResult.events)
+        .find(event => event.event === 'Transfer');
+      const address = helper.ethAddress.fromCollectionId(collectionId);
+
+      expect(event).to.be.deep.equal({
+        address,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: user,
+          tokenId: '1',
+        },
+      });
+      expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+    }));
+
+  itEth('Can reassign collection sponsor', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsorEth = await helper.eth.createAccountWithBalance(donor);
+    const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
+    const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);
+    const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);
+
+    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');
+    const collectionSub = helper.nft.getCollectionObject(collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    // Set and confirm sponsor:
+    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});
+    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
+
+    // Can reassign sponsor:
+    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});
+    const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
+    expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
+  });
+});
+
+describe('evm RFT collection sponsoring', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let nominal: bigint;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  [
+    'mintCross',
+    'mintWithTokenURI',
+  ].map(testCase =>
+    itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => {
+      const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [
+        {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+      ]});
+
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      await collection.setSponsor(alice, alice.address);
+      await collection.confirmSponsorship(alice);
+
+      const minter = helper.eth.createAccount();
+      const minterCross = helper.ethCrossAccount.fromAddress(minter);
+      expect(await helper.balance.getEthereum(minter)).to.equal(0n);
+
+      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true);
+
+      await collection.addToAllowList(alice, {Ethereum: minter});
+      await collection.addAdmin(alice, {Ethereum: owner});
+      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+      await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/')
+        .send();
+
+      let mintingResult;
+      let tokenId;
+      switch (testCase) {
+        case 'mintCross':
+          mintingResult = await contract.methods.mintCross(minterCross, []).send();
+          break;
+        case 'mintWithTokenURI':
+          mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send();
+          tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+          expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+          break;
+      }
+
+      const events = helper.eth.normalizeEvents(mintingResult.events);
+      expect(events).to.deep.include({
+        address: collectionAddress,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: minter,
+          tokenId: '1',
+        },
+      });
+    }));
+
+  [
+    'setCollectionSponsorCross',
+    'setCollectionSponsor', // Soft-deprecated
+  ].map(testCase =>
+    itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+
+      let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
+      const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, testCase === 'setCollectionSponsor');
+
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+      result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
+
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+
+      await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
+
+      const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
+      expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');
+    }));
+
+  [
+    'setCollectionSponsorCross',
+    'setCollectionSponsor', // Soft-deprecated
+  ].map(testCase =>
+    itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
+
+      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', '');
+
+      const collectionSub = helper.rft.getCollectionObject(collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');
+
+      // Set collection sponsor:
+      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});
+      let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
+      expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
+      // Account cannot confirm sponsorship if it is not set as a sponsor
+      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+      // Sponsor can confirm sponsorship:
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
+      sponsorship = (await collectionSub.getData())!.raw.sponsorship;
+      expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
+
+      // Create user with no balance:
+      const user = helper.eth.createAccount();
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+
+      // Set collection permissions:
+      const oldPermissions = (await collectionSub.getData())!.raw.permissions;
+      expect(oldPermissions.mintMode).to.be.false;
+      expect(oldPermissions.access).to.be.equal('Normal');
+
+      await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+      await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
+      await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+
+      const newPermissions = (await collectionSub.getData())!.raw.permissions;
+      expect(newPermissions.mintMode).to.be.true;
+      expect(newPermissions.access).to.be.equal('AllowList');
+
+      // Set token permissions
+      await collectionEvm.methods.setTokenPropertyPermissions([
+        ['URI', [
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, true],
+        ],
+        ],
+      ]).send({from: owner});
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+      // User can mint token without balance:
+      {
+        const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+        const events = helper.eth.normalizeEvents(result.events);
+
+        expect(events).to.deep.include({
+          address: collectionAddress,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: user,
+            tokenId: '1',
+          },
+        });
+
+        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+        const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+        expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+        expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+        expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+      }
+    }));
+
+  [
+    'setCollectionSponsorCross',
+    'setCollectionSponsor', // Soft-deprecated
+  ].map(testCase =>
+    itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+
+      const collectionSub = helper.rft.getCollectionObject(collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');
+      // Set collection sponsor:
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
+      let collectionData = (await collectionSub.getData())!;
+      expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
+
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      collectionData = (await collectionSub.getData())!;
+      expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+      const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
+      const sponsorSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.ethToSubstrate(sponsor));
+      const actualSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub)));
+      expect(actualSubAddress).to.be.equal(sponsorSubAddress);
+
+      const user = helper.eth.createAccount();
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+
+      const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+
+      const events = helper.eth.normalizeEvents(mintingResult.events);
+      const address = helper.ethAddress.fromCollectionId(collectionId);
+
+      expect(events).to.deep.include({
+        address,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: user,
+          tokenId: '1',
+        },
+      });
+      expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+    }));
+
+  itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = alice;
+    const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);
+
+    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+
+    const collectionSub = helper.rft.getCollectionObject(collectionId);
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);
+    // Set collection sponsor:
+    expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+    await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+    expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
+    let collectionData = (await collectionSub.getData())!;
+    expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);
+    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+    await collectionSub.confirmSponsorship(sponsor);
+    collectionData = (await collectionSub.getData())!;
+    expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);
+    expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+    const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
+    expect(BigInt(sponsorStruct.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));
+
+    const user = helper.eth.createAccount();
+    const userCross = helper.ethCrossAccount.fromAddress(user);
+    await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+    const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);
+
+    const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+    const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+
+    const events = helper.eth.normalizeEvents(mintingResult.events);
+
+    expect(events).to.deep.include({
+      address: collectionAddress,
+      event: 'Transfer',
+      args: {
+        from: '0x0000000000000000000000000000000000000000',
+        to: user,
+        tokenId: '1',
+      },
+    });
+    expect(await collectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+
+    const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+    const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);
+    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+  });
+
+  itEth('Sponsoring collection from substrate address via access list', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const user = helper.eth.createAccount();
+    const userCross =  helper.ethCrossAccount.fromAddress(user);
+    const sponsor = alice;
+    const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);
+
+    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);
+
+    await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});
+
+    const collectionSub = helper.rft.getCollectionObject(collectionId);
+    await collectionSub.confirmSponsorship(sponsor);
+
+    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+
+    await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+
+    await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
+    await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+
+    // Set token permissions
+    await collectionEvm.methods.setTokenPropertyPermissions([
+      ['URI', [
+        [TokenPermissionField.TokenOwner, true],
+        [TokenPermissionField.CollectionAdmin, true],
+      ],
+      ],
+    ]).send({from: owner});
+
+    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+    const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);
+    const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+    {
+      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const mintingResult = await collectionEvm.methods.mintWithTokenURI(
+        user,
+        'Test URI',
+      ).send({from: user});
+
+      const events = helper.eth.normalizeEvents(mintingResult.events);
+
+
+      expect(events).to.deep.include({
+        address: collectionAddress,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: user,
+          tokenId: nextTokenId,
+        },
+      });
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);
+      const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+      expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+      expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+      expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+    }
+  });
+
+  itEth('Can reassign collection sponsor', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsorEth = await helper.eth.createAccountWithBalance(donor);
+    const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
+    const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);
+    const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);
+
+    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+    const collectionSub = helper.rft.getCollectionObject(collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+    // Set and confirm sponsor:
+    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});
+    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
+
+    // Can reassign sponsor:
+    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});
+    const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
+    expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
+  });
+
+  [
+    'transfer',
+    'transferCross',
+    'transferFrom',
+    'transferFromCross',
+  ].map(testCase =>
+    itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const receiver = await helper.eth.createAccountWithBalance(donor);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+
+      const user = await helper.eth.createAccountWithBalance(donor);
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+      switch (testCase) {
+        case 'transfer':
+          await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});
+          break;
+        case 'transferCross':
+          await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+          break;
+        case 'transferFrom':
+          await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});
+          break;
+        case 'transferFromCross':
+          await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+          break;
+      }
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+    }));
+});
+
+describe('evm RFT token sponsoring', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  [
+    'transfer',
+    'transferCross',
+    'transferFrom',
+    'transferFromCross',
+  ].map(testCase =>
+    itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const receiver = await helper.eth.createAccountWithBalance(donor);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+
+      const user = await helper.eth.createAccountWithBalance(donor);
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+
+      const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
+      await tokenContract.methods.repartition(2).send();
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+      switch (testCase) {
+        case 'transfer':
+          await tokenContract.methods.transfer(receiver, 1).send();
+          break;
+        case 'transferCross':
+          await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
+          break;
+        case 'transferFrom':
+          await tokenContract.methods.transferFrom(user, receiver, 1).send();
+          break;
+        case 'transferFromCross':
+          await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();
+          break;
+      }
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+    }));
+
+  [
+    'approve',
+    'approveCross',
+  ].map(testCase =>
+    itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const receiver = await helper.eth.createAccountWithBalance(donor);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+
+      const user = await helper.eth.createAccountWithBalance(donor);
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+
+      const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
+      await tokenContract.methods.repartition(2).send();
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+      switch (testCase) {
+        case 'approve':
+          await tokenContract.methods.approve(receiver, 1).send();
+          break;
+        case 'approveCross':
+          await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
+          break;
+      }
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+    }));
+});
+
addedjs-packages/tests/eth/contractSponsoring.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/contractSponsoring.test.ts
@@ -0,0 +1,579 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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 type {CompiledContract} from './util/playgrounds/types.js';
+
+describe('Sponsoring EVM contracts', () => {
+  let donor: IKeyringPair;
+  let nominal: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const flipper = await helper.eth.deployFlipper(owner);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+
+    // 1. owner can set selfSponsoring:
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner});
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+
+    // 1.1 Can get sponsor using methods.sponsor:
+    const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();
+    expect(actualSponsorOpt.status).to.be.true;
+    const actualSponsor = actualSponsorOpt.value;
+    expect(actualSponsor.eth).to.eq(flipper.options.address);
+    expect(actualSponsor.sub).to.eq('0');
+
+    // 2. Events should be:
+    const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events);
+    expect(ethEvents).to.be.deep.equal([
+      {
+        address: flipper.options.address,
+        event: 'ContractSponsorSet',
+        args: {
+          contractAddress: flipper.options.address,
+          sponsor: flipper.options.address,
+        },
+      },
+      {
+        address: flipper.options.address,
+        event: 'ContractSponsorshipConfirmed',
+        args: {
+          contractAddress: flipper.options.address,
+          sponsor: flipper.options.address,
+        },
+      },
+    ]);
+  });
+
+  itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const notOwner = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itEth('Sponsoring can be set by the address that has deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
+    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
+  });
+
+  itEth('Sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const notOwner = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
+  });
+
+  itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    // 1. owner can set a sponsor:
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
+    const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;
+
+    // 2. Events should be:
+    const events = helper.eth.normalizeEvents(result.events);
+    expect(events).to.be.deep.equal([
+      {
+        address: flipper.options.address,
+        event: 'ContractSponsorSet',
+        args: {
+          contractAddress: flipper.options.address,
+          sponsor: sponsor,
+        },
+      },
+    ]);
+  });
+
+  itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const notOwner = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+
+    // 1. sponsor can confirm sponsorship:
+    const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+
+    // 1.1 Can get sponsor using methods.sponsor:
+    const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();
+    expect(actualSponsorOpt.status).to.be.true;
+    const actualSponsor = actualSponsorOpt.value;
+    expect(actualSponsor.eth).to.eq(sponsor);
+    expect(actualSponsor.sub).to.eq('0');
+
+    // 2. Events should be:
+    const events = helper.eth.normalizeEvents(result.events);
+    expect(events).to.be.deep.equal([
+      {
+        address: flipper.options.address,
+        event: 'ContractSponsorshipConfirmed',
+        args: {
+          contractAddress: flipper.options.address,
+          sponsor: sponsor,
+        },
+      },
+    ]);
+  });
+
+  itEth('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const notSponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
+    await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission');
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itEth('Sponsorship can not be confirmed by the address that not set as sponsor', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const notSponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor');
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+  });
+
+  itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+    // 1. Can remove sponsor:
+    const result = await helpers.methods.removeSponsor(flipper.options.address).send();
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+
+    // 2. Events should be:
+    const events = helper.eth.normalizeEvents(result.events);
+    expect(events).to.be.deep.equal([
+      {
+        address: flipper.options.address,
+        event: 'ContractSponsorRemoved',
+        args: {
+          contractAddress: flipper.options.address,
+        },
+      },
+    ]);
+
+    // TODO: why call method reverts?
+    // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
+    // expect(actualSponsor.eth).to.eq(sponsor);
+    // expect(actualSponsor.sub).to.eq('0');
+  });
+
+  itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const notOwner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+
+    await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected;
+    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+  });
+
+  itEth('In generous mode, non-allowlisted user transaction will be sponsored', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+
+    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+
+    const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+    const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
+
+    await flipper.methods.flip().send({from: caller});
+    expect(await flipper.methods.getValue().call()).to.be.true;
+
+    // Balance should be taken from sponsor instead of caller
+    const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+    const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
+    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+    expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
+  });
+
+  itEth('In generous mode, non-allowlisted user transaction will be self sponsored', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    await helpers.methods.selfSponsoredEnable(flipper.options.address).send();
+
+    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+
+    await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
+
+    const contractBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));
+    const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
+
+    await flipper.methods.flip().send({from: caller});
+    expect(await flipper.methods.getValue().call()).to.be.true;
+
+    // Balance should be taken from sponsor instead of caller
+    const contractBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));
+    const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
+    expect(contractBalanceAfter < contractBalanceBefore).to.be.true;
+    expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
+  });
+
+  [
+    {balance: 0n, label: '0'},
+    {balance: 10n, label: '10'},
+  ].map(testCase => {
+    itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const caller = helper.eth.createAccount();
+      await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance);
+      const helpers = await helper.ethNativeContract.contractHelpers(owner);
+      const flipper = await helper.eth.deployFlipper(owner);
+
+      await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+      await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
+
+      await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+      await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+
+      await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+      await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceBefore > 0n).to.be.true;
+
+      await flipper.methods.flip().send({from: caller});
+      expect(await flipper.methods.getValue().call()).to.be.true;
+
+      // Balance should be taken from flipper instead of caller
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      // Caller's balance does not change:
+      const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
+      expect(callerBalanceAfter).to.eq(testCase.balance * nominal);
+    });
+  });
+
+  itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const caller = helper.eth.createAccount();
+    const contractHelpers = await helper.ethNativeContract.contractHelpers(owner);
+
+    // Deploy flipper and send some tokens:
+    const flipper = await helper.eth.deployFlipper(owner);
+    await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
+    expect(await flipper.methods.getValue().call()).to.be.false;
+    // flipper address has some tokens:
+    const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);
+    expect(originalFlipperBalance > 0n).to.be.true;
+
+    // Set Allowlisted sponsoring mode. caller is not in allow list:
+    await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+    await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+    await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+
+    // 1. Caller has no UNQ and is not in allow list. So he cannot flip:
+    await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/);
+    expect(await flipper.methods.getValue().call()).to.be.false;
+
+    // Flipper's balance does not change:
+    const balanceAfter = await helper.balance.getEthereum(flipper.options.address);
+    expect(balanceAfter).to.be.equal(originalFlipperBalance);
+  });
+
+  itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    const originalCallerBalance = await helper.balance.getEthereum(caller);
+    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+    await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
+
+    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner});
+
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+
+    const originalFlipperBalance = await helper.balance.getEthereum(sponsor);
+    expect(originalFlipperBalance > 0n).to.be.true;
+
+    await flipper.methods.flip().send({from: caller});
+    expect(await flipper.methods.getValue().call()).to.be.true;
+    expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance);
+
+    const newFlipperBalance = await helper.balance.getEthereum(sponsor);
+    expect(newFlipperBalance).to.be.not.equal(originalFlipperBalance);
+
+    await flipper.methods.flip().send({from: caller});
+    // todo:playgrounds fails rarely (expected 99893341659775672580n to equal 99912598679356033129n) (again, 99893341659775672580n)
+    expect(await helper.balance.getEthereum(sponsor)).to.be.equal(newFlipperBalance);
+    expect(await helper.balance.getEthereum(caller)).to.be.not.equal(originalCallerBalance);
+  });
+
+  // TODO: Find a way to calculate default rate limit
+  itEth('Default rate limit equal 7200', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.sponsoringRateLimit(flipper.options.address).call()).to.be.equal('7200');
+  });
+
+  itEth('Gas price boundaries', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+
+    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+
+    let sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+    let callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
+
+    let expectValue = await flipper.methods.getValue().call();
+
+    const flip = async (gasPrice: bigint, shouldPass = true) => {
+      await flipper.methods.flip().send({from: caller, gasPrice: gasPrice});
+      expectValue = !expectValue;
+      expect(await flipper.methods.getValue().call()).to.be.eq(expectValue);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.eq(shouldPass);
+      expect(callerBalanceAfter === callerBalanceBefore).to.be.eq(shouldPass);
+      sponsorBalanceBefore = sponsorBalanceAfter;
+      callerBalanceBefore = callerBalanceAfter;
+    };
+
+    const gasPrice = BigInt((await helper.eth.getGasPrice())!);
+    await flip(gasPrice);
+    await flip(gasPrice * 2n);
+    await flip(gasPrice * 21n / 10n);
+    await flip(gasPrice * 22n / 10n, false);
+  });
+});
+
+describe('Sponsoring Fee Limit', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let testContract: CompiledContract;
+
+  async function compileTestContract(helper: EthUniqueHelper) {
+    if(!testContract) {
+      testContract = await helper.ethContract.compile(
+        'TestContract',
+        `
+        // SPDX-License-Identifier: MIT
+        pragma solidity ^0.8.0;
+
+        contract TestContract {
+          event Result(bool);
+
+          function test(uint32 cycles) public {
+            uint256 counter = 0;
+            while(true) {
+              counter ++;
+              if (counter > cycles){
+                break;
+              }
+            }
+            emit Result(true);
+          }
+        }
+      `,
+      );
+    }
+    return testContract;
+  }
+
+  async function deployTestContract(helper: EthUniqueHelper, owner: string) {
+    const compiled = await compileTestContract(helper);
+    return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object);
+  }
+
+  before(async () => {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  itEth('Default fee limit', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('115792089237316195423570985008687907853269984665640564039457584007913129639935');
+  });
+
+  itEth('Set fee limit', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send();
+    expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('100');
+  });
+
+  itEth('Negative test - set fee limit by non-owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const stranger = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected;
+  });
+
+  itEth('Negative test - check that eth transactions exceeding fee limit are not executed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const user = await helper.eth.createAccountWithBalance(donor);
+    const helpers = helper.ethNativeContract.contractHelpers(owner);
+
+    const testContract = await deployTestContract(helper, owner);
+
+    await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});
+
+    await helpers.methods.setSponsor(testContract.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});
+
+    const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());
+
+    await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();
+
+    const originalUserBalance = await helper.balance.getEthereum(user);
+    await testContract.methods.test(100).send({from: user, gas: 2_000_000, maxFeePerGas: gasPrice.toString()});
+    expect(await helper.balance.getEthereum(user)).to.be.equal(originalUserBalance);
+
+    await testContract.methods.test(100).send({from: user, gas: 2_100_000, maxFeePerGas: gasPrice.toString()});
+    expect(await helper.balance.getEthereum(user)).to.not.be.equal(originalUserBalance);
+  });
+
+  itEth('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+
+    const testContract = await deployTestContract(helper, owner);
+
+    await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});
+
+    await helpers.methods.setSponsor(testContract.options.address, sponsor).send();
+    await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});
+
+    const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());
+
+    await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();
+
+    const originalAliceBalance = await helper.balance.getSubstrate(alice.address);
+
+    await helper.eth.sendEVM(
+      alice,
+      testContract.options.address,
+      testContract.methods.test(100).encodeABI(),
+      '0',
+      2_000_000,
+    );
+    // expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance);
+    expect(await helper.balance.getSubstrate(alice.address)).to.be.equal(originalAliceBalance);
+
+    await helper.eth.sendEVM(
+      alice,
+      testContract.options.address,
+      testContract.methods.test(100).encodeABI(),
+      '0',
+      2_100_000,
+    );
+    expect(await helper.balance.getSubstrate(alice.address)).to.not.be.equal(originalAliceBalance);
+  });
+});
addedjs-packages/tests/eth/createCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/createCollection.test.ts
@@ -0,0 +1,1449 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {evmToAddress} from '@polkadot/util-crypto';
+import {Pallets, requirePalletsOrSkip} from '../util/index.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';
+
+const DECIMALS = 18;
+const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [
+  [],  // properties
+  [],  // tokenPropertyPermissions
+  [],  // adminList
+  [false, false, []],  // nestingSettings
+  [],  // limits
+  emptyAddress,  // pendingSponsor
+  [0],  // flags
+];
+
+type ElementOf<A> = A extends readonly (infer T)[] ? T : never;
+function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {
+  if(args.length === 0) {
+    yield internalRest as any;
+    return;
+  }
+  for(const value of args[0]) {
+    yield* cartesian([...internalRest, value], ...args.slice(1)) as any;
+  }
+}
+
+describe('Create collection from EVM', () => {
+  let donor: IKeyringPair;
+  let nominal: bigint;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  describe('Fungible collection', () => {
+    before(async function() {
+      await usingEthPlaygrounds((helper) => {
+        requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
+        return Promise.resolve();
+      });
+    });
+
+    itEth('Collection address exist', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
+      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+      expect(await collectionHelpers
+        .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
+        .to.be.false;
+
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();
+      expect(await collectionHelpers
+        .methods.isCollectionExist(collectionAddress).call())
+        .to.be.true;
+
+      // check collectionOwner:
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+      const collectionOwner = await collectionEvm.methods.collectionOwner().call();
+      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
+    });
+
+    itEth('destroyCollection', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+
+      const result = await collectionHelper.methods
+        .destroyCollection(collectionAddress)
+        .send({from: owner});
+
+      const events = helper.eth.normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address: collectionHelper.options.address,
+          event: 'CollectionDestroyed',
+          args: {
+            collectionId: collectionAddress,
+          },
+        },
+      ]);
+
+      expect(await collectionHelper.methods
+        .isCollectionExist(collectionAddress)
+        .call()).to.be.false;
+      expect(await helper.collection.getData(collectionId)).to.be.null;
+    });
+
+    itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+      {
+        const MAX_NAME_LENGTH = 64;
+        const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
+        const description = 'A';
+        const tokenPrefix = 'A';
+
+        await expect(collectionHelper.methods
+          .createCollection([
+            collectionName,
+            description,
+            tokenPrefix,
+            CollectionMode.Fungible,
+            DECIMALS,
+            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+          ])
+          .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
+      }
+      {
+        const MAX_DESCRIPTION_LENGTH = 256;
+        const collectionName = 'A';
+        const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
+        const tokenPrefix = 'A';
+        await expect(collectionHelper.methods
+          .createCollection([
+            collectionName,
+            description,
+            tokenPrefix,
+            CollectionMode.Fungible,
+            DECIMALS,
+            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+          ])
+          .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
+      }
+      {
+        const MAX_TOKEN_PREFIX_LENGTH = 16;
+        const collectionName = 'A';
+        const description = 'A';
+        const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
+        await expect(collectionHelper.methods
+          .createCollection([
+            collectionName,
+            description,
+            tokenPrefix,
+            CollectionMode.Fungible,
+            DECIMALS,
+            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+          ])
+          .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
+      }
+    });
+
+    itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+      const expects = [0n, 1n, 30n].map(async value => {
+        await expect(collectionHelper.methods
+          .createCollection([
+            'Peasantry',
+            'absolutely anything',
+            'TWIW',
+            CollectionMode.Fungible,
+            DECIMALS,
+            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+          ])
+          .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+      });
+      await Promise.all(expects);
+    });
+  });
+
+  describe('Nonfungible collection', () => {
+    before(async function() {
+      await usingEthPlaygrounds((helper) => {
+        requirePalletsOrSkip(this, helper, [Pallets.NFT]);
+        return Promise.resolve();
+      });
+    });
+
+    itEth('Create collection', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const name = 'CollectionEVM';
+      const description = 'Some description';
+      const prefix = 'token prefix';
+
+      // todo:playgrounds this might fail when in async environment.
+      const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+      const {collectionId, collectionAddress, events} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'nft')).send();
+
+      expect(events).to.be.deep.equal([
+        {
+          address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
+          event: 'CollectionCreated',
+          args: {
+            owner: owner,
+            collectionId: collectionAddress,
+          },
+        },
+      ]);
+
+      const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+
+      const collection = helper.nft.getCollectionObject(collectionId);
+      const data = (await collection.getData())!;
+
+      // Parallel test safety
+      expect(collectionCountAfter - collectionCountBefore).to.be.gte(1);
+      expect(collectionId).to.be.eq(collectionCountAfter);
+      expect(data.name).to.be.eq(name);
+      expect(data.description).to.be.eq(description);
+      expect(data.raw.tokenPrefix).to.be.eq(prefix);
+      expect(data.raw.mode).to.be.eq('NFT');
+
+      const options = await collection.getOptions();
+
+      expect(options.tokenPropertyPermissions).to.be.empty;
+    });
+
+    // this test will occasionally fail when in async environment.
+    itEth('Check collection address exist', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
+      const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
+      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+      expect(await collectionHelpers.methods
+        .isCollectionExist(expectedCollectionAddress)
+        .call()).to.be.false;
+
+      await collectionHelpers.methods
+        .createCollection([
+          'A',
+          'A',
+          'A',
+          CollectionMode.Nonfungible,
+          0,
+          ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+        ])
+        .send({value: Number(2n * helper.balance.getOneTokenNominal())});
+
+      expect(await collectionHelpers.methods
+        .isCollectionExist(expectedCollectionAddress)
+        .call()).to.be.true;
+    });
+  });
+
+  describe('Create RFT collection from EVM', () => {
+    before(async function() {
+      await usingEthPlaygrounds((helper) => {
+        requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+        return Promise.resolve();
+      });
+    });
+
+    itEth('Create collection', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const name = 'CollectionEVM';
+      const description = 'Some description';
+      const prefix = 'token prefix';
+
+      const {collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'rft')).send();
+      const data = (await helper.rft.getData(collectionId))!;
+      const collection = helper.rft.getCollectionObject(collectionId);
+
+      expect(data.name).to.be.eq(name);
+      expect(data.description).to.be.eq(description);
+      expect(data.raw.tokenPrefix).to.be.eq(prefix);
+      expect(data.raw.mode).to.be.eq('ReFungible');
+
+      const options = await collection.getOptions();
+
+      expect(options.tokenPropertyPermissions).to.be.empty;
+    });
+
+    itEth('Create collection with properties & get description', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const name = 'CollectionEVM';
+      const description = 'Some description';
+      const prefix = 'token prefix';
+      const baseUri = 'BaseURI';
+
+      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+      const collection = helper.rft.getCollectionObject(collectionId);
+      const data = (await collection.getData())!;
+
+      expect(data.name).to.be.eq(name);
+      expect(data.description).to.be.eq(description);
+      expect(data.raw.tokenPrefix).to.be.eq(prefix);
+      expect(data.raw.mode).to.be.eq('ReFungible');
+
+      expect(await contract.methods.description().call()).to.deep.equal(description);
+
+      const options = await collection.getOptions();
+      expect(options.tokenPropertyPermissions).to.be.deep.equal([
+        {
+          key: 'URI',
+          permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
+        },
+        {
+          key: 'URISuffix',
+          permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
+        },
+      ]);
+    });
+
+    itEth('Set sponsorship', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const ss58Format = helper.chain.getChainProperties().ss58Format;
+      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send();
+
+      const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      await collection.methods.setCollectionSponsorCross(sponsorCross).send();
+
+      let data = (await helper.rft.getData(collectionId))!;
+      expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+      await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+      await sponsorCollection.methods.confirmCollectionSponsorship().send();
+
+      data = (await helper.rft.getData(collectionId))!;
+      expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+    });
+
+    itEth('Collection address exist', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
+      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+      expect(await collectionHelpers
+        .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
+        .to.be.false;
+
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'rft')).send();
+      expect(await collectionHelpers
+        .methods.isCollectionExist(collectionAddress).call())
+        .to.be.true;
+
+      // check collectionOwner:
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+      const collectionOwner = await collectionEvm.methods.collectionOwner().call();
+      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
+    });
+
+    itEth('destroyCollection', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send();
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+
+      await expect(collectionHelper.methods
+        .destroyCollection(collectionAddress)
+        .send({from: owner})).to.be.fulfilled;
+
+      expect(await collectionHelper.methods
+        .isCollectionExist(collectionAddress)
+        .call()).to.be.false;
+      expect(await helper.collection.getData(collectionId)).to.be.null;
+    });
+
+    itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+      {
+        const MAX_NAME_LENGTH = 64;
+        const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
+        const description = 'A';
+        const tokenPrefix = 'A';
+
+        await expect(collectionHelper.methods
+          .createCollection([
+            collectionName,
+            description,
+            tokenPrefix,
+            CollectionMode.Refungible,
+            DECIMALS,
+            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+          ])
+          .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
+      }
+      {
+        const MAX_DESCRIPTION_LENGTH = 256;
+        const collectionName = 'A';
+        const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
+        const tokenPrefix = 'A';
+        await expect(collectionHelper.methods
+          .createCollection([
+            collectionName,
+            description,
+            tokenPrefix,
+            CollectionMode.Refungible,
+            DECIMALS,
+            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+          ])
+          .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
+      }
+      {
+        const MAX_TOKEN_PREFIX_LENGTH = 16;
+        const collectionName = 'A';
+        const description = 'A';
+        const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
+        await expect(collectionHelper.methods
+          .createCollection([
+            collectionName,
+            description,
+            tokenPrefix,
+            CollectionMode.Refungible,
+            DECIMALS,
+            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+          ])
+          .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
+      }
+    });
+
+    itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+      await expect(collectionHelper.methods
+        .createCollection([
+          'Peasantry',
+          'absolutely anything',
+          'TWIW',
+          CollectionMode.Refungible,
+          0,
+          ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
+        ])
+        .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+    });
+  });
+
+  describe('Sponsoring', () => {
+    itEth('Сan remove collection sponsor', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);
+
+      const {collectionAddress} = await helper.eth.createCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'Sponsor collection',
+          description: '1',
+          tokenPrefix: '1',
+          collectionMode: 'nft',
+          pendingSponsor: sponsorCross,
+        },
+      ).send();
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
+
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
+      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
+
+      await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
+
+      sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
+      expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');
+    });
+
+    itEth('Can sponsor from evm address via access list', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsorEth);
+
+      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'Sponsor collection',
+          description: '1',
+          tokenPrefix: '1',
+          collectionMode: 'nft',
+          pendingSponsor: sponsorCross,
+          limits: [{field: CollectionLimitField.SponsoredDataRateLimit, value: 30n}],
+          tokenPropertyPermissions: [{key: 'key', permissions: [{code: TokenPermissionField.TokenOwner, value: true}]}],
+        },
+        '',
+      );
+
+      const collectionSub = helper.nft.getCollectionObject(collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+      let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
+      expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
+      // Account cannot confirm sponsorship if it is not set as a sponsor
+      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+      // Sponsor can confirm sponsorship:
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
+      sponsorship = (await collectionSub.getData())!.raw.sponsorship;
+      expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
+
+      // Create user with no balance:
+      const user = helper.ethCrossAccount.createAccount();
+      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+
+      // Set collection permissions:
+      const oldPermissions = (await collectionSub.getData())!.raw.permissions;
+      expect(oldPermissions.mintMode).to.be.false;
+      expect(oldPermissions.access).to.be.equal('Normal');
+
+      await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
+      await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});
+      await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+
+      const newPermissions = (await collectionSub.getData())!.raw.permissions;
+      expect(newPermissions.mintMode).to.be.true;
+      expect(newPermissions.access).to.be.equal('AllowList');
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
+
+      // User can mint token without balance:
+      {
+        const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});
+        const event = helper.eth.normalizeEvents(result.events)
+          .find(event => event.event === 'Transfer');
+
+        expect(event).to.be.deep.equal({
+          address: collectionAddress,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: user.eth,
+            tokenId: '1',
+          },
+        });
+
+        // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth});
+
+        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+        const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
+
+        expect(await collectionEvm.methods.properties(nextTokenId, []).call())
+          .to.be.like([
+            [
+              'key',
+              '0x' + Buffer.from('Value').toString('hex'),
+            ],
+          ]);
+        expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+        expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
+      }
+    });
+
+    itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);
+      const user = helper.eth.createAccount();
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'Sponsor collection',
+          description: '1',
+          tokenPrefix: '1',
+          collectionMode: 'nft',
+          pendingSponsor: sponsorCross,
+          adminList: [userCross],
+        },
+        '',
+      );
+
+      const collectionSub = helper.nft.getCollectionObject(collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      // Set collection sponsor:
+      let collectionData = (await collectionSub.getData())!;
+      expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      collectionData = (await collectionSub.getData())!;
+      expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
+
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+
+      const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+
+      const event = helper.eth.normalizeEvents(mintingResult.events)
+        .find(event => event.event === 'Transfer');
+      const address = helper.ethAddress.fromCollectionId(collectionId);
+
+      expect(event).to.be.deep.equal({
+        address,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: user,
+          tokenId: '1',
+        },
+      });
+      expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+    });
+
+    itEth('Can reassign collection sponsor', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCrossEth = helper.ethCrossAccount.fromAddr(sponsorEth);
+      const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);
+      const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);
+
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'Sponsor collection',
+          description: '1',
+          tokenPrefix: '1',
+          collectionMode: 'nft',
+          pendingSponsor: sponsorCrossEth,
+        },
+        '',
+      );
+      const collectionSub = helper.nft.getCollectionObject(collectionId);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+      // Set and confirm sponsor:
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
+
+      // Can reassign sponsor:
+      await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});
+      const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
+      expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
+    });
+
+    [
+      'transfer',
+      'transferCross',
+      'transferFrom',
+      'transferFromCross',
+    ].map(testCase =>
+      itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const sponsor = await helper.eth.createAccountWithBalance(donor);
+        const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);
+        const user = await helper.eth.createAccountWithBalance(donor);
+        const userCross = helper.ethCrossAccount.fromAddress(user);
+
+        const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(
+          owner,
+          {
+            ...CREATE_COLLECTION_DATA_DEFAULTS,
+            name: 'Sponsor collection',
+            description: '1',
+            tokenPrefix: '1',
+            collectionMode: 'rft',
+            pendingSponsor: sponsorCross,
+            adminList: [userCross],
+          },
+          '',
+        );
+        const receiver = await helper.eth.createAccountWithBalance(donor);
+        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+        await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+
+        const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+        const tokenId = result.events.Transfer.returnValues.tokenId;
+
+        const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+        const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+        const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+        switch (testCase) {
+          case 'transfer':
+            await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});
+            break;
+          case 'transferCross':
+            await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+            break;
+          case 'transferFrom':
+            await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});
+            break;
+          case 'transferFromCross':
+            await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+            break;
+        }
+
+        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+        expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+        expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+        const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+      }));
+  });
+
+  describe('Collection admins', () => {
+    let donor: IKeyringPair;
+
+    before(async function() {
+      await usingEthPlaygrounds(async (_helper, privateKey) => {
+        donor = await privateKey({url: import.meta.url});
+      });
+    });
+
+    [
+      {mode: 'nft' as const, requiredPallets: []},
+      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+      {mode: 'ft' as const, requiredPallets: []},
+    ].map(testCase => {
+      itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {
+        // arrange
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const adminSub = await privateKey('//admin2');
+        const adminEth = helper.eth.createAccount().toLowerCase();
+
+        const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
+        const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
+
+        const {collectionAddress, collectionId} = await helper.eth.createCollection(
+          owner,
+          {
+            ...CREATE_COLLECTION_DATA_DEFAULTS,
+            name: 'Mint collection',
+            description: 'a',
+            tokenPrefix: 'b',
+            collectionMode: testCase.mode,
+            adminList: [adminCrossSub, adminCrossEth],
+          },
+        ).send();
+        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
+
+        // 1. Expect api.rpc.unique.adminlist returns admins:
+        const adminListRpc = await helper.collection.getAdmins(collectionId);
+        expect(adminListRpc).to.has.length(2);
+        expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}]);
+
+        // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist
+        let adminListEth = await collectionEvm.methods.collectionAdmins().call();
+        adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));
+        expect(adminListRpc).to.be.like(adminListEth);
+
+        // 3. check isOwnerOrAdminCross returns true:
+        expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;
+        expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;
+      });
+    });
+
+    itEth('cross account admin can mint', async ({helper}) => {
+      // arrange: create collection and accounts
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
+      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
+      const [adminSub] = await helper.arrange.createAccounts([100n], donor);
+      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'Mint collection',
+          description: 'a',
+          tokenPrefix: 'b',
+          collectionMode: 'nft',
+          adminList: [adminCrossSub, adminCrossEth],
+        },
+        'uri',
+      );
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+
+      // admin (sub and eth) can mint token:
+      await collectionEvm.methods.mint(owner).send({from: adminEth});
+      await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});
+
+      expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);
+    });
+
+    itEth('cannot add invalid cross account admin', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);
+
+      const adminCross = {
+        eth: helper.address.substrateToEth(admin.address),
+        sub: admin.addressRaw,
+      };
+
+      await expect(helper.eth.createCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'A',
+          description: 'B',
+          tokenPrefix: 'C',
+          collectionMode: 'nft',
+          adminList: [adminCross],
+        },
+      ).call()).to.be.rejected;
+    });
+
+    itEth('Remove [cross] admin by owner', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const [adminSub] = await helper.arrange.createAccounts([10n], donor);
+      const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
+      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
+      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
+
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'A',
+          description: 'B',
+          tokenPrefix: 'C',
+          collectionMode: 'nft',
+          adminList: [adminCrossSub, adminCrossEth],
+        },
+      ).send();
+
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+      {
+        const adminList = await helper.collection.getAdmins(collectionId);
+        expect(adminList).to.deep.include({Substrate: adminSub.address});
+        expect(adminList).to.deep.include({Ethereum: adminEth});
+      }
+
+      await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();
+      await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();
+      const adminList = await helper.collection.getAdmins(collectionId);
+      expect(adminList.length).to.be.eq(0);
+
+      // Non admin cannot mint:
+      await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);
+      await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;
+    });
+  });
+
+  describe('Collection limits', () => {
+    describe('Can set collection limits', () => {
+      [
+        {case: 'nft' as const},
+        {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+        {case: 'ft' as const},
+      ].map(testCase =>
+        itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
+          const owner = await helper.eth.createAccountWithBalance(donor);
+
+          const limits = {
+            accountTokenOwnershipLimit: 1000n,
+            sponsoredDataSize: 1024n,
+            sponsoredDataRateLimit: 30n,
+            tokenLimit: 1000000n,
+            sponsorTransferTimeout: 6n,
+            sponsorApproveTimeout: 6n,
+            ownerCanTransfer: 1n,
+            ownerCanDestroy: 0n,
+            transfersEnabled: 0n,
+          };
+
+          const {collectionId, collectionAddress} = await helper.eth.createCollection(
+            owner,
+            {
+              ...CREATE_COLLECTION_DATA_DEFAULTS,
+              name: 'Limits',
+              description: 'absolutely anything',
+              tokenPrefix: 'FLO',
+              collectionMode: testCase.case,
+              limits: [
+                {field: CollectionLimitField.AccountTokenOwnership, value: limits.accountTokenOwnershipLimit},
+                {field: CollectionLimitField.SponsoredDataSize, value: limits.sponsoredDataSize},
+                {field: CollectionLimitField.SponsoredDataRateLimit, value: limits.sponsoredDataRateLimit},
+                {field: CollectionLimitField.TokenLimit, value: limits.tokenLimit},
+                {field: CollectionLimitField.SponsorTransferTimeout, value: limits.sponsorTransferTimeout},
+                {field: CollectionLimitField.SponsorApproveTimeout, value: limits.sponsorApproveTimeout},
+                {field: CollectionLimitField.OwnerCanTransfer, value: limits.ownerCanTransfer},
+                {field: CollectionLimitField.OwnerCanDestroy, value: limits.ownerCanDestroy},
+                {field: CollectionLimitField.TransferEnabled, value: limits.transfersEnabled},
+              ],
+            },
+          ).send();
+
+          const expectedLimits = {
+            accountTokenOwnershipLimit: 1000,
+            sponsoredDataSize: 1024,
+            sponsoredDataRateLimit: {blocks: 30},
+            tokenLimit: 1000000,
+            sponsorTransferTimeout: 6,
+            sponsorApproveTimeout: 6,
+            ownerCanTransfer: true,
+            ownerCanDestroy: false,
+            transfersEnabled: false,
+          };
+
+          const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
+
+          // Check limits from sub:
+          const data = (await helper.rft.getData(collectionId))!;
+          expect(data.raw.limits).to.deep.eq(expectedLimits);
+          expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits);
+          // Check limits from eth:
+          const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner});
+          expect(limitsEvm).to.have.length(9);
+          expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]);
+          expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]);
+          expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]);
+          expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]);
+          expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]);
+          expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]);
+          expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]);
+          expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]);
+          expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]);
+        }));
+    });
+
+    describe('(!negative test!) Cannot set invalid collection limits', () => {
+      [
+        {case: 'nft' as const},
+        {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+        {case: 'ft' as const},
+      ].map(testCase =>
+        itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
+          const invalidLimits = {
+            accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER),
+            transfersEnabled: 3,
+          };
+
+          const createCollectionData = {
+            ...CREATE_COLLECTION_DATA_DEFAULTS,
+            name: 'Limits',
+            description: 'absolutely anything',
+            tokenPrefix: 'ISNI',
+            collectionMode: testCase.case,
+          };
+
+          const owner = await helper.eth.createAccountWithBalance(donor);
+          await expect(helper.eth.createCollection(
+            owner,
+            {
+              ...createCollectionData,
+              limits: [{field: 9 as CollectionLimitField, value: 1n}],
+            },
+          ).call()).to.be.rejectedWith('value not convertible into enum "CollectionLimitField"');
+
+          await expect(helper.eth.createCollection(
+            owner,
+            {
+              ...createCollectionData,
+              limits: [{field: CollectionLimitField.AccountTokenOwnership, value: invalidLimits.accountTokenOwnershipLimit}],
+            },
+          ).call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`);
+
+          await expect(helper.eth.createCollection(
+            owner,
+            {
+              ...createCollectionData,
+              limits: [{field: CollectionLimitField.TransferEnabled, value: 3n}],
+            },
+          ).call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`);
+
+          await expect(helper.eth.createCollection(
+            owner,
+            {
+              ...createCollectionData,
+              limits: [{field: CollectionLimitField.SponsoredDataSize, value: -1n}],
+            },
+          ).call()).to.be.rejectedWith('value out-of-bounds');
+        }));
+    });
+  });
+
+  describe('Collection properties', () => {
+
+    [
+      {mode: 'nft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
+      {mode: 'rft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
+      {mode: 'ft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
+    ].map(testCase =>
+      itEth.ifWithPallets(`Collection properties can be set for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
+        const caller = await helper.eth.createAccountWithBalance(donor);
+        const callerCross = helper.ethCrossAccount.fromAddress(caller);
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const {collectionId, collectionAddress} = await helper.eth.createCollection(
+          owner,
+          {
+            ...CREATE_COLLECTION_DATA_DEFAULTS,
+            name: 'name',
+            description: 'test',
+            tokenPrefix: 'test',
+            collectionMode: testCase.mode,
+            adminList: [callerCross],
+            properties: testCase.methodParams,
+          },
+        ).send();
+
+        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
+
+        const raw = (await helper[testCase.mode].getData(collectionId))?.raw;
+        expect(raw.properties).to.deep.equal(testCase.expectedProps);
+
+        // collectionProperties returns properties:
+        expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));
+      }));
+
+    [
+      {mode: 'nft' as const},
+      {mode: 'rft' as const},
+      {mode: 'ft' as const},
+    ].map(testCase =>
+      itEth.ifWithPallets(`Collection properties can be deleted for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
+        const caller = await helper.eth.createAccountWithBalance(donor);
+        const callerCross = helper.ethCrossAccount.fromAddress(caller);
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const {collectionId, collectionAddress} = await helper.eth.createCollection(
+          owner,
+          {
+            ...CREATE_COLLECTION_DATA_DEFAULTS,
+            name: 'name',
+            description: 'test',
+            tokenPrefix: 'test',
+            collectionMode: testCase.mode,
+            adminList: [callerCross],
+            properties:[
+              {key: 'testKey1', value: Buffer.from('testValue1')},
+              {key: 'testKey2', value: Buffer.from('testValue2')},
+              {key: 'testKey3', value: Buffer.from('testValue3')}],
+          },
+        ).send();
+
+        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
+
+        await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller});
+
+        const raw = (await helper[testCase.mode].getData(collectionId))?.raw;
+
+        expect(raw.properties.length).to.equal(1);
+        expect(raw.properties).to.deep.equal([{key: 'testKey3', value: 'testValue3'}]);
+      }));
+
+    itEth('(!negative test!) Cannot set invalid properties', async({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const callerCross = helper.ethCrossAccount.fromAddress(caller);
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const createCollectionData = {
+        ...CREATE_COLLECTION_DATA_DEFAULTS,
+        name: 'name',
+        description: 'test',
+        tokenPrefix: 'test',
+        collectionMode: 'nft' as TCollectionMode,
+        adminList: [callerCross],
+      };
+      await expect(helper.eth.createCollection(
+        owner,
+        {
+          ...createCollectionData,
+          properties: [{key: '', value: Buffer.from('val1')}],
+        },
+      ).call()).to.be.rejected;
+
+      await expect(helper.eth.createCollection(
+        owner,
+        {
+          ...createCollectionData,
+          properties: [{key: 'déjà vu', value: Buffer.from('hmm...')}],
+        },
+      ).call()).to.be.rejected;
+
+      await expect(helper.eth.createCollection(
+        owner,
+        {
+          ...createCollectionData,
+          properties: [{key: 'a'.repeat(257), value: Buffer.from('val3')}],
+        },
+      ).call()).to.be.rejected;
+    });
+
+    itEth('(!negative test!) cannot delete properties of non-owned collections', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress} = await helper.eth.createCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'name',
+          description: 'test',
+          tokenPrefix: 'test',
+          collectionMode: 'nft',
+          properties:[
+            {key: 'testKey1', value: Buffer.from('testValue1')},
+            {key: 'testKey2', value: Buffer.from('testValue2')}],
+        },
+      ).send();
+
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
+
+      await expect(collectionEvm.methods.deleteCollectionProperties(['testKey2']).send({from: caller})).to.be.rejected;
+    });
+  });
+
+  describe('Token property permissions', () => {
+    [
+      {mode: 'nft' as const, requiredPallets: []},
+      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    ].map(testCase =>
+      itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
+        for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
+          const {collectionId, collectionAddress} = await helper.eth.createCollection(
+            owner,
+            {
+              ...CREATE_COLLECTION_DATA_DEFAULTS,
+              name: 'A',
+              description: 'B',
+              tokenPrefix: 'C',
+              collectionMode: testCase.mode,
+              adminList: [caller],
+              tokenPropertyPermissions: [
+                {
+                  key: 'testKey',
+                  permissions: [
+                    {code: TokenPermissionField.Mutable, value: mutable},
+                    {code: TokenPermissionField.TokenOwner, value: tokenOwner},
+                    {code: TokenPermissionField.CollectionAdmin, value: collectionAdmin},
+                  ],
+                },
+              ],
+            },
+          ).send();
+          const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+          expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
+            key: 'testKey',
+            permission: {mutable, collectionAdmin, tokenOwner},
+          }]);
+
+          expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
+            ['testKey', [
+              [TokenPermissionField.Mutable.toString(), mutable],
+              [TokenPermissionField.TokenOwner.toString(), tokenOwner],
+              [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]],
+            ],
+          ]);
+        }
+      }));
+
+    [
+      {mode: 'nft' as const, requiredPallets: []},
+      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    ].map(testCase =>
+      itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions`, testCase.requiredPallets, async({helper}) => {
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const {collectionId, collectionAddress} = await helper.eth.createCollection(
+          owner,
+          {
+            ...CREATE_COLLECTION_DATA_DEFAULTS,
+            name: 'A',
+            description: 'B',
+            tokenPrefix: 'C',
+            collectionMode: testCase.mode,
+            tokenPropertyPermissions: [
+              {
+                key: 'testKey_0',
+                permissions: [
+                  {code: TokenPermissionField.Mutable, value: true},
+                  {code: TokenPermissionField.TokenOwner, value: true},
+                  {code: TokenPermissionField.CollectionAdmin, value: true}],
+              },
+              {
+                key: 'testKey_1',
+                permissions: [
+                  {code: TokenPermissionField.Mutable, value: true},
+                  {code: TokenPermissionField.TokenOwner, value: false},
+                  {code: TokenPermissionField.CollectionAdmin, value: true}],
+              },
+              {
+                key: 'testKey_2',
+                permissions: [
+                  {code: TokenPermissionField.Mutable, value: false},
+                  {code: TokenPermissionField.TokenOwner, value: true},
+                  {code: TokenPermissionField.CollectionAdmin, value: false}],
+              },
+            ],
+          },
+        ).send();
+        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+        expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
+          {
+            key: 'testKey_0',
+            permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
+          },
+          {
+            key: 'testKey_1',
+            permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
+          },
+          {
+            key: 'testKey_2',
+            permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
+          },
+        ]);
+
+        expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([
+          ['testKey_0', [
+            [TokenPermissionField.Mutable.toString(), true],
+            [TokenPermissionField.TokenOwner.toString(), true],
+            [TokenPermissionField.CollectionAdmin.toString(), true]],
+          ],
+          ['testKey_1', [
+            [TokenPermissionField.Mutable.toString(), true],
+            [TokenPermissionField.TokenOwner.toString(), false],
+            [TokenPermissionField.CollectionAdmin.toString(), true]],
+          ],
+          ['testKey_2', [
+            [TokenPermissionField.Mutable.toString(), false],
+            [TokenPermissionField.TokenOwner.toString(), true],
+            [TokenPermissionField.CollectionAdmin.toString(), false]],
+          ],
+        ]);
+      }));
+
+    [
+      {mode: 'nft' as const, requiredPallets: []},
+      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    ].map(testCase =>
+      itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
+        const caller = await helper.eth.createAccountWithBalance(donor);
+        const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
+        const {collectionAddress} = await helper.eth.createCollection(
+          caller,
+          {
+            ...CREATE_COLLECTION_DATA_DEFAULTS,
+            name: 'A',
+            description: 'B',
+            tokenPrefix: 'C',
+            collectionMode: testCase.mode,
+            adminList: [receiver],
+            tokenPropertyPermissions: [
+              {
+                key: 'testKey',
+                permissions: [
+                  {code: TokenPermissionField.Mutable, value: true},
+                  {code: TokenPermissionField.CollectionAdmin, value: true}],
+              },
+              {
+                key: 'testKey_1',
+                permissions: [
+                  {code: TokenPermissionField.Mutable, value: true},
+                  {code: TokenPermissionField.CollectionAdmin, value: true}],
+              },
+            ],
+          },
+        ).send();
+
+        const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
+        const tokenId = (await collection.methods.mintCross(receiver, [{key: 'testKey', value: Buffer.from('testValue')}, {key: 'testKey_1', value: Buffer.from('testValue_1')}]).send()).events.Transfer.returnValues.tokenId;
+        expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2);
+
+        await collection.methods.deleteProperties(tokenId, ['testKey', 'testKey_1']).send({from: caller});
+        expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(0);
+      }));
+  });
+
+  describe('Nesting', () => {
+    itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'A',
+          description: 'B',
+          tokenPrefix: 'C',
+          collectionMode: 'nft',
+          nestingSettings: {token_owner: true, collection_admin: false, restricted: []},
+        },
+      ).send();
+
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+      // Create a token to be nested to
+      const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;
+      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);
+
+      // Create a nested token
+      const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});
+      const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;
+      expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);
+
+      // Create a token to be nested and nest
+      const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;
+
+      await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});
+      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);
+
+      // Unnest token back
+      await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});
+      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);
+    });
+
+    itEth('NFT: collectionNesting()', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createCollection(
+        owner,
+        new CreateCollectionData('A', 'B', 'C', 'nft'),
+      ).send();
+
+      const unnestedContract = helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);
+      expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
+
+      const {collectionAddress} = await helper.eth.createCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'A',
+          description: 'B',
+          tokenPrefix: 'C',
+          collectionMode: 'nft',
+          nestingSettings: {token_owner: true, collection_admin: false, restricted: [unnestedCollectionAddress.toString()]},
+        },
+      ).send();
+
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]);
+      await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});
+      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
+    });
+
+    itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionId, collectionAddress} = await helper.eth.createCollection(
+        owner,
+        {
+          ...CREATE_COLLECTION_DATA_DEFAULTS,
+          name: 'A',
+          description: 'B',
+          tokenPrefix: 'C',
+          collectionMode: 'nft',
+          nestingSettings: {token_owner: false, collection_admin: false, restricted: []},
+        },
+      ).send();
+
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+      // Create a token to nest into
+      const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
+      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);
+
+      // Create a token to nest
+      const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;
+
+      // Try to nest
+      await expect(contract.methods
+        .transfer(targetNftTokenAddress, nftTokenId)
+        .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
+    });
+  });
+
+  describe('Flags', () => {
+    const createCollectionData = {
+      ...CREATE_COLLECTION_DATA_DEFAULTS,
+      name: 'A',
+      description: 'B',
+      tokenPrefix: 'C',
+      collectionMode: 'nft' as TCollectionMode,
+    };
+
+    itEth('NFT: use numbers for flags', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      {
+        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 0}).send();
+        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});
+      }
+
+      {
+        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 64}).send();
+        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});
+      }
+    });
+
+    itEth('NFT: can\'t set foreign flag number', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      {
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
+      }
+
+      {
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
+      }
+    });
+
+    itEth('NFT: use enum for flags', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      {
+        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata]}).send();
+        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});
+      }
+    });
+
+    itEth('NFT: foreign flag enum is ignored', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      {
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
+      }
+
+      {
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
+      }
+    });
+  });
+});
addedjs-packages/tests/eth/createFTCollection.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/createFTCollection.seqtest.ts
@@ -0,0 +1,76 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+
+const DECIMALS = 18;
+
+describe('Create FT collection from EVM', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Create collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const name = 'CollectionEVM';
+    const description = 'Some description';
+    const prefix = 'token prefix';
+
+    // todo:playgrounds this might fail when in async environment.
+    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+
+    const {collectionId} = await helper.eth.createFungibleCollection(owner, name, DECIMALS, description, prefix);
+
+    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+    const data = (await helper.ft.getData(collectionId))!;
+
+    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+    expect(collectionId).to.be.eq(collectionCountAfter);
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.deep.eq({Fungible: DECIMALS.toString()});
+  });
+
+  // todo:playgrounds this test will fail when in async environment.
+  itEth('Check collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
+    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.false;
+
+
+    await helper.eth.createFungibleCollection(owner, 'A', DECIMALS, 'A', 'A');
+
+
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.true;
+  });
+});
addedjs-packages/tests/eth/createFTCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/createFTCollection.test.ts
@@ -0,0 +1,237 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {evmToAddress} from '@polkadot/util-crypto';
+import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import {CollectionLimitField} from './util/playgrounds/types.js';
+
+const DECIMALS = 18;
+
+describe('Create FT collection from EVM', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  // TODO move sponsorship tests to another file:
+  // Soft-deprecated
+  itEth('[eth] Set sponsorship', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const description = 'absolutely anything';
+
+    const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY');
+
+    const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    await collection.methods.setCollectionSponsor(sponsor).send();
+
+    let data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+
+    data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+  });
+
+  itEth('[cross] Set sponsorship', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const description = 'absolutely anything';
+    const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY');
+
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+    await collection.methods.setCollectionSponsorCross(sponsorCross).send();
+
+    let data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+
+    data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+    expect(await collection.methods.description().call()).to.deep.equal(description);
+  });
+
+  itEth('Collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await collectionHelpers
+      .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
+      .to.be.false;
+
+    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT');
+    expect(await collectionHelpers
+      .methods.isCollectionExist(collectionAddress).call())
+      .to.be.true;
+
+    // check collectionOwner:
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    const collectionOwner = await collectionEvm.methods.collectionOwner().call();
+    expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
+  });
+
+  itEth('destroyCollection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT');
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+
+    const result = await collectionHelper.methods
+      .destroyCollection(collectionAddress)
+      .send({from: owner});
+
+    const events = helper.eth.normalizeEvents(result.events);
+
+    expect(events).to.be.deep.equal([
+      {
+        address: collectionHelper.options.address,
+        event: 'CollectionDestroyed',
+        args: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+
+    expect(await collectionHelper.methods
+      .isCollectionExist(collectionAddress)
+      .call()).to.be.false;
+    expect(await helper.collection.getData(collectionId)).to.be.null;
+  });
+});
+
+describe('(!negative tests!) Create FT collection from EVM', () => {
+  let donor: IKeyringPair;
+  let nominal: bigint;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
+      donor = await privateKey({url: import.meta.url});
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    {
+      const MAX_NAME_LENGTH = 64;
+      const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
+      const description = 'A';
+      const tokenPrefix = 'A';
+
+      await expect(collectionHelper.methods
+        .createFTCollection(collectionName, DECIMALS, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
+    }
+    {
+      const MAX_DESCRIPTION_LENGTH = 256;
+      const collectionName = 'A';
+      const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
+      const tokenPrefix = 'A';
+      await expect(collectionHelper.methods
+        .createFTCollection(collectionName, DECIMALS, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
+    }
+    {
+      const MAX_TOKEN_PREFIX_LENGTH = 16;
+      const collectionName = 'A';
+      const description = 'A';
+      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
+      await expect(collectionHelper.methods
+        .createFTCollection(collectionName, DECIMALS, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
+    }
+  });
+
+  itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    const expects = [0n, 1n, 30n].map(async value => {
+      await expect(collectionHelper.methods
+        .createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW')
+        .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+    });
+    await Promise.all(expects);
+  });
+
+  // Soft-deprecated
+  itEth('(!negative test!) [eth] Check owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const peasant = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true);
+    const EXPECTED_ERROR = 'NoPermission';
+    {
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      await expect(peasantCollection.methods
+        .setCollectionSponsor(sponsor)
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true);
+      await expect(sponsorCollection.methods
+        .confirmCollectionSponsorship()
+        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+    }
+    {
+      await expect(peasantCollection.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+    }
+  });
+
+  itEth('(!negative test!) [cross] Check owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const peasant = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant);
+    const EXPECTED_ERROR = 'NoPermission';
+    {
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      await expect(peasantCollection.methods
+        .setCollectionSponsorCross(sponsorCross)
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor);
+      await expect(sponsorCollection.methods
+        .confirmCollectionSponsorship()
+        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+    }
+    {
+      await expect(peasantCollection.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+    }
+  });
+});
addedjs-packages/tests/eth/createNFTCollection.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/createNFTCollection.seqtest.ts
@@ -0,0 +1,89 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+
+
+describe('Create NFT collection from EVM', () => {
+  let donor: IKeyringPair;
+
+  before(async function () {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Create collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const name = 'CollectionEVM';
+    const description = 'Some description';
+    const prefix = 'token prefix';
+
+    // todo:playgrounds this might fail when in async environment.
+    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+    const {collectionId, collectionAddress, events} = await helper.eth.createNFTCollection(owner, name, description, prefix);
+
+    expect(events).to.be.deep.equal([
+      {
+        address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
+        event: 'CollectionCreated',
+        args: {
+          owner: owner,
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+
+    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+
+    const collection = helper.nft.getCollectionObject(collectionId);
+    const data = (await collection.getData())!;
+
+    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+    expect(collectionId).to.be.eq(collectionCountAfter);
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.eq('NFT');
+
+    const options = await collection.getOptions();
+
+    expect(options.tokenPropertyPermissions).to.be.empty;
+  });
+
+  // this test will occasionally fail when in async environment.
+  itEth('Check collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
+    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.false;
+
+    await collectionHelpers.methods
+      .createNFTCollection('A', 'A', 'A')
+      .send({value: Number(2n * helper.balance.getOneTokenNominal())});
+
+    expect(await collectionHelpers.methods
+      .isCollectionExist(expectedCollectionAddress)
+      .call()).to.be.true;
+  });
+});
addedjs-packages/tests/eth/createNFTCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/createNFTCollection.test.ts
@@ -0,0 +1,275 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {evmToAddress} from '@polkadot/util-crypto';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import {CollectionLimitField} from './util/playgrounds/types.js';
+
+
+describe('Create NFT collection from EVM', () => {
+  let donor: IKeyringPair;
+
+  before(async function () {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Create collection with properties & get desctription', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const name = 'CollectionEVM';
+    const description = 'Some description';
+    const prefix = 'token prefix';
+    const baseUri = 'BaseURI';
+
+    const {collectionId, collectionAddress, events} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    expect(events).to.be.deep.equal([
+      {
+        address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
+        event: 'CollectionCreated',
+        args: {
+          owner: owner,
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+
+    const collection = helper.nft.getCollectionObject(collectionId);
+    const data = (await collection.getData())!;
+
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.eq('NFT');
+
+    expect(await contract.methods.description().call()).to.deep.equal(description);
+
+    const options = await collection.getOptions();
+    expect(options.tokenPropertyPermissions).to.be.deep.equal([
+      {
+        key: 'URI',
+        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
+      },
+      {
+        key: 'URISuffix',
+        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
+      },
+    ]);
+  });
+
+  // Soft-deprecated
+  itEth('[eth] Set sponsorship', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+    await collection.methods.setCollectionSponsor(sponsor).send();
+
+    let data = (await helper.nft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
+    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+
+    data = (await helper.nft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+  });
+
+  itEth('[cross] Set sponsorship & get description', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const description = 'absolutely anything';
+    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', description, 'ROC');
+
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+    await collection.methods.setCollectionSponsorCross(sponsorCross).send();
+
+    let data = (await helper.nft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+    const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
+    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+
+    data = (await helper.nft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    expect(await sponsorCollection.methods.description().call()).to.deep.equal(description);
+  });
+
+  itEth('Collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await collectionHelpers
+      .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
+      .to.be.false;
+
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Exister', 'absolutely anything', 'EVC');
+    expect(await collectionHelpers
+      .methods.isCollectionExist(collectionAddress).call())
+      .to.be.true;
+
+    // check collectionOwner:
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    const collectionOwner = await collectionEvm.methods.collectionOwner().call();
+    expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
+  });
+});
+
+describe('(!negative tests!) Create NFT collection from EVM', () => {
+  let donor: IKeyringPair;
+  let nominal: bigint;
+
+  before(async function () {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    {
+      const MAX_NAME_LENGTH = 64;
+      const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
+      const description = 'A';
+      const tokenPrefix = 'A';
+
+      await expect(collectionHelper.methods
+        .createNFTCollection(collectionName, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
+
+    }
+    {
+      const MAX_DESCRIPTION_LENGTH = 256;
+      const collectionName = 'A';
+      const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
+      const tokenPrefix = 'A';
+      await expect(collectionHelper.methods
+        .createNFTCollection(collectionName, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
+    }
+    {
+      const MAX_TOKEN_PREFIX_LENGTH = 16;
+      const collectionName = 'A';
+      const description = 'A';
+      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
+      await expect(collectionHelper.methods
+        .createNFTCollection(collectionName, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
+    }
+  });
+
+  itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    await expect(collectionHelper.methods
+      .createNFTCollection('Peasantry', 'absolutely anything', 'CVE')
+      .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+  });
+
+  // Soft-deprecated
+  itEth('(!negative test!) [eth] Check owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const malfeasant = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
+    const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant, true);
+    const EXPECTED_ERROR = 'NoPermission';
+    {
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      await expect(malfeasantCollection.methods
+        .setCollectionSponsor(sponsor)
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
+      await expect(sponsorCollection.methods
+        .confirmCollectionSponsorship()
+        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+    }
+    {
+      await expect(malfeasantCollection.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+    }
+  });
+
+  itEth('(!negative test!) [cross] Check owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const malfeasant = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
+    const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
+    const EXPECTED_ERROR = 'NoPermission';
+    {
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      await expect(malfeasantCollection.methods
+        .setCollectionSponsorCross(sponsorCross)
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
+      await expect(sponsorCollection.methods
+        .confirmCollectionSponsorship()
+        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+    }
+    {
+      await expect(malfeasantCollection.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+    }
+  });
+
+  itEth('destroyCollection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+
+
+    const result = await collectionHelper.methods
+      .destroyCollection(collectionAddress)
+      .send({from: owner});
+
+    const events = helper.eth.normalizeEvents(result.events);
+
+    expect(events).to.be.deep.equal([
+      {
+        address: collectionHelper.options.address,
+        event: 'CollectionDestroyed',
+        args: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+
+    expect(await collectionHelper.methods
+      .isCollectionExist(collectionAddress)
+      .call()).to.be.false;
+    expect(await helper.collection.getData(collectionId)).to.be.null;
+  });
+});
addedjs-packages/tests/eth/createRFTCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/createRFTCollection.test.ts
@@ -0,0 +1,273 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {evmToAddress} from '@polkadot/util-crypto';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {Pallets, requirePalletsOrSkip} from '../util/index.js';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import {CollectionLimitField} from './util/playgrounds/types.js';
+
+
+describe('Create RFT collection from EVM', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Create collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const name = 'CollectionEVM';
+    const description = 'Some description';
+    const prefix = 'token prefix';
+
+    const {collectionId} = await helper.eth.createRFTCollection(owner, name, description, prefix);
+    const data = (await helper.rft.getData(collectionId))!;
+    const collection = helper.rft.getCollectionObject(collectionId);
+
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.eq('ReFungible');
+
+    const options = await collection.getOptions();
+
+    expect(options.tokenPropertyPermissions).to.be.empty;
+  });
+
+
+
+  itEth('Create collection with properties & get description', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const name = 'CollectionEVM';
+    const description = 'Some description';
+    const prefix = 'token prefix';
+    const baseUri = 'BaseURI';
+
+    const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    const collection = helper.rft.getCollectionObject(collectionId);
+    const data = (await collection.getData())!;
+
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.eq('ReFungible');
+
+    expect(await contract.methods.description().call()).to.deep.equal(description);
+
+    const options = await collection.getOptions();
+    expect(options.tokenPropertyPermissions).to.be.deep.equal([
+      {
+        key: 'URI',
+        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
+      },
+      {
+        key: 'URISuffix',
+        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
+      },
+    ]);
+  });
+
+  // Soft-deprecated
+  itEth('[eth] Set sponsorship', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
+
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
+    await collection.methods.setCollectionSponsor(sponsor).send();
+
+    let data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+
+    data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+  });
+
+  itEth('[cross] Set sponsorship', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
+
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+    await collection.methods.setCollectionSponsorCross(sponsorCross).send();
+
+    let data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+
+    data = (await helper.rft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+  });
+
+  itEth('Collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
+    expect(await collectionHelpers
+      .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
+      .to.be.false;
+
+    const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Exister', 'absolutely anything', 'WIWT');
+    expect(await collectionHelpers
+      .methods.isCollectionExist(collectionAddress).call())
+      .to.be.true;
+
+    // check collectionOwner:
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    const collectionOwner = await collectionEvm.methods.collectionOwner().call();
+    expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
+  });
+});
+
+describe('(!negative tests!) Create RFT collection from EVM', () => {
+  let donor: IKeyringPair;
+  let nominal: bigint;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      donor = await privateKey({url: import.meta.url});
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    {
+      const MAX_NAME_LENGTH = 64;
+      const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
+      const description = 'A';
+      const tokenPrefix = 'A';
+
+      await expect(collectionHelper.methods
+        .createRFTCollection(collectionName, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
+    }
+    {
+      const MAX_DESCRIPTION_LENGTH = 256;
+      const collectionName = 'A';
+      const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
+      const tokenPrefix = 'A';
+      await expect(collectionHelper.methods
+        .createRFTCollection(collectionName, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
+    }
+    {
+      const MAX_TOKEN_PREFIX_LENGTH = 16;
+      const collectionName = 'A';
+      const description = 'A';
+      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
+      await expect(collectionHelper.methods
+        .createRFTCollection(collectionName, description, tokenPrefix)
+        .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
+    }
+  });
+
+  itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    await expect(collectionHelper.methods
+      .createRFTCollection('Peasantry', 'absolutely anything', 'TWIW')
+      .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+  });
+
+  // Soft-deprecated
+  itEth('(!negative test!) [eth] Check owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const peasant = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true);
+    const EXPECTED_ERROR = 'NoPermission';
+    {
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      await expect(peasantCollection.methods
+        .setCollectionSponsor(sponsor)
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+      await expect(sponsorCollection.methods
+        .confirmCollectionSponsorship()
+        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+    }
+    {
+      await expect(peasantCollection.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+    }
+  });
+
+  itEth('(!negative test!) [cross] Check owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const peasant = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
+    const EXPECTED_ERROR = 'NoPermission';
+    {
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      await expect(peasantCollection.methods
+        .setCollectionSponsorCross(sponsorCross)
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+      await expect(sponsorCollection.methods
+        .confirmCollectionSponsorship()
+        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+    }
+    {
+      await expect(peasantCollection.methods
+        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
+        .call()).to.be.rejectedWith(EXPECTED_ERROR);
+    }
+  });
+
+  itEth('destroyCollection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress, collectionId} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+
+    await expect(collectionHelper.methods
+      .destroyCollection(collectionAddress)
+      .send({from: owner})).to.be.fulfilled;
+
+    expect(await collectionHelper.methods
+      .isCollectionExist(collectionAddress)
+      .call()).to.be.false;
+    expect(await helper.collection.getData(collectionId)).to.be.null;
+  });
+});
addedjs-packages/tests/eth/crossTransfer.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/crossTransfer.test.ts
@@ -0,0 +1,105 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {itEth, usingEthPlaygrounds} from './util/index.js';
+import {CrossAccountId} from '@unique/playgrounds/unique.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+
+describe('Token transfer between substrate address and EVM address. Fungible', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    });
+  });
+
+  itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => {
+    const bobCA = CrossAccountId.fromKeyring(bob).toICrossAccountId();
+    const charlieCA = CrossAccountId.fromKeyring(charlie).toICrossAccountId();
+    const charlieCAEth = CrossAccountId.fromKeyring(charlie).toEthereum().toICrossAccountId();
+
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+
+    await collection.mint(alice, 200n);
+    await collection.transfer(alice, charlieCAEth, 200n);
+    await collection.transferFrom(alice, charlieCAEth, charlieCA, 50n);
+    await collection.transfer(charlie, bobCA, 50n);
+  });
+
+  itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => {
+    const aliceProxy = await helper.eth.createAccountWithBalance(donor);
+    const bobProxy = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'ft', aliceProxy);
+
+    await collection.mint(alice, 200n, {Ethereum: aliceProxy});
+    await contract.methods.transfer(bobProxy, 50).send({from: aliceProxy});
+    await collection.transferFrom(alice, {Ethereum: bobProxy}, CrossAccountId.fromKeyring(bob).toICrossAccountId(), 50n);
+    await collection.transfer(bob, CrossAccountId.fromKeyring(alice).toICrossAccountId(), 50n);
+  });
+});
+
+describe('Token transfer between substrate address and EVM address. NFT', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    });
+  });
+
+  itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => {
+    const charlieEth = CrossAccountId.fromKeyring(charlie, 'Ethereum').toICrossAccountId();
+
+    const collection = await helper.nft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    const token = await collection.mintToken(alice);
+    await token.transfer(alice, charlieEth);
+    await token.transferFrom(alice, charlieEth, CrossAccountId.fromKeyring(charlie).toICrossAccountId());
+    await token.transfer(charlie, CrossAccountId.fromKeyring(bob).toICrossAccountId());
+  });
+
+  itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => {
+    const aliceProxy = await helper.eth.createAccountWithBalance(donor);
+    const bobProxy = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.nft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', aliceProxy);
+
+    const token = await collection.mintToken(alice);
+    await token.transfer(alice, {Ethereum: aliceProxy});
+    await contract.methods.transfer(bobProxy, 1).send({from: aliceProxy});
+    await token.transferFrom(alice, {Ethereum: bobProxy}, {Substrate: bob.address});
+    await token.transfer(bob, {Substrate: charlie.address});
+  });
+});
addedjs-packages/tests/eth/destroyCollection.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/destroyCollection.test.ts
@@ -0,0 +1,63 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../util/index.js';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import type {TCollectionMode} from '@unique/playgrounds/types.js';
+import {CreateCollectionData} from './util/playgrounds/types.js';
+
+describe('Destroy Collection from EVM', function() {
+  let donor: IKeyringPair;
+  const testCases = [
+    {case: 'rft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'rft'], requiredPallets: [Pallets.ReFungible]},
+    {case: 'nft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'nft'], requiredPallets: [Pallets.NFT]},
+    {case: 'ft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'ft', 18], requiredPallets: [Pallets.Fungible]},
+  ];
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  testCases.map((testCase) =>
+    itEth.ifWithPallets(`Cannot burn non-owned or non-existing collection ${testCase.case}`, testCase.requiredPallets, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const signer = await helper.eth.createAccountWithBalance(donor);
+
+      const unexistedCollection = helper.ethAddress.fromCollectionId(1000000);
+
+      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(signer);
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData(...testCase.params as [string, string, string, TCollectionMode, number?])).send();
+      // cannot burn collec
+      await expect(collectionHelpers.methods
+        .destroyCollection(collectionAddress)
+        .send({from: signer})).to.be.rejected;
+
+      await expect(collectionHelpers.methods
+        .destroyCollection(unexistedCollection)
+        .send({from: signer})).to.be.rejected;
+
+      expect(await collectionHelpers.methods
+        .isCollectionExist(unexistedCollection)
+        .call()).to.be.false;
+
+      expect(await collectionHelpers.methods
+        .isCollectionExist(collectionAddress)
+        .call()).to.be.true;
+    }));
+});
addedjs-packages/tests/eth/ethFeesAreCorrect.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/ethFeesAreCorrect.test.ts
@@ -0,0 +1,66 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://witww.gnu.org/licenses/>.
+
+import type {IKeyringPair} from '@polkadot/types/types';
+import {itEth, usingEthPlaygrounds, expect} from './util/index.js';
+
+describe('Eth fees are correct', () => {
+  let donor: IKeyringPair;
+  let minter: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [minter, alice] = await helper.arrange.createAccounts([100n, 200n], donor);
+    });
+  });
+
+
+  itEth('web3 fees are the same as evm.call fees', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const aliceEth = helper.address.substrateToEth(alice.address);
+
+    const {tokenId: tokenA} = await collection.mintToken(minter, {Ethereum: owner});
+    const {tokenId: tokenB} = await collection.mintToken(minter, {Ethereum: aliceEth});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    const balanceBeforeWeb3Transfer = await helper.balance.getEthereum(owner);
+    await contract.methods.transfer(receiver, tokenA).send({from: owner, maxFeePerGas: ((await helper.eth.getGasPrice())!).toString()});
+    const balanceAfterWeb3Transfer = await helper.balance.getEthereum(owner);
+    const web3Diff = balanceBeforeWeb3Transfer - balanceAfterWeb3Transfer;
+
+    const encodedCall = contract.methods.transfer(receiver, tokenB)
+      .encodeABI();
+
+    const balanceBeforeEvmCall = await helper.balance.getSubstrate(alice.address);
+    await helper.eth.sendEVM(
+      alice,
+      collectionAddress,
+      encodedCall,
+      '0',
+    );
+    const balanceAfterEvmCall = await helper.balance.getSubstrate(alice.address);
+    const evmCallDiff = balanceBeforeEvmCall - balanceAfterEvmCall;
+
+    expect(web3Diff).to.be.equal(evmCallDiff);
+  });
+});
addedjs-packages/tests/eth/events.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/events.test.ts
@@ -0,0 +1,548 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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} from 'chai';
+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 {CollectionLimitField, TokenPermissionField, CreateCollectionData} from './util/playgrounds/types.js';
+import type {NormalizedEvent} from './util/playgrounds/types.js';
+
+let donor: IKeyringPair;
+
+before(async function () {
+  await usingEthPlaygrounds(async (_helper, privateKey) => {
+    donor = await privateKey({url: import.meta.url});
+  });
+});
+
+function clearEvents(ethEvents: NormalizedEvent[] | null, subEvents: IEvent[]) {
+  if(ethEvents !== null) {
+    ethEvents.splice(0);
+  }
+  subEvents.splice(0);
+}
+
+async function testCollectionCreatedAndDestroy(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionCreated', 'CollectionDestroyed']}]);
+  const {collectionAddress, events: ethEvents} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+
+  await helper.wait.newBlocks(1);
+  {
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionCreated',
+        args: {
+          owner: owner,
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionCreated'}]);
+    clearEvents(ethEvents, subEvents);
+  }
+  {
+    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const result = await collectionHelper.methods.destroyCollection(collectionAddress).send({from:owner});
+    await helper.wait.newBlocks(1);
+    expect(result.events).to.containSubset({
+      CollectionDestroyed: {
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    });
+    expect(subEvents).to.containSubset([{method: 'CollectionDestroyed'}]);
+  }
+  unsubscribe();
+}
+
+async function testCollectionPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+
+  const ethEvents: any = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPropertySet', 'CollectionPropertyDeleted']}]);
+  {
+    await collection.methods.setCollectionProperties([{key: 'A', value: [0,1,2,3]}]).send({from:owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionPropertySet'}]);
+    clearEvents(ethEvents, subEvents);
+  }
+  {
+    await collection.methods.deleteCollectionProperties(['A']).send({from:owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionPropertyDeleted'}]);
+  }
+  unsubscribe();
+}
+
+async function testPropertyPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+  const ethEvents: any = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['PropertyPermissionSet']}]);
+  await collection.methods.setTokenPropertyPermissions([
+    ['A', [
+      [TokenPermissionField.Mutable, true],
+      [TokenPermissionField.TokenOwner, true],
+      [TokenPermissionField.CollectionAdmin, true]],
+    ],
+  ]).send({from: owner});
+  await helper.wait.newBlocks(1);
+  expect(ethEvents).to.containSubset([
+    {
+      event: 'CollectionChanged',
+      returnValues: {
+        collectionId: collectionAddress,
+      },
+    },
+  ]);
+  expect(subEvents).to.containSubset([{method: 'PropertyPermissionSet'}]);
+  unsubscribe();
+}
+
+async function testAllowListAddressAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const user = helper.ethCrossAccount.createAccount();
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+  const ethEvents: any[] = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['AllowListAddressAdded', 'AllowListAddressRemoved']}]);
+  {
+    await collection.methods.addToCollectionAllowListCross(user).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'AllowListAddressAdded'}]);
+    clearEvents(ethEvents, subEvents);
+  }
+  {
+    await collection.methods.removeFromCollectionAllowListCross(user).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'AllowListAddressRemoved'}]);
+  }
+  unsubscribe();
+}
+
+async function testCollectionAdminAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const user = helper.ethCrossAccount.createAccount();
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+  const ethEvents: any = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionAdminAdded', 'CollectionAdminRemoved']}]);
+  {
+    await collection.methods.addCollectionAdminCross(user).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionAdminAdded'}]);
+    clearEvents(ethEvents, subEvents);
+  }
+  {
+    await collection.methods.removeCollectionAdminCross(user).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionAdminRemoved'}]);
+  }
+  unsubscribe();
+}
+
+async function testCollectionLimitSet(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+  const ethEvents: any = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionLimitSet']}]);
+  {
+    await collection.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: 0}}).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionLimitSet'}]);
+  }
+  unsubscribe();
+}
+
+async function testCollectionOwnerChanged(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const newOwner = helper.ethCrossAccount.createAccount();
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+  const ethEvents: any = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionOwnerChanged']}]);
+  {
+    await collection.methods.changeCollectionOwnerCross(newOwner).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionOwnerChanged'}]);
+  }
+  unsubscribe();
+}
+
+async function testCollectionPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+  const ethEvents: any = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPermissionSet']}]);
+  {
+    await collection.methods.setCollectionMintMode(true).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
+    clearEvents(ethEvents, subEvents);
+  }
+  {
+    await collection.methods.setCollectionAccess(1).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
+  }
+  unsubscribe();
+}
+
+async function testCollectionSponsorSetAndConfirmedAndThenRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const sponsor = await helper.ethCrossAccount.createAccountWithBalance(donor);
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+  const ethEvents: any = [];
+  collectionHelper.events.allEvents((_: any, event: any) => {
+    ethEvents.push(event);
+  });
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{
+    section: 'common', names: ['CollectionSponsorSet', 'SponsorshipConfirmed', 'CollectionSponsorRemoved',
+    ]}]);
+  {
+    await collection.methods.setCollectionSponsorCross(sponsor).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([{
+      event: 'CollectionChanged',
+      returnValues: {
+        collectionId: collectionAddress,
+      },
+    }]);
+    expect(subEvents).to.containSubset([{method: 'CollectionSponsorSet'}]);
+    clearEvents(ethEvents, subEvents);
+  }
+  {
+    await collection.methods.confirmCollectionSponsorship().send({from: sponsor.eth});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'SponsorshipConfirmed'}]);
+    clearEvents(ethEvents, subEvents);
+  }
+  {
+    await collection.methods.removeCollectionSponsor().send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(ethEvents).to.containSubset([
+      {
+        event: 'CollectionChanged',
+        returnValues: {
+          collectionId: collectionAddress,
+        },
+      },
+    ]);
+    expect(subEvents).to.containSubset([{method: 'CollectionSponsorRemoved'}]);
+  }
+  unsubscribe();
+}
+
+async function testTokenPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
+  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
+  const result = await collection.methods.mint(owner).send({from: owner});
+  const tokenId = result.events.Transfer.returnValues.tokenId;
+  await collection.methods.setTokenPropertyPermissions([
+    ['A', [
+      [TokenPermissionField.Mutable, true],
+      [TokenPermissionField.TokenOwner, true],
+      [TokenPermissionField.CollectionAdmin, true]],
+    ],
+  ]).send({from: owner});
+
+  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['TokenPropertySet', 'TokenPropertyDeleted']}]);
+  {
+    const result = await collection.methods.setProperties(tokenId, [{key: 'A', value: [1,2,3]}]).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(result.events.TokenChanged).to.be.like({
+      event: 'TokenChanged',
+      returnValues: {
+        tokenId: tokenId,
+      },
+    });
+    expect(subEvents).to.containSubset([{method: 'TokenPropertySet'}]);
+    clearEvents(null, subEvents);
+  }
+  {
+    const result = await collection.methods.deleteProperties(tokenId, ['A']).send({from: owner});
+    await helper.wait.newBlocks(1);
+    expect(result.events.TokenChanged).to.be.like({
+      event: 'TokenChanged',
+      returnValues: {
+        tokenId: tokenId,
+      },
+    });
+    expect(subEvents).to.containSubset([{method: 'TokenPropertyDeleted'}]);
+  }
+  unsubscribe();
+}
+
+describe('[FT] Sync sub & eth events', () => {
+  const mode: TCollectionMode = 'ft';
+
+  itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => {
+    await testCollectionCreatedAndDestroy(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => {
+    await testCollectionPropertySetAndDeleted(helper, mode);
+  });
+
+  itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => {
+    await testAllowListAddressAddedAndRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => {
+    await testCollectionAdminAddedAndRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => {
+    await testCollectionLimitSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => {
+    await testCollectionOwnerChanged(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => {
+    await testCollectionPermissionSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => {
+    await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode);
+  });
+});
+
+describe('[NFT] Sync sub & eth events', () => {
+  const mode: TCollectionMode = 'nft';
+
+  itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => {
+    await testCollectionCreatedAndDestroy(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => {
+    await testCollectionPropertySetAndDeleted(helper, mode);
+  });
+
+  itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => {
+    await testPropertyPermissionSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => {
+    await testAllowListAddressAddedAndRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => {
+    await testCollectionAdminAddedAndRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => {
+    await testCollectionLimitSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => {
+    await testCollectionOwnerChanged(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => {
+    await testCollectionPermissionSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => {
+    await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => {
+    await testTokenPropertySetAndDeleted(helper, mode);
+  });
+});
+
+describe('[RFT] Sync sub & eth events', () => {
+  const mode: TCollectionMode = 'rft';
+
+  before(async function() {
+    await usingEthPlaygrounds((helper) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      return Promise.resolve();
+    });
+  });
+
+  itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => {
+    await testCollectionCreatedAndDestroy(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => {
+    await testCollectionPropertySetAndDeleted(helper, mode);
+  });
+
+  itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => {
+    await testPropertyPermissionSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => {
+    await testAllowListAddressAddedAndRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => {
+    await testCollectionAdminAddedAndRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => {
+    await testCollectionLimitSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => {
+    await testCollectionOwnerChanged(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => {
+    await testCollectionPermissionSet(helper, mode);
+  });
+
+  itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => {
+    await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode);
+  });
+
+  itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => {
+    await testTokenPropertySetAndDeleted(helper, mode);
+  });
+});
addedjs-packages/tests/eth/evmCoder.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/evmCoder.test.ts
@@ -0,0 +1,88 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itEth, expect, usingEthPlaygrounds} from './util/index.js';
+
+const getContractSource = (collectionAddress: string, contractAddress: string): string => `
+  // SPDX-License-Identifier: MIT
+  pragma solidity ^0.8.0;
+  interface ITest {
+    function ztestzzzzzzz() external returns (uint256 n);
+  }
+  contract Test {
+    event Result(bool, uint256);
+    function test1() public {
+        try
+            ITest(${collectionAddress}).ztestzzzzzzz()
+        returns (uint256 n) {
+            // enters
+            emit Result(true, n); // => [true, BigNumber { value: "43648854190028290368124427828690944273759144372138548774646036134290060795932" }]
+        } catch {
+            emit Result(false, 0);
+        }
+    }
+    function test2() public {
+        try
+            ITest(${contractAddress}).ztestzzzzzzz()
+        returns (uint256 n) {
+            emit Result(true, n);
+        } catch {
+            // enters
+            emit Result(false, 0); // => [ false, BigNumber { value: "0" } ]
+        }
+    }
+    function test3() public {
+      ITest(${collectionAddress}).ztestzzzzzzz();
+    }
+  }
+  `;
+
+
+describe('Evm Coder tests', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Call non-existing function', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.eth.createNFTCollection(owner, 'EVMCODER', '', 'TEST');
+    const contract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c'));
+    const testContract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, contract.options.address));
+    {
+      const result = await testContract.methods.test1().send();
+      expect(result.events.Result.returnValues).to.deep.equal({
+        '0': false,
+        '1': '0',
+      });
+    }
+    {
+      const result = await testContract.methods.test2().send();
+      expect(result.events.Result.returnValues).to.deep.equal({
+        '0': false,
+        '1': '0',
+      });
+    }
+    {
+      await expect(testContract.methods.test3().call())
+        .to.be.rejectedWith(/unrecognized selector: 0xd9f02b36$/g);
+    }
+  });
+});
addedjs-packages/tests/eth/fractionalizer/Fractionalizer.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/fractionalizer/Fractionalizer.sol
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier:  Apache License
+pragma solidity >=0.8.0;
+import {CollectionHelpers} from "../api/CollectionHelpers.sol";
+import {ContractHelpers} from "../api/ContractHelpers.sol";
+import {UniqueRefungibleToken} from "../api/UniqueRefungibleToken.sol";
+import {UniqueRefungible, CrossAddress} from "../api/UniqueRefungible.sol";
+import {UniqueNFT} from "../api/UniqueNFT.sol";
+
+/// @dev Fractionalization contract. It stores mappings between NFT and RFT tokens,
+///  stores allowlist of NFT tokens available for fractionalization, has methods
+///  for fractionalization and defractionalization of NFT tokens.
+contract Fractionalizer {
+	struct Token {
+		address _collection;
+		uint256 _tokenId;
+	}
+	address rftCollection;
+	mapping(address => bool) nftCollectionAllowList;
+	mapping(address => mapping(uint256 => uint256)) public nft2rftMapping;
+	mapping(address => Token) public rft2nftMapping;
+	//use constant to reduce gas cost
+	bytes32 constant refungibleCollectionType = keccak256(bytes("ReFungible"));
+
+	receive() external payable onlyOwner {}
+
+	/// @dev Method modifier to only allow contract owner to call it.
+	modifier onlyOwner() {
+		address contracthelpersAddress = 0x842899ECF380553E8a4de75bF534cdf6fBF64049;
+		ContractHelpers contractHelpers = ContractHelpers(contracthelpersAddress);
+		address contractOwner = contractHelpers.contractOwner(address(this));
+		require(msg.sender == contractOwner, "Only owner can");
+		_;
+	}
+
+	/// @dev This emits when RFT collection setting is changed.
+	event RFTCollectionSet(address _collection);
+
+	/// @dev This emits when NFT collection is allowed or disallowed.
+	event AllowListSet(address _collection, bool _status);
+
+	/// @dev This emits when NFT token is fractionalized by contract.
+	event Fractionalized(address _collection, uint256 _tokenId, address _rftToken, uint128 _amount);
+
+	/// @dev This emits when NFT token is defractionalized by contract.
+	event Defractionalized(address _rftToken, address _nftCollection, uint256 _nftTokenId);
+
+	/// Set RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens
+	/// would be created in this collection.
+	/// @dev Throws if RFT collection is already configured for this contract.
+	///  Throws if collection of wrong type (NFT, Fungible) is provided instead
+	///  of RFT collection.
+	///  Throws if `msg.sender` is not owner or admin of provided RFT collection.
+	///  Can only be called by contract owner.
+	/// @param _collection address of RFT collection.
+	function setRFTCollection(address _collection) external onlyOwner {
+		require(rftCollection == address(0), "RFT collection is already set");
+		UniqueRefungible refungibleContract = UniqueRefungible(_collection);
+		string memory collectionType = refungibleContract.uniqueCollectionType();
+
+		// compare hashed to reduce gas cost
+		require(
+			keccak256(bytes(collectionType)) == refungibleCollectionType,
+			"Wrong collection type. Collection is not refungible."
+		);
+		require(
+			refungibleContract.isOwnerOrAdminCross(CrossAddress({eth: address(this), sub: uint256(0)})),
+			"Fractionalizer contract should be an admin of the collection"
+		);
+		rftCollection = _collection;
+		emit RFTCollectionSet(rftCollection);
+	}
+
+	/// Creates and sets RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens
+	/// would be created in this collection.
+	/// @dev Throws if RFT collection is already configured for this contract.
+	///  Can only be called by contract owner.
+	/// @param _name name for created RFT collection.
+	/// @param _description description for created RFT collection.
+	/// @param _tokenPrefix token prefix for created RFT collection.
+	function createAndSetRFTCollection(
+		string calldata _name,
+		string calldata _description,
+		string calldata _tokenPrefix
+	) external payable onlyOwner {
+		require(rftCollection == address(0), "RFT collection is already set");
+		address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
+		rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection{value: msg.value}(
+			_name,
+			_description,
+			_tokenPrefix
+		);
+		emit RFTCollectionSet(rftCollection);
+	}
+
+	/// Allow or disallow NFT collection tokens from being fractionalized by this contract.
+	/// @dev Can only be called by contract owner.
+	/// @param collection NFT token address.
+	/// @param status `true` to allow and `false` to disallow NFT token.
+	function setNftCollectionIsAllowed(address collection, bool status) external onlyOwner {
+		nftCollectionAllowList[collection] = status;
+		emit AllowListSet(collection, status);
+	}
+
+	/// Fractionilize NFT token.
+	/// @dev Takes NFT token from `msg.sender` and transfers RFT token to `msg.sender`
+	///  instead. Creates new RFT token if provided NFT token never was fractionalized
+	///  by this contract or existing RFT token if it was.
+	///  Throws if RFT collection isn't configured for this contract.
+	///  Throws if fractionalization of provided NFT token is not allowed
+	///  Throws if `msg.sender` is not owner of provided NFT token
+	/// @param  _collection NFT collection address
+	/// @param  _token id of NFT token to be fractionalized
+	/// @param  _pieces number of pieces new RFT token would have
+	function nft2rft(
+		address _collection,
+		uint256 _token,
+		uint128 _pieces
+	) external {
+		require(rftCollection != address(0), "RFT collection is not set");
+		UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection);
+		require(
+			nftCollectionAllowList[_collection] == true,
+			"Fractionalization of this collection is not allowed by admin"
+		);
+		require(UniqueNFT(_collection).ownerOf(_token) == msg.sender, "Only token owner could fractionalize it");
+		UniqueNFT(_collection).transferFrom(msg.sender, address(this), _token);
+		uint256 rftTokenId;
+		address rftTokenAddress;
+		UniqueRefungibleToken rftTokenContract;
+		if (nft2rftMapping[_collection][_token] == 0) {
+			rftTokenId = rftCollectionContract.mint(address(this));
+			rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId);
+			nft2rftMapping[_collection][_token] = rftTokenId;
+			rft2nftMapping[rftTokenAddress] = Token(_collection, _token);
+
+			rftTokenContract = UniqueRefungibleToken(rftTokenAddress);
+		} else {
+			rftTokenId = nft2rftMapping[_collection][_token];
+			rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId);
+			rftTokenContract = UniqueRefungibleToken(rftTokenAddress);
+		}
+		rftTokenContract.repartition(_pieces);
+		rftTokenContract.transfer(msg.sender, _pieces);
+		emit Fractionalized(_collection, _token, rftTokenAddress, _pieces);
+	}
+
+	/// Defrationalize NFT token.
+	/// @dev Takes RFT token from `msg.sender` and transfers corresponding NFT token
+	///  to `msg.sender` instead.
+	///  Throws if RFT collection isn't configured for this contract.
+	///  Throws if provided RFT token is no from configured RFT collection.
+	///  Throws if RFT token was not created by this contract.
+	///  Throws if `msg.sender` isn't owner of all RFT token pieces.
+	/// @param _collection RFT collection address
+	/// @param _token id of RFT token
+	function rft2nft(address _collection, uint256 _token) external {
+		require(rftCollection != address(0), "RFT collection is not set");
+		require(rftCollection == _collection, "Wrong RFT collection");
+		UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection);
+		address rftTokenAddress = rftCollectionContract.tokenContractAddress(_token);
+		Token memory nftToken = rft2nftMapping[rftTokenAddress];
+		require(nftToken._collection != address(0), "No corresponding NFT token found");
+		UniqueRefungibleToken rftTokenContract = UniqueRefungibleToken(rftTokenAddress);
+		require(
+			rftTokenContract.balanceOf(msg.sender) == rftTokenContract.totalSupply(),
+			"Not all pieces are owned by the caller"
+		);
+		rftCollectionContract.transferFrom(msg.sender, address(this), _token);
+		UniqueNFT(nftToken._collection).transferFrom(address(this), msg.sender, nftToken._tokenId);
+		emit Defractionalized(rftTokenAddress, nftToken._collection, nftToken._tokenId);
+	}
+}
addedjs-packages/tests/eth/fractionalizer/fractionalizer.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/fractionalizer/fractionalizer.test.ts
@@ -0,0 +1,453 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {readFile} from 'fs/promises';
+
+import type {IKeyringPair} from '@polkadot/types/types';
+import {evmToAddress} from '@polkadot/util-crypto';
+
+import {Contract} from 'web3-eth-contract';
+
+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';
+
+const {dirname} = makeNames(import.meta.url);
+
+let compiledFractionalizer: CompiledContract;
+
+const compileContract = async (helper: EthUniqueHelper): Promise<CompiledContract> => {
+  if(!compiledFractionalizer) {
+    compiledFractionalizer = await helper.ethContract.compile('Fractionalizer', (await readFile(`${dirname}/Fractionalizer.sol`)).toString(), [
+      {solPath: 'api/CollectionHelpers.sol', fsPath: `${dirname}/../api/CollectionHelpers.sol`},
+      {solPath: 'api/ContractHelpers.sol', fsPath: `${dirname}/../api/ContractHelpers.sol`},
+      {solPath: 'api/UniqueRefungibleToken.sol', fsPath: `${dirname}/../api/UniqueRefungibleToken.sol`},
+      {solPath: 'api/UniqueRefungible.sol', fsPath: `${dirname}/../api/UniqueRefungible.sol`},
+      {solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`},
+    ]);
+  }
+  return compiledFractionalizer;
+};
+
+
+const deployContract = async (helper: EthUniqueHelper, owner: string): Promise<Contract> => {
+  const compiled = await compileContract(helper);
+  return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object);
+};
+
+
+const initContract = async (helper: EthUniqueHelper, owner: string): Promise<{contract: Contract, rftCollectionAddress: string}> => {
+  const fractionalizer = await deployContract(helper, owner);
+  const amount = 10n * helper.balance.getOneTokenNominal();
+  const web3 = helper.getWeb3();
+  await web3.eth.sendTransaction({from: owner, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS});
+  const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({value: Number(2n * helper.balance.getOneTokenNominal())});
+  const rftCollectionAddress = result.events.RFTCollectionSet.returnValues._collection;
+  return {contract: fractionalizer, rftCollectionAddress};
+};
+
+const mintRFTToken = async (helper: EthUniqueHelper, owner: string, fractionalizer: Contract, amount: bigint): Promise<{
+  nftCollectionAddress: string, nftTokenId: number, rftTokenAddress: string
+}> => {
+  const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+  const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+  const mintResult = await nftContract.methods.mint(owner).send({from: owner});
+  const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+  await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
+  await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
+  const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, amount).send({from: owner});
+  const {_collection, _tokenId, _rftToken} = result.events.Fractionalized.returnValues;
+  return {
+    nftCollectionAddress: _collection,
+    nftTokenId: _tokenId,
+    rftTokenAddress: _rftToken,
+  };
+};
+
+
+describe('Fractionalizer contract usage', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Set RFT collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const fractionalizer = await deployContract(helper, owner);
+    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
+    const rftContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
+
+    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
+    await rftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
+    const result = await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});
+    expect(result.events).to.be.like({
+      RFTCollectionSet: {
+        returnValues: {
+          _collection: rftCollection.collectionAddress,
+        },
+      },
+    });
+  });
+
+  itEth('Mint RFT collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const fractionalizer = await deployContract(helper, owner);
+    await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal());
+
+    const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())});
+    expect(result.events).to.be.like({
+      RFTCollectionSet: {},
+    });
+    expect(result.events.RFTCollectionSet.returnValues._collection).to.be.ok;
+  });
+
+  itEth('Set Allowlist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {contract: fractionalizer} = await initContract(helper, owner);
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+
+    const result1 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
+    expect(result1.events).to.be.like({
+      AllowListSet: {
+        returnValues: {
+          _collection: nftCollection.collectionAddress,
+          _status: true,
+        },
+      },
+    });
+    const result2 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, false).send({from: owner});
+    expect(result2.events).to.be.like({
+      AllowListSet: {
+        returnValues: {
+          _collection: nftCollection.collectionAddress,
+          _status: false,
+        },
+      },
+    });
+  });
+
+  itEth('NFT to RFT', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+    const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
+    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    const {contract: fractionalizer} = await initContract(helper, owner);
+
+    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
+    await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
+    const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).send({from: owner});
+    expect(result.events).to.be.like({
+      Fractionalized: {
+        returnValues: {
+          _collection: nftCollection.collectionAddress,
+          _tokenId: nftTokenId,
+          _amount: '100',
+        },
+      },
+    });
+    const rftTokenAddress = result.events.Fractionalized.returnValues._rftToken;
+
+    // FIXME: should work without the caller
+    const rftTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress);
+    expect(await rftTokenContract.methods.balanceOf(owner).call()).to.equal('100');
+  });
+
+  itEth('RFT to NFT', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner);
+    const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n);
+
+    const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress);
+    const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId);
+    expect(rftCollectionAddress).to.be.equal(refungibleAddress);
+    const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner);
+    await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner});
+    const result = await fractionalizer.methods.rft2nft(refungibleAddress, tokenId).send({from: owner});
+    expect(result.events).to.be.like({
+      Defractionalized: {
+        returnValues: {
+          _rftToken: rftTokenAddress,
+          _nftCollection: nftCollectionAddress,
+          _nftTokenId: nftTokenId,
+        },
+      },
+    });
+  });
+
+  itEth('Test fractionalizer NFT <-> RFT mapping ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner);
+    const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n);
+
+    const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress);
+    const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId);
+    expect(rftCollectionAddress).to.be.equal(refungibleAddress);
+    const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner);
+    await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner});
+
+    const rft2nft = await fractionalizer.methods.rft2nftMapping(rftTokenAddress).call();
+    expect(rft2nft).to.be.like({
+      _collection: nftCollectionAddress,
+      _tokenId: nftTokenId,
+    });
+
+    const nft2rft = await fractionalizer.methods.nft2rftMapping(nftCollectionAddress, nftTokenId).call();
+    expect(nft2rft).to.be.eq(tokenId.toString());
+  });
+});
+
+
+
+describe('Negative Integration Tests for fractionalizer', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('call setRFTCollection twice', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
+    const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
+
+    const fractionalizer = await deployContract(helper, owner);
+    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
+    await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
+    await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});
+
+    await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call())
+      .to.be.rejectedWith(/RFT collection is already set$/g);
+  });
+
+  itEth('call setRFTCollection with NFT collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+
+    const fractionalizer = await deployContract(helper, owner);
+    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
+    await nftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
+
+    await expect(fractionalizer.methods.setRFTCollection(nftCollection.collectionAddress).call())
+      .to.be.rejectedWith(/Wrong collection type. Collection is not refungible.$/g);
+  });
+
+  itEth('call setRFTCollection while not collection admin', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const fractionalizer = await deployContract(helper, owner);
+    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
+
+    await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call())
+      .to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g);
+  });
+
+  itEth('call setRFTCollection after createAndSetRFTCollection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const fractionalizer = await deployContract(helper, owner);
+    await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal());
+
+    const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())});
+    const collectionIdAddress = result.events.RFTCollectionSet.returnValues._collection;
+
+    await expect(fractionalizer.methods.setRFTCollection(collectionIdAddress).call())
+      .to.be.rejectedWith(/RFT collection is already set$/g);
+  });
+
+  itEth('call nft2rft without setting RFT collection for contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
+    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    const fractionalizer = await deployContract(helper, owner);
+
+    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call())
+      .to.be.rejectedWith(/RFT collection is not set$/g);
+  });
+
+  itEth('call nft2rft while not owner of NFT token', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const nftOwner = await helper.eth.createAccountWithBalance(donor);
+
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
+    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+    await nftContract.methods.transfer(nftOwner, 1).send({from: owner});
+
+
+    const {contract: fractionalizer} = await initContract(helper, owner);
+    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
+
+    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call({from: owner}))
+      .to.be.rejectedWith(/Only token owner could fractionalize it$/g);
+  });
+
+  itEth('call nft2rft while not in list of allowed accounts', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
+    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    const {contract: fractionalizer} = await initContract(helper, owner);
+
+    await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
+    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call())
+      .to.be.rejectedWith(/Fractionalization of this collection is not allowed by admin$/g);
+  });
+
+  itEth('call nft2rft while fractionalizer doesnt have approval for nft token', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+    const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
+    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    const {contract: fractionalizer} = await initContract(helper, owner);
+
+    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
+    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call())
+      .to.be.rejectedWith(/ApprovedValueTooLow$/g);
+  });
+
+  itEth('call rft2nft without setting RFT collection for contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const fractionalizer = await deployContract(helper, owner);
+    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
+    const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
+    const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});
+    const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call({from: owner}))
+      .to.be.rejectedWith(/RFT collection is not set$/g);
+  });
+
+  itEth('call rft2nft for RFT token that is not from configured RFT collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const {contract: fractionalizer} = await initContract(helper, owner);
+    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
+    const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
+    const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});
+    const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())
+      .to.be.rejectedWith(/Wrong RFT collection$/g);
+  });
+
+  itEth('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
+    const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
+
+    const fractionalizer = await deployContract(helper, owner);
+
+    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
+    await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
+    await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});
+
+    const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});
+    const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())
+      .to.be.rejectedWith(/No corresponding NFT token found$/g);
+  });
+
+  itEth('call rft2nft without owning all RFT pieces', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+
+    const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner);
+    const {rftTokenAddress} = await mintRFTToken(helper, owner, fractionalizer, 100n);
+
+    const {tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress);
+    const refungibleTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress, owner);
+    await refungibleTokenContract.methods.transfer(receiver, 50).send({from: owner});
+    await refungibleTokenContract.methods.approve(fractionalizer.options.address, 50).send({from: receiver});
+    await expect(fractionalizer.methods.rft2nft(rftCollectionAddress, tokenId).call({from: receiver}))
+      .to.be.rejectedWith(/Not all pieces are owned by the caller$/g);
+  });
+
+  itEth('send QTZ/UNQ to contract from non owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const payer = await helper.eth.createAccountWithBalance(donor);
+
+    const fractionalizer = await deployContract(helper, owner);
+    const amount = 10n * helper.balance.getOneTokenNominal();
+    const web3 = helper.getWeb3();
+    await expect(web3.eth.sendTransaction({from: payer, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS})).to.be.rejected;
+  });
+
+  itEth('fractionalize NFT with NFT transfers disallowed', async ({helper}) => {
+    const nftCollection = await helper.nft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const nftToken = await nftCollection.mintToken(donor, {Ethereum: owner});
+    await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [nftCollection.collectionId, false], true);
+    const nftCollectionAddress = helper.ethAddress.fromCollectionId(nftCollection.collectionId);
+    const {contract: fractionalizer} = await initContract(helper, owner);
+    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollectionAddress, true).send({from: owner});
+
+    const nftContract = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner);
+    await nftContract.methods.approve(fractionalizer.options.address, nftToken.tokenId).send({from: owner});
+    await expect(fractionalizer.methods.nft2rft(nftCollectionAddress, nftToken.tokenId, 100).call())
+      .to.be.rejectedWith(/TransferNotAllowed$/g);
+  });
+
+  itEth('fractionalize NFT with RFT transfers disallowed', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const rftCollection = await helper.rft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const rftCollectionAddress = helper.ethAddress.fromCollectionId(rftCollection.collectionId);
+    const fractionalizer = await deployContract(helper, owner);
+    await rftCollection.addAdmin(donor, {Ethereum: fractionalizer.options.address});
+
+    await fractionalizer.methods.setRFTCollection(rftCollectionAddress).send({from: owner});
+    await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [rftCollection.collectionId, false], true);
+
+    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
+    const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
+    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
+    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
+
+    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
+    await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
+
+    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100n).call())
+      .to.be.rejectedWith(/TransferNotAllowed$/g);
+  });
+});
addedjs-packages/tests/eth/fungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/fungible.test.ts
@@ -0,0 +1,643 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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, itEth, usingEthPlaygrounds} from './util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+
+describe('Fungible: Plain calls', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let owner: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, owner] = await helper.arrange.createAccounts([30n, 20n], donor);
+    });
+  });
+
+  [
+    'substrate' as const,
+    'ethereum' as const,
+  ].map(testCase => {
+    itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
+      // 1. Create receiver depending on the test case:
+      const receiverEth = helper.eth.createAccount();
+      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+      const receiverSub = owner;
+      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(owner);
+
+      const ethOwner = await helper.eth.createAccountWithBalance(donor);
+      const collection = await helper.ft.mintCollection(alice);
+      await collection.addAdmin(alice, {Ethereum: ethOwner});
+
+      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner);
+
+      // 2. Mint tokens:
+      const result = await collectionEvm.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, 100).send();
+
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(receiverSub.address));
+      expect(event.returnValues.value).to.equal('100');
+
+      // 3. Get balance depending on the test case:
+      let balance;
+      if(testCase === 'ethereum') balance = await collection.getBalance({Ethereum: receiverEth});
+      else if(testCase === 'substrate') balance = await collection.getBalance({Substrate: receiverSub.address});
+      // 3.1 Check balance:
+      expect(balance).to.eq(100n);
+    });
+  });
+
+  itEth('Can perform mintBulk()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const bulkSize = 3;
+    const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount());
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.addAdmin(alice, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => (
+      [receivers[i], (i + 1) * 10]
+    ))).send();
+    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value);
+    for(let i = 0; i < bulkSize; i++) {
+      const event = events[i];
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.equal(receivers[i]);
+      expect(event.returnValues.value).to.equal(String(10 * (i + 1)));
+    }
+  });
+
+  // Soft-deprecated
+  itEth('Can perform burn()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.addAdmin(alice, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    await contract.methods.mint(receiver, 100).send();
+
+    const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver});
+
+    const event = result.events.Transfer;
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal(receiver);
+    expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.value).to.equal('49');
+
+    const balance = await contract.methods.balanceOf(receiver).call();
+    expect(balance).to.equal('51');
+  });
+
+  itEth('Can perform approve()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    {
+      const result = await contract.methods.approve(spender, 100).send({from: owner});
+
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(spender);
+      expect(event.returnValues.value).to.be.equal('100');
+    }
+
+    {
+      const allowance = await contract.methods.allowance(owner, spender).call();
+      expect(+allowance).to.equal(100);
+    }
+    {
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const spenderCross = helper.ethCrossAccount.fromAddress(spender);
+      const allowance = await contract.methods.allowanceCross(ownerCross, spenderCross).call();
+      expect(+allowance).to.equal(100);
+    }
+  });
+
+  itEth('Can perform approveCross()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0];
+    const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
+    const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub);
+
+
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    {
+      const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(spender);
+      expect(event.returnValues.value).to.be.equal('100');
+    }
+
+    {
+      const allowance = await contract.methods.allowance(owner, spender).call();
+      expect(+allowance).to.equal(100);
+    }
+
+
+    {
+      const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner});
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address));
+      expect(event.returnValues.value).to.be.equal('100');
+    }
+
+    {
+      const allowance = await collection.getApprovedTokens({Ethereum: owner}, {Substrate: spenderSub.address});
+      expect(allowance).to.equal(100n);
+    }
+
+    {
+      //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call()
+    }
+  });
+
+  itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
+    const nonOwner = await helper.eth.createAccountWithBalance(donor);
+    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    await collection.mint(alice, 100n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    await expect(collectionEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
+  });
+
+
+  itEth('Can perform burnFromCross()', async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
+
+    await collection.mint(owner, 200n, {Substrate: owner.address});
+    await collection.approveTokens(owner, {Ethereum: sender}, 100n);
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'ft');
+
+    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});
+
+    const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+    const result = await contract.methods.burnFromCross(ownerCross, 49).send({from: sender});
+    const events = result.events;
+
+    expect(events).to.be.like({
+      Transfer: {
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: '0x0000000000000000000000000000000000000000',
+          value: '49',
+        },
+      },
+      Approval: {
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        returnValues: {
+          owner: helper.address.substrateToEth(owner.address),
+          spender: sender,
+          value: '51',
+        },
+        event: 'Approval',
+      },
+    });
+
+    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});
+    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(49n);
+  });
+
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    await contract.methods.approve(spender, 100).send();
+
+    {
+      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});
+
+      let event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.value).to.be.equal('49');
+
+      event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(spender);
+      expect(event.returnValues.value).to.be.equal('51');
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(49);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(owner).call();
+      expect(+balance).to.equal(151);
+    }
+  });
+
+  itEth('Can perform transferCross()', async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+    const receiverEth = await helper.eth.createAccountWithBalance(donor);
+    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: sender});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender);
+
+    {
+      // Can transferCross to ethereum address:
+      const result = await collectionEvm.methods.transferCross(receiverCrossEth, 50).send({from: sender});
+      // Check events:
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(sender);
+      expect(event.returnValues.to).to.be.equal(receiverEth);
+      expect(event.returnValues.value).to.be.equal('50');
+      // Sender's balance decreased:
+      const ownerBalance = await collectionEvm.methods.balanceOf(sender).call();
+      expect(+ownerBalance).to.equal(150);
+      // Receiver's balance increased:
+      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
+      expect(+receiverBalance).to.equal(50);
+    }
+
+    {
+      // Can transferCross to substrate address:
+      const result = await collectionEvm.methods.transferCross(receiverCrossSub, 50).send({from: sender});
+      // Check events:
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(sender);
+      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(donor.address));
+      expect(event.returnValues.value).to.be.equal('50');
+      // Sender's balance decreased:
+      const senderBalance = await collection.getBalance({Ethereum: sender});
+      expect(senderBalance).to.equal(100n);
+      // Receiver's balance increased:
+      const balance = await collection.getBalance({Substrate: donor.address});
+      expect(balance).to.equal(50n);
+    }
+  });
+
+  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} incorrect amount`, async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+    const receiverEth = await helper.eth.createAccountWithBalance(donor);
+    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+    const BALANCE = 200n;
+    const BALANCE_TO_TRANSFER = BALANCE + 100n;
+
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, BALANCE, {Ethereum: sender});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender);
+
+    // 1. Cannot transfer more than have
+    const receiver = testCase === 'transfer' ? receiverEth : receiverCrossEth;
+    await expect(collectionEvm.methods[testCase](receiver, BALANCE_TO_TRANSFER).send({from: sender})).to.be.rejected;
+    // 2. Zero transfer allowed (EIP-20):
+    await collectionEvm.methods[testCase](receiver, 0n).send({from: sender});
+  }));
+
+
+  itEth('Can perform transfer()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    {
+      const result = await contract.methods.transfer(receiver, 50).send({from: owner});
+
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.value).to.be.equal('50');
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(owner).call();
+      expect(+balance).to.equal(150);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(50);
+    }
+  });
+
+  itEth('Can perform transferFromCross()', async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
+
+    const receiver = helper.eth.createAccount();
+
+    await collection.mint(owner, 200n, {Substrate: owner.address});
+    await collection.approveTokens(owner, {Ethereum: sender}, 100n);
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'ft');
+
+    const from = helper.ethCrossAccount.fromKeyringPair(owner);
+    const to = helper.ethCrossAccount.fromAddress(receiver);
+
+    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});
+    const toBalanceBefore = await collection.getBalance({Ethereum: receiver});
+
+    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});
+
+    expect(result.events).to.be.like({
+      Transfer: {
+        address,
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: receiver,
+          value: '51',
+        },
+      },
+      Approval: {
+        address,
+        event: 'Approval',
+        returnValues: {
+          owner: helper.address.substrateToEth(owner.address),
+          spender: sender,
+          value: '49',
+        },
+      }});
+
+    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});
+    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(51n);
+    const toBalanceAfter = await collection.getBalance({Ethereum: receiver});
+    expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n);
+  });
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const other = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
+
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await collection.mint(alice, 100n, {Ethereum: owner.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
+
+    await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
+
+    await collectionEvm.methods.transferCross(owner, 100n).send({from: other.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+  });
+});
+
+describe('Fungible: Fees', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([20n], donor);
+    });
+  });
+
+  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+
+  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    await contract.methods.approve(spender, 100).send({from: owner});
+
+    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+
+  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+});
+
+describe('Fungible: Substrate calls', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let owner: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, owner] = await helper.arrange.createAccounts([20n, 20n], donor);
+    });
+  });
+
+  itEth('Events emitted for approve()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await collection.approveTokens(alice, {Ethereum: receiver}, 100n);
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.spender).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('100');
+  });
+
+  itEth('Events emitted for transferFrom()', async ({helper}) => {
+    const [bob] = await helper.arrange.createAccounts([10n], donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n);
+    await collection.approveTokens(alice, {Substrate: bob.address}, 100n);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n);
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    let event = events[0];
+
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('51');
+
+    event = events[1];
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
+    expect(event.returnValues.value).to.be.equal('49');
+  });
+
+  itEth('Events emitted for transfer()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.mint(alice, 200n);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await collection.transfer(alice, {Ethereum:receiver}, 51n);
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('51');
+  });
+
+  itEth('Events emitted for transferFromCross()', async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
+
+    const receiver = helper.eth.createAccount();
+
+    await collection.mint(owner, 200n, {Substrate: owner.address});
+    await collection.approveTokens(owner, {Ethereum: sender}, 100n);
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'ft');
+
+    const from = helper.ethCrossAccount.fromKeyringPair(owner);
+    const to = helper.ethCrossAccount.fromAddress(receiver);
+
+    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});
+
+    expect(result.events).to.be.like({
+      Transfer: {
+        address,
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: receiver,
+          value: '51',
+        },
+      },
+      Approval: {
+        address,
+        event: 'Approval',
+        returnValues: {
+          owner: helper.address.substrateToEth(owner.address),
+          spender: sender,
+          value: '49',
+        },
+      }});
+  });
+});
addedjs-packages/tests/eth/getCode.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/getCode.test.ts
@@ -0,0 +1,53 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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, itEth, usingEthPlaygrounds} from './util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {COLLECTION_HELPER, CONTRACT_HELPER} from '../util/index.js';
+
+describe('RPC eth_getCode', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  [
+    {address: COLLECTION_HELPER},
+    {address: CONTRACT_HELPER},
+  ].map(testCase => {
+    itEth(`returns value for native contract: ${testCase.address}`, async ({helper}) => {
+      const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [testCase.address])).toJSON();
+      const contractCodeEth = (await helper.getWeb3().eth.getCode(testCase.address));
+
+      expect(contractCodeSub).to.has.length.greaterThan(4);
+      expect(contractCodeEth).to.has.length.greaterThan(4);
+    });
+  });
+
+  itEth('returns value for custom contract', async ({helper}) => {
+    const signer = await helper.eth.createAccountWithBalance(donor);
+    const flipper = await helper.eth.deployFlipper(signer);
+
+    const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [flipper.options.address])).toJSON();
+    const contractCodeEth = (await helper.getWeb3().eth.getCode(flipper.options.address));
+
+    expect(contractCodeSub).to.has.length.greaterThan(4);
+    expect(contractCodeEth).to.has.length.greaterThan(4);
+  });
+});
addedjs-packages/tests/eth/helpersSmoke.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/helpersSmoke.test.ts
@@ -0,0 +1,46 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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, itEth, usingEthPlaygrounds} from './util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+
+describe('Helpers sanity check', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Contract owner is recorded', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await (await helper.ethNativeContract.contractHelpers(owner)).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner);
+  });
+
+  itEth('Flipper is working', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await flipper.methods.getValue().call()).to.be.false;
+    await flipper.methods.flip().send({from: owner});
+    expect(await flipper.methods.getValue().call()).to.be.true;
+  });
+});
addedjs-packages/tests/eth/marketplace-v2/Market.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace-v2/Market.sol
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.18;
+
+import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
+import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
+import "@openzeppelin/contracts/access/Ownable.sol";
+import { UniqueNFT, CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol";
+import { UniqueFungible, CrossAddress as CrossAddressF } from "@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol";
+import { CollectionHelpers } from "@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol";
+import "./royalty/UniqueRoyaltyHelper.sol";
+
+contract Market is Ownable, ReentrancyGuard {
+    using ERC165Checker for address;
+
+    struct Order {
+      uint32 id;
+      uint32 collectionId;
+      uint32 tokenId;
+      uint32 amount;
+      uint256 price;
+      CrossAddress seller;
+    }
+
+    uint32 public constant version = 0;
+    uint32 public constant buildVersion = 3;
+    bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd;
+    bytes4 private constant InterfaceId_ERC165 = 0x5755c3f2;
+    CollectionHelpers private constant collectionHelpers =
+        CollectionHelpers(0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F);
+
+    mapping(uint32 => mapping(uint32 => Order)) orders;
+    uint32 private idCount = 1;
+    uint32 public marketFee;
+    uint64 public ctime;
+    address public ownerAddress;
+    mapping(address => bool) public admins;
+
+    event TokenIsUpForSale(uint32 version, Order item);
+    event TokenRevoke(uint32 version, Order item, uint32 amount);
+    event TokenIsApproved(uint32 version, Order item);
+    event TokenIsPurchased(
+      uint32 version,
+      Order item,
+      uint32 salesAmount,
+      CrossAddress buyer,
+      RoyaltyAmount[] royalties
+    );
+    event Log(string message);
+
+    error InvalidArgument(string info);
+    error InvalidMarketFee();
+    error SellerIsNotOwner();
+    error TokenIsAlreadyOnSale();
+    error TokenIsNotApproved();
+    error CollectionNotFound();
+    error CollectionNotSupportedERC721();
+    error OrderNotFound();
+    error TooManyAmountRequested();
+    error NotEnoughMoneyError();
+    error InvalidRoyaltiesError(uint256 totalRoyalty);
+    error FailTransferToken(string reason);
+
+    modifier onlyAdmin() {
+      require(msg.sender == this.owner() || admins[msg.sender], "Only admin can");
+      _;
+    }
+
+    modifier validCrossAddress(address eth, uint256 sub) {
+      if (eth == address(0) && sub == 0) {
+        revert InvalidArgument("Ethereum and Substrate addresses cannot be null at the same time");
+      }
+
+      if (eth != address(0) && sub != 0) {
+        revert InvalidArgument("Ethereum and Substrate addresses cannot be not null at the same time");
+      }
+
+      _;
+    }
+
+    constructor(uint32 fee, uint64 timestamp) {
+        marketFee = fee;
+        ctime = timestamp;
+
+        if (marketFee >= 100) {
+            revert InvalidMarketFee();
+        }
+    }
+
+    /**
+     * Fallback that allows this contract to receive native token.
+     * We need this for self-sponsoring
+     */
+    fallback() external payable {}
+
+    /**
+     * Receive also allows this contract to receive native token.
+     * We need this for self-sponsoring
+     */
+    receive() external payable {}
+
+    function getErc721(uint32 collectionId) private view returns (IERC721) {
+        address collectionAddress = collectionHelpers.collectionAddress(
+            collectionId
+        );
+
+        uint size;
+        assembly {
+            size := extcodesize(collectionAddress)
+        }
+
+        if (size == 0) {
+            revert CollectionNotFound();
+        }
+
+        if (!collectionAddress.supportsInterface(InterfaceId_ERC721)) {
+            revert CollectionNotSupportedERC721();
+        }
+
+        return IERC721(collectionAddress);
+    }
+
+    /**
+     * Add new admin. Only owner or an existing admin can add admins.
+     *
+     * @param admin: Address of a new admin to add
+     */
+    function addAdmin(address admin) public onlyAdmin {
+      admins[admin] = true;
+    }
+
+    /**
+     * Remove an admin. Only owner or an existing admin can remove admins.
+     *
+     * @param admin: Address of a new admin to add
+     */
+    function removeAdmin(address admin) public onlyAdmin {
+      delete admins[admin];
+    }
+
+    /**
+     * Place an NFT or RFT token for sale. It must be pre-approved for transfers by this contract address.
+     *
+     * @param collectionId: ID of the token collection
+     * @param tokenId: ID of the token
+     * @param price: Price (with proper network currency decimals)
+     * @param amount: Number of token fractions to list (must always be 1 for NFT)
+     * @param seller: The seller cross-address (the beneficiary account to receive payment, may be different from transaction sender)
+     */
+    function put(
+        uint32 collectionId,
+        uint32 tokenId,
+        uint256 price,
+        uint32 amount,
+        CrossAddress memory seller
+    ) public validCrossAddress(seller.eth, seller.sub) {
+        if (price == 0) {
+          revert InvalidArgument("price must not be zero");
+        }
+        if (amount == 0) {
+          revert InvalidArgument("amount must not be zero");
+        }
+
+        if (orders[collectionId][tokenId].price > 0) {
+            revert TokenIsAlreadyOnSale();
+        }
+
+        IERC721 erc721 = getErc721(collectionId);
+
+        if (erc721.ownerOf(tokenId) != msg.sender) {
+          revert SellerIsNotOwner();
+        }
+
+        if (erc721.getApproved(tokenId) != address(this)) {
+          revert TokenIsNotApproved();
+        }
+
+        Order memory order = Order(
+            0,
+            collectionId,
+            tokenId,
+            amount,
+            price,
+            seller
+        );
+
+        order.id = idCount++;
+        orders[collectionId][tokenId] = order;
+
+        emit TokenIsUpForSale(version, order);
+    }
+
+    /**
+     * Get information about the listed token order
+     *
+     * @param collectionId: ID of the token collection
+     * @param tokenId: ID of the token
+     * @return The order information
+     */
+    function getOrder(
+        uint32 collectionId,
+        uint32 tokenId
+    ) external view returns (Order memory) {
+        return orders[collectionId][tokenId];
+    }
+
+    /**
+     * Revoke the token from the sale. Only the original lister can use this method.
+     *
+     * @param collectionId: ID of the token collection
+     * @param tokenId: ID of the token
+     * @param amount: Number of token fractions to de-list (must always be 1 for NFT)
+     */
+    function revoke(
+        uint32 collectionId,
+        uint32 tokenId,
+        uint32 amount
+    ) external {
+        if (amount == 0) {
+          revert InvalidArgument("amount must not be zero");
+        }
+
+        Order memory order = orders[collectionId][tokenId];
+
+        if (order.price == 0) {
+          revert OrderNotFound();
+        }
+
+        if (amount > order.amount) {
+          revert TooManyAmountRequested();
+        }
+
+        IERC721 erc721 = getErc721(collectionId);
+
+        address ethAddress;
+        if (order.seller.eth != address(0)) {
+          ethAddress = order.seller.eth;
+        } else {
+          ethAddress = payable(address(uint160(order.seller.sub >> 96)));
+        }
+        if (erc721.ownerOf(tokenId) != ethAddress) {
+          revert SellerIsNotOwner();
+        }
+
+        order.amount -= amount;
+        if (order.amount == 0) {
+            delete orders[collectionId][tokenId];
+        } else {
+            orders[collectionId][tokenId] = order;
+        }
+
+        emit TokenRevoke(version, order, amount);
+    }
+
+    /**
+     * Test if the token is still approved to be transferred by this contract and delete the order if not.
+     *
+     * @param collectionId: ID of the token collection
+     * @param tokenId: ID of the token
+     */
+    function checkApproved(uint32 collectionId, uint32 tokenId) public onlyAdmin {
+        Order memory order = orders[collectionId][tokenId];
+        if (order.price == 0) {
+            revert OrderNotFound();
+        }
+
+        IERC721 erc721 = getErc721(collectionId);
+
+        if (erc721.getApproved(tokenId) != address(this) || erc721.ownerOf(tokenId) != getAddressFromCrossAccount(order.seller)) {
+          uint32 amount = order.amount;
+          order.amount = 0;
+          emit TokenRevoke(version, order, amount);
+
+          delete orders[collectionId][tokenId];
+        } else {
+          emit TokenIsApproved(version, order);
+        }
+    }
+
+    function getAddressFromCrossAccount(CrossAddress memory account) private pure returns (address) {
+        if (account.eth != address(0)) {
+            return account.eth;
+        } else {
+            return address(uint160(account.sub >> 96));
+        }
+    }
+
+    /**
+     * Revoke the token from the sale. Only the contract admin can use this method.
+     *
+     * @param collectionId: ID of the token collection
+     * @param tokenId: ID of the token
+     */
+    function revokeAdmin(uint32 collectionId, uint32 tokenId) public onlyAdmin {
+        Order memory order = orders[collectionId][tokenId];
+        if (order.price == 0) {
+          revert OrderNotFound();
+        }
+
+        uint32 amount = order.amount;
+        order.amount = 0;
+        emit TokenRevoke(version, order, amount);
+
+        delete orders[collectionId][tokenId];
+    }
+
+    /**
+     * Buy a token (partially for an RFT).
+     *
+     * @param collectionId: ID of the token collection
+     * @param tokenId: ID of the token
+     * @param amount: Number of token fractions to buy (must always be 1 for NFT)
+     * @param buyer: Cross-address of the buyer, eth part must be equal to the transaction signer address
+     */
+    function buy(
+        uint32 collectionId,
+        uint32 tokenId,
+        uint32 amount,
+        CrossAddress memory buyer
+    ) public payable validCrossAddress(buyer.eth, buyer.sub) nonReentrant {
+        if (msg.value == 0) {
+          revert InvalidArgument("msg.value must not be zero");
+        }
+        if (amount == 0) {
+          revert InvalidArgument("amount must not be zero");
+        }
+
+        Order memory order = orders[collectionId][tokenId];
+        if (order.price == 0) {
+            revert OrderNotFound();
+        }
+
+        if (amount > order.amount) {
+            revert TooManyAmountRequested();
+        }
+
+        uint256 totalValue = order.price * amount;
+        uint256 feeValue = (totalValue * marketFee) / 100;
+
+        if (msg.value < totalValue) {
+            revert NotEnoughMoneyError();
+        }
+
+        IERC721 erc721 = getErc721(order.collectionId);
+        if (erc721.getApproved(tokenId) != address(this)) {
+          revert TokenIsNotApproved();
+        }
+
+        order.amount -= amount;
+        if (order.amount == 0) {
+            delete orders[collectionId][tokenId];
+        } else {
+            orders[collectionId][tokenId] = order;
+        }
+
+        address collectionAddress = collectionHelpers.collectionAddress(collectionId);
+        UniqueNFT nft = UniqueNFT(collectionAddress);
+
+        nft.transferFromCross(
+          order.seller,
+          buyer,
+          order.tokenId
+        );
+
+        (uint256 totalRoyalty, RoyaltyAmount[] memory royalties) = sendRoyalties(collectionAddress, tokenId, totalValue - feeValue);
+
+        if (totalRoyalty >= totalValue - feeValue) {
+          revert InvalidRoyaltiesError(totalRoyalty);
+        }
+
+        sendMoney(order.seller, totalValue - feeValue - totalRoyalty);
+
+        if (msg.value > totalValue) {
+            sendMoney(buyer, msg.value - totalValue);
+        }
+
+        emit TokenIsPurchased(version, order, amount, buyer, royalties);
+    }
+
+    function sendMoney(CrossAddress memory to, uint256 money) private {
+      address collectionAddress = collectionHelpers.collectionAddress(0);
+
+      UniqueFungible fungible = UniqueFungible(collectionAddress);
+
+      CrossAddressF memory fromF = CrossAddressF(address(this), 0);
+      CrossAddressF memory toF = CrossAddressF(to.eth, to.sub);
+
+      fungible.transferFromCross(fromF, toF, money);
+    }
+
+    function sendRoyalties(address collection, uint tokenId, uint sellPrice) private returns (uint256, RoyaltyAmount[] memory) {
+      RoyaltyAmount[] memory royalties = UniqueRoyaltyHelper.calculate(collection, tokenId, sellPrice);
+
+      uint256 totalRoyalty = 0;
+
+      for (uint256 i=0; i<royalties.length; i++) {
+        RoyaltyAmount memory royalty = royalties[i];
+
+        totalRoyalty += royalty.amount;
+
+        sendMoney(royalty.crossAddress, royalty.amount);
+      }
+
+      return (totalRoyalty, royalties);
+    }
+
+    function withdraw(address transferTo) public onlyOwner {
+        uint256 balance = address(this).balance;
+
+        if (balance > 0) {
+            payable(transferTo).transfer(balance);
+        }
+    }
+}
addedjs-packages/tests/eth/marketplace-v2/marketplace.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace-v2/marketplace.test.ts
@@ -0,0 +1,240 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 * as web3 from 'web3';
+import type {IKeyringPair} from '@polkadot/types/types';
+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';
+
+const {dirname} = makeNames(import.meta.url);
+
+const MARKET_FEE = 1;
+
+describe('Market V2 Contract', () => {
+  let donor: IKeyringPair;
+
+  before(async () => {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+
+      const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n);
+
+      await deployMarket(helper, marketOwner);
+    });
+  });
+
+  async function deployMarket(helper: EthUniqueHelper, marketOwner: string) {
+    return await helper.ethContract.deployByCode(
+      marketOwner,
+      'Market',
+      (await readFile(`${dirname}/Market.sol`)).toString(),
+      [
+        {
+          solPath: '@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol',
+          fsPath: `${dirname}/../api/UniqueNFT.sol`,
+        },
+        {
+          solPath: '@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol',
+          fsPath: `${dirname}/../api/UniqueFungible.sol`,
+        },
+        {
+          solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol',
+          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`,
+        },
+        {
+          solPath: '@openzeppelin/contracts/access/Ownable.sol',
+          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/access/Ownable.sol`,
+        },
+        {
+          solPath: '@openzeppelin/contracts/utils/Context.sol',
+          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`,
+        },
+        {
+          solPath: '@openzeppelin/contracts/security/ReentrancyGuard.sol',
+          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol`,
+        },
+        {
+          solPath: '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol',
+          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/ERC165Checker.sol`,
+        },
+        {
+          solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol',
+          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`,
+        },
+        {
+          solPath: '@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol',
+          fsPath: `${dirname}/../api/CollectionHelpers.sol`,
+        },
+        {
+          solPath: 'royalty/UniqueRoyaltyHelper.sol',
+          fsPath: `${dirname}/royalty/UniqueRoyaltyHelper.sol`,
+        },
+        {
+          solPath: 'royalty/UniqueRoyalty.sol',
+          fsPath: `${dirname}/royalty/UniqueRoyalty.sol`,
+        },
+        {
+          solPath: 'royalty/LibPart.sol',
+          fsPath: `${dirname}/royalty/LibPart.sol`,
+        },
+      ],
+      15000000,
+      [MARKET_FEE, 0],
+    );
+  }
+
+  function substrateAddressToHex(sub: Uint8Array| string, web3: web3.default) {
+    if(typeof sub === 'string')
+      return web3.utils.padLeft(web3.utils.toHex(web3.utils.toBN(sub)), 64);
+    else if(sub instanceof Uint8Array)
+      return web3.utils.padLeft(web3.utils.bytesToHex(Array.from(sub)), 64);
+    throw Error('Infallible');
+  }
+
+  itEth('Put + Buy [eth]', async ({helper}) => {
+    const ONE_TOKEN = helper.balance.getOneTokenNominal();
+    const PRICE = 2n * ONE_TOKEN;  // 2 UNQ
+    const marketOwner = await helper.eth.createAccountWithBalance(donor, 60000n);
+    const market = await deployMarket(helper, marketOwner);
+    const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner);
+
+    // Set external sponsoring
+    await contractHelpers.methods.setSponsor(market.options.address, marketOwner).send({from: marketOwner});
+    await contractHelpers.methods.confirmSponsorship(market.options.address).send({from: marketOwner});
+
+    // Configure sponsoring
+    await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner});
+    await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner});
+
+    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC');
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true);
+
+    // Set collection sponsoring
+    await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner});
+    await collection.methods.confirmCollectionSponsorship().send({from: marketOwner});
+
+    const sellerCross = helper.ethCrossAccount.createAccount();
+    const result = await collection.methods.mintCross(sellerCross, []).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+    await collection.methods.approve(market.options.address, tokenId).send({from: sellerCross.eth});
+
+    // Seller has no funds at all, his transactions are sponsored
+    const sellerBalance = await helper.balance.getEthereum(sellerCross.eth);
+    expect(sellerBalance).to.be.eq(0n);
+
+    const putResult = await market.methods.put(collectionId, tokenId, PRICE.toString(), 1, sellerCross).send({
+      from: sellerCross.eth, gasLimit: 1_000_000,
+    });
+    expect(putResult.events.TokenIsUpForSale).is.not.undefined;
+
+    // Seller balance are still 0
+    const sellerBalanceAfter = await helper.balance.getEthereum(sellerCross.eth);
+    expect(sellerBalanceAfter).to.be.eq(0n);
+
+    let ownerCross = await collection.methods.ownerOfCross(tokenId).call();
+    expect(ownerCross.eth).to.be.eq(sellerCross.eth);
+    expect(ownerCross.sub).to.be.eq(sellerCross.sub);
+
+    const buyerCross = await helper.ethCrossAccount.createAccountWithBalance(donor, 10n);
+
+    // Buyer has only 10 UNQ
+    const buyerBalance = await helper.balance.getEthereum(buyerCross.eth);
+    expect(buyerBalance).to.be.eq(10n * ONE_TOKEN);
+
+    const buyResult = await market.methods.buy(collectionId, tokenId, 1, buyerCross).send({from: buyerCross.eth, value: PRICE.toString(), gasLimit: 1_000_000});
+    expect(buyResult.events.TokenIsPurchased).is.not.undefined;
+
+    // Buyer pays only value, transaction use sponsoring
+    const buyerBalanceAfter = await helper.balance.getEthereum(buyerCross.eth);
+    expect(buyerBalanceAfter).to.be.eq(10n * ONE_TOKEN - PRICE);
+
+    ownerCross = await collection.methods.ownerOfCross(tokenId).call();
+    expect(ownerCross.eth).to.be.eq(buyerCross.eth);
+    expect(ownerCross.sub).to.be.eq(buyerCross.sub);
+  });
+
+  itEth('Put + Buy [sub]', async ({helper}) => {
+    const ONE_TOKEN = helper.balance.getOneTokenNominal();
+    const PRICE = 2n * ONE_TOKEN;  // 2 UNQ
+    const web3 = helper.getWeb3();
+    const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n);
+    const market = await deployMarket(helper, marketOwner);
+    const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner);
+
+    // Set self sponsoring from contract balance
+    await contractHelpers.methods.selfSponsoredEnable(market.options.address).send({from: marketOwner});
+    await helper.eth.transferBalanceFromSubstrate(donor, market.options.address, 10n);
+
+    // Configure sponsoring
+    await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner});
+    await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner});
+
+    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC');
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true);
+
+    // Set collection sponsoring
+    await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner});
+    await collection.methods.confirmCollectionSponsorship().send({from: marketOwner});
+
+    const seller = helper.util.fromSeed(`//Market-seller-${(new Date()).getTime()}`);
+    const sellerCross = helper.ethCrossAccount.fromKeyringPair(seller);
+
+    // Seller has no funds at all, his transactions are sponsored
+    {
+      const sellerBalance = await helper.balance.getSubstrate(seller.address);
+      expect(sellerBalance).to.be.eq(0n);
+    }
+
+    const result = await collection.methods.mintCross(sellerCross, []).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+    await helper.nft.approveToken(seller, collectionId, tokenId, {Ethereum: market.options.address});
+
+    await helper.eth.sendEVM(seller, market.options.address, market.methods.put(collectionId, tokenId, PRICE, 1, sellerCross).encodeABI(), '0');
+    // Seller balance is still zero
+    {
+      const sellerBalance = await helper.balance.getSubstrate(seller.address);
+      expect(sellerBalance).to.be.eq(0n);
+    }
+    let ownerCross = await collection.methods.ownerOfCross(tokenId).call();
+    expect(ownerCross.eth).to.be.eq(sellerCross.eth);
+    expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(sellerCross.sub, web3));
+
+    const [buyer] = await helper.arrange.createAccounts([600n], donor);
+    // Buyer has only expected balance
+    {
+      const buyerBalance = await helper.balance.getSubstrate(buyer.address);
+      expect(buyerBalance).to.be.eq(600n * ONE_TOKEN);
+    }
+    const buyerCross = helper.ethCrossAccount.fromKeyringPair(buyer);
+
+    const buyerBalanceBefore = await helper.balance.getSubstrate(buyer.address);
+    await helper.eth.sendEVM(buyer, market.options.address, market.methods.buy(collectionId, tokenId, 1, buyerCross).encodeABI(), PRICE.toString());
+    const buyerBalanceAfter = await helper.balance.getSubstrate(buyer.address);
+    // Buyer balance not changed: transaction is sponsored
+    expect(buyerBalanceBefore).to.be.eq(buyerBalanceAfter + PRICE);
+
+    const sellerBalanceAfterBuy = BigInt(await helper.balance.getSubstrate(seller.address));
+    ownerCross = await collection.methods.ownerOfCross(tokenId).call();
+    expect(ownerCross.eth).to.be.eq(buyerCross.eth);
+    expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(buyerCross.sub, web3));
+
+    // Seller got only PRICE - MARKET_FEE
+    expect(sellerBalanceAfterBuy).to.be.eq(PRICE * BigInt(100 - MARKET_FEE) / 100n);
+  });
+});
addedjs-packages/tests/eth/marketplace-v2/royalty/LibPart.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace-v2/royalty/LibPart.sol
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity ^0.8.0;
+
+import "./UniqueRoyalty.sol";
+
+library LibPart {
+    bytes32 public constant TYPE_HASH = keccak256("Part(address account,uint96 value)");
+
+    struct Part {
+        address payable account;
+        uint96 value;
+    }
+
+    function hash(Part memory part) internal pure returns (bytes32) {
+        return keccak256(abi.encode(TYPE_HASH, part.account, part.value));
+    }
+}
+
+library LibPartAdapter {
+    function encode(LibPart.Part[] memory parts) internal pure returns (bytes memory) {
+        if (parts.length == 0) return "";
+
+        uint256[] memory encoded = new uint256[](parts.length * 2);
+
+        for (uint i = 0; i < parts.length; i++) {
+            encoded[i * 2] = 0x0100000000000000000000000000000000000000000000040000000000000000 | uint256(parts[i].value);
+            encoded[i * 2 + 1] = uint256(uint160(address(parts[i].account)));
+        }
+
+        return abi.encodePacked(encoded);
+    }
+
+    function decode(bytes memory b) internal pure returns (LibPart.Part[] memory) {
+        if (b.length == 0) return new LibPart.Part[](0);
+
+        require((b.length % (32 * 2)) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count");
+        uint partsCount = b.length / (32 * 2);
+        uint numbersCount = partsCount * 2;
+
+        LibPart.Part[] memory parts = new LibPart.Part[](partsCount);
+
+        // need this because numbers encoded via abi.encodePacked
+        bytes memory prefix = new bytes(64);
+
+        assembly {
+            mstore(add(prefix, 32), 32)
+            mstore(add(prefix, 64), numbersCount)
+        }
+
+        uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[]));
+
+        for (uint i = 0; i < partsCount; i++) {
+            uint96 value = uint96(encoded[i * 2] & 0xFFFFFFFFFFFFFFFF);
+            address account = address(uint160(encoded[i * 2 + 1]));
+
+            parts[i] = LibPart.Part({
+                account: payable(account),
+                value: value
+            });
+        }
+
+        return parts;
+    }
+}
+
+library LibPartAdapterComplex {
+    function decodeSafe(bytes memory data) internal pure returns (LibPart.Part[] memory) {
+        return fromUniqueRoyalties(UniqueRoyalty.decode(data));
+    }
+
+    function encodeSafe(LibPart.Part[] memory parts) internal pure returns (bytes memory) {
+        return UniqueRoyalty.encode(toUniqueRoyalties(parts));
+    }
+
+    function fromUniqueRoyalties(UniqueRoyaltyPart[] memory royalties) internal pure returns (LibPart.Part[] memory) {
+        LibPart.Part[] memory parts = new LibPart.Part[](royalties.length);
+
+        for (uint i = 0; i < royalties.length; i++) {
+            uint96 value = royalties[i].decimals >= 4
+            ? uint96(royalties[i].value * (10 ** (royalties[i].decimals - 4)))
+            : uint96(royalties[i].value / (10 ** (4 - royalties[i].decimals)));
+
+            parts[i] = LibPart.Part({
+                account: payable(CrossAddressLib.toAddress(royalties[i].crossAddress)),
+                value: value
+            });
+        }
+
+        return parts;
+    }
+
+    function toUniqueRoyalties(LibPart.Part[] memory parts) internal pure returns (UniqueRoyaltyPart[] memory) {
+        UniqueRoyaltyPart[] memory royalties = new UniqueRoyaltyPart[](parts.length);
+
+        for (uint i = 0; i < parts.length; i++) {
+            royalties[i] = UniqueRoyaltyPart({
+                version: 1,
+                value: uint64(parts[i].value),
+                decimals: 4,
+                crossAddress: CrossAddress({
+                    sub: 0,
+                    eth: parts[i].account
+                }),
+                isPrimarySaleOnly: false
+            });
+        }
+
+        return royalties;
+    }
+}
\ No newline at end of file
addedjs-packages/tests/eth/marketplace-v2/royalty/UniqueRoyalty.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace-v2/royalty/UniqueRoyalty.sol
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity >=0.8.17;
+
+import { CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol";
+
+struct UniqueRoyaltyPart {
+    uint8 version;
+    uint8 decimals;
+    uint64 value;
+    bool isPrimarySaleOnly;
+    CrossAddress crossAddress;
+}
+
+library CrossAddressLib {
+    function toAddress(CrossAddress memory crossAddress) internal pure returns (address) {
+        return crossAddress.eth != address(0) ? crossAddress.eth : address(uint160(crossAddress.sub >> 96));
+    }
+}
+
+library UniqueRoyalty {
+    uint private constant DECIMALS_OFFSET = 4 * 16;
+    uint private constant ADDRESS_TYPE_OFFSET = 4 * (16 + 2);           // 0 - eth, 1 - sub
+    uint private constant ROYALTY_TYPE_OFFSET = 4 * (16 + 2 + 1);       // 0 - default, 1 - primary sale only
+    uint private constant VERSION_OFFSET = 4 * (16 + 2 + 1 + 1 + 42);
+
+    uint private constant PART_LENGTH = 32 * 2;
+
+    function decode(bytes memory b) internal pure returns (UniqueRoyaltyPart[] memory) {
+        if (b.length == 0) return new UniqueRoyaltyPart[](0);
+
+        require((b.length % PART_LENGTH) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count");
+        uint partsCount = b.length / PART_LENGTH;
+        uint numbersCount = partsCount * 2;
+
+        UniqueRoyaltyPart[] memory parts = new UniqueRoyaltyPart[](partsCount);
+
+        // need this because numbers encoded via abi.encodePacked
+        bytes memory prefix = new bytes(64);
+
+        assembly {
+            mstore(add(prefix, 32), 32)
+            mstore(add(prefix, 64), numbersCount)
+        }
+
+        uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[]));
+
+        for (uint i = 0; i < partsCount; i++) {
+            parts[i] = decodePart(encoded[i * 2], encoded[i * 2 + 1]);
+        }
+
+        return parts;
+    }
+
+    function encode(UniqueRoyaltyPart[] memory parts) internal pure returns (bytes memory) {
+        if (parts.length == 0) return "";
+
+        uint256[] memory encoded = new uint256[](parts.length * 2);
+
+        for (uint i = 0; i < parts.length; i++) {
+            (uint256 encodedMeta, uint256 encodedAddress) = encodePart(parts[i]);
+
+            encoded[i * 2] = encodedMeta;
+            encoded[i * 2 + 1] = encodedAddress;
+        }
+
+        return abi.encodePacked(encoded);
+    }
+
+    function decodePart(bytes memory b) internal pure returns (UniqueRoyaltyPart memory) {
+        require(b.length == PART_LENGTH, "Invalid bytes length, expected 32 * 2");
+
+        uint256[2] memory encoded = abi.decode(b, (uint256[2]));
+
+        return decodePart(encoded[0], encoded[1]);
+    }
+
+    function decodePart(
+        uint256 _meta,
+        uint256 _address
+    ) internal pure returns (UniqueRoyaltyPart memory) {
+        uint256 version = _meta >> VERSION_OFFSET;
+        bool isPrimarySaleOnly = (_meta & (1 << ROYALTY_TYPE_OFFSET)) > 0;
+        bool isEthereumAddress = (_meta & (1 << ADDRESS_TYPE_OFFSET)) == 0;
+        uint256 decimals = (_meta >> 4 * 16) & 0xFF;
+        uint256 value = _meta & 0xFFFFFFFFFFFFFFFF;
+
+        CrossAddress memory crossAddress = isEthereumAddress
+            ? CrossAddress({ sub: 0, eth: address(uint160(_address)) })
+            : CrossAddress({ sub: _address, eth: address(0) });
+
+        UniqueRoyaltyPart memory royaltyPart = UniqueRoyaltyPart({
+            version: uint8(version),
+            decimals: uint8(decimals),
+            value: uint64(value),
+            isPrimarySaleOnly: isPrimarySaleOnly,
+            crossAddress: crossAddress
+        });
+
+        return royaltyPart;
+    }
+
+    function encodePart(UniqueRoyaltyPart memory royaltyPart) internal pure returns (uint256, uint256) {
+        uint256 encodedMeta = 0;
+        uint256 encodedAddress = 0;
+
+        encodedMeta |= uint256(royaltyPart.version) << VERSION_OFFSET;
+        if (royaltyPart.isPrimarySaleOnly) encodedMeta |= 1 << ROYALTY_TYPE_OFFSET;
+
+        if (royaltyPart.crossAddress.eth == address(0x0)) {
+            encodedMeta |= 1 << ADDRESS_TYPE_OFFSET;
+            encodedAddress = royaltyPart.crossAddress.sub;
+        } else {
+            encodedAddress = uint256(uint160(royaltyPart.crossAddress.eth));
+        }
+
+        encodedMeta |= uint256(royaltyPart.decimals) << DECIMALS_OFFSET;
+        encodedMeta |= uint256(royaltyPart.value);
+
+        return (encodedMeta, encodedAddress);
+    }
+}
\ No newline at end of file
addedjs-packages/tests/eth/marketplace-v2/royalty/UniqueRoyaltyHelper.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace-v2/royalty/UniqueRoyaltyHelper.sol
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity >=0.8.17;
+
+import "./UniqueRoyalty.sol";
+import "./LibPart.sol";
+
+string constant ROYALTIES_PROPERTY = "royalties";
+
+interface ICollection {
+    function collectionProperty(string memory key) external view returns (bytes memory);
+    function property(uint256 tokenId, string memory key) external view returns (bytes memory);
+}
+
+struct RoyaltyAmount {
+    CrossAddress crossAddress;
+    uint amount;
+}
+
+library UniqueRoyaltyHelper {
+    function encodePart(UniqueRoyaltyPart memory part) internal pure returns (bytes memory) {
+        (uint256 encodedMeta, uint256 encodedAddress) = UniqueRoyalty.encodePart(part);
+
+        return abi.encodePacked(encodedMeta, encodedAddress);
+    }
+
+    function decodePart(bytes memory data) internal pure returns (UniqueRoyaltyPart memory) {
+        return UniqueRoyalty.decodePart(data);
+    }
+
+    // todo - implement smth better - check royalties sum is lte 100%
+    function validatePart(bytes memory b) internal pure returns (bool isValid) {
+        isValid = b.length == 64;
+    }
+
+    function encode(UniqueRoyaltyPart[] memory royalties) internal pure returns (bytes memory) {
+        return UniqueRoyalty.encode(royalties);
+    }
+
+    function decode(bytes memory data) internal pure returns (UniqueRoyaltyPart[] memory) {
+        return UniqueRoyalty.decode(data);
+    }
+
+    // todo - implement smth better - check royalties sum is lte 100%
+    function validate(bytes memory b) internal pure returns (bool) {
+        return b.length % 64 == 0;
+    }
+
+    function getTokenRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory) {
+        try ICollection(collection).property(tokenId, ROYALTIES_PROPERTY) returns (bytes memory encoded) {
+            return UniqueRoyalty.decode(encoded);
+        } catch {
+            return new UniqueRoyaltyPart[](0);
+        }
+    }
+
+    function getCollectionRoyalty(address collection) internal view returns (UniqueRoyaltyPart[] memory) {
+        try ICollection(collection).collectionProperty(ROYALTIES_PROPERTY) returns (bytes memory encoded) {
+            return UniqueRoyalty.decode(encoded);
+        } catch {
+            return new UniqueRoyaltyPart[](0);
+        }
+    }
+
+    function getRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory royalty) {
+        royalty = getTokenRoyalty(collection, tokenId);
+
+        if (royalty.length == 0) {
+            royalty = getCollectionRoyalty(collection);
+        }
+    }
+
+    function calculateRoyalties(UniqueRoyaltyPart[] memory royalties, bool isPrimarySale, uint sellPrice) internal pure returns (RoyaltyAmount[] memory) {
+        RoyaltyAmount[] memory royaltyAmounts = new RoyaltyAmount[](royalties.length);
+        uint amountsCount = 0;
+
+        for (uint i = 0; i < royalties.length; i++) {
+            if (isPrimarySale == royalties[i].isPrimarySaleOnly) {
+                uint amount = (sellPrice * royalties[i].value) / (10 ** (royalties[i].decimals));
+
+                royaltyAmounts[amountsCount] = RoyaltyAmount({
+                    crossAddress: royalties[i].crossAddress,
+                    amount: amount
+                });
+
+                amountsCount += 1;
+            }
+        }
+
+        // shrink royaltyAmounts to amountsCount length
+        assembly { mstore(royaltyAmounts, amountsCount) }
+
+        return royaltyAmounts;
+    }
+
+    function calculateForPrimarySale(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) {
+        UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId);
+
+        return calculateRoyalties(royalties, true, sellPrice);
+    }
+
+    function calculate(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) {
+        UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId);
+
+        return calculateRoyalties(royalties, false, sellPrice);
+    }
+}
\ No newline at end of file
addedjs-packages/tests/eth/marketplace-v2/utils.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace-v2/utils.sol
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity ^0.8.17;
+
+contract Utils {
+    function toString(address account) public pure returns (string memory) {
+        return toString(abi.encodePacked(account));
+    }
+
+    function toString(bool value) public pure returns (string memory) {
+        return value ? "true" : "false";
+    }
+
+    function toString(uint256 value) public pure returns (string memory) {
+        return toString(abi.encodePacked(value));
+    }
+
+    function toString(bytes32 value) public pure returns (string memory) {
+        return toString(abi.encodePacked(value));
+    }
+
+    function toString(bytes memory data) public pure returns (string memory) {
+        bytes memory alphabet = "0123456789abcdef";
+
+        bytes memory str = new bytes(2 + data.length * 2);
+        str[0] = "0";
+        str[1] = "x";
+        for (uint i = 0; i < data.length; i++) {
+            str[2 + i * 2] = alphabet[uint(uint8(data[i] >> 4))];
+            str[3 + i * 2] = alphabet[uint(uint8(data[i] & 0x0f))];
+        }
+        return string(str);
+    }
+}
\ No newline at end of file
addedjs-packages/tests/eth/marketplace/MarketPlace.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace/MarketPlace.sol
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier:  Apache License
+pragma solidity >=0.8.0;
+import {UniqueNFT, Dummy, ERC165} from "../api/UniqueNFT.sol";
+
+// Inline
+interface ERC20Events {
+    event Transfer(address indexed from, address indexed to, uint256 value);
+    event Approval(
+        address indexed owner,
+        address indexed spender,
+        uint256 value
+    );
+}
+
+interface ERC20 is Dummy, ERC165, ERC20Events {
+    // Selector: name() 06fdde03
+    function name() external view returns (string memory);
+
+    // Selector: symbol() 95d89b41
+    function symbol() external view returns (string memory);
+
+    // Selector: totalSupply() 18160ddd
+    function totalSupply() external view returns (uint256);
+
+    // Selector: decimals() 313ce567
+    function decimals() external view returns (uint8);
+
+    // Selector: balanceOf(address) 70a08231
+    function balanceOf(address owner) external view returns (uint256);
+
+    // Selector: transfer(address,uint256) a9059cbb
+    function transfer(address to, uint256 amount) external returns (bool);
+
+    // Selector: transferFrom(address,address,uint256) 23b872dd
+    function transferFrom(
+        address from,
+        address to,
+        uint256 amount
+    ) external returns (bool);
+
+    // Selector: approve(address,uint256) 095ea7b3
+    function approve(address spender, uint256 amount) external returns (bool);
+
+    // Selector: allowance(address,address) dd62ed3e
+    function allowance(address owner, address spender)
+        external
+        view
+        returns (uint256);
+}
+
+interface UniqueFungible is Dummy, ERC165, ERC20 {}
+
+contract MarketPlace {
+    struct Order {
+        
+        uint256 idNFT;
+        address currencyCode; // UNIQ tokens as address address (1); wKSM 
+        uint256 price;
+        uint256 time;
+        address idCollection;
+        address ownerAddr;
+        uint8 flagActive;
+        string name;
+        string symbol;
+        string tokenURI;        
+    }
+    Order[] public  orders;
+    uint test;
+    mapping (address => uint256) public balanceKSM;  //  [ownerAddr][currency] => [KSMs]
+    mapping (address => mapping (uint256 => uint256)) public  asks ; // [buyer][idCollection][idNFT] => idorder
+    mapping (address => mapping (uint => uint[])) public ordersbyNFT; // [addressCollection] =>idNFT =>idorder
+
+    mapping (address => uint[]) public asksbySeller; // [addressSeller] =>idorder
+
+    mapping (address =>bool) internal isEscrow;
+
+    //address escrow;
+    address owner;
+    address nativecoin;
+
+    // from abstract contract ReentrancyGuard 
+    // Booleans are more expensive than uint256 or any type that takes up a full
+    // word because each write operation emits an extra SLOAD to first read the
+    // slot's contents, replace the bits taken up by the boolean, and then write
+    // back. This is the compiler's defense against contract upgrades and
+    // pointer aliasing, and it cannot be disabled.
+
+    // The values being non-zero value makes deployment a bit more expensive,
+    // but in exchange the refund on every call to nonReentrant will be lower in
+    // amount. Since refunds are capped to a percentage of the total
+    // transaction's gas, it is best to keep them low in cases like this one, to
+    // increase the likelihood of the full refund coming into effect.
+    uint8 private constant _NOT_ENTERED = 1;
+    uint8 private constant _ENTERED = 2;
+
+    uint8 private _status;
+
+    struct NFT {
+        address  collection;
+        uint256 id;
+    }
+    
+     //function initialize() public initializer {
+   constructor () { // call setEscrow directly
+        owner = msg.sender;
+
+         orders.push(Order(        
+                    0,
+                    address(0),
+                    0,
+                    0,
+                    address(0),
+                    address(0),
+                    0, "","",""));
+         _status = _NOT_ENTERED;
+
+    }
+
+    modifier nonReentrant() { // from abstract contract ReentrancyGuard 
+        // On the first call to nonReentrant, _notEntered will be true
+        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
+
+        // Any calls to nonReentrant after this point will fail
+        _status = _ENTERED;
+
+        _;
+
+        // By storing the original value once again, a refund is triggered (see
+        // https://eips.ethereum.org/EIPS/eip-2200)
+        _status = _NOT_ENTERED;
+    }
+
+    modifier onlyEscrow () {
+        require(isEscrow [msg.sender] , "Only escrow can");
+        _;
+    }
+
+    modifier onlyOwner () {
+        require(msg.sender == owner, "Only owner can");
+        _;
+    }
+
+    /**
+    * Make bids (orders) to sell NFTs 
+    */
+
+     
+    receive  () external payable  {
+     //   revert ("Can't accept payment without collection and IDs, use dApp to send");
+    }
+    fallback () external payable {
+        revert ("No such function");
+    } 
+
+    event AddedAsk (uint256 _price, 
+                    address  _currencyCode, 
+                    address _idCollection, 
+                    uint256 _idNFT,
+                    uint256 orderId
+                  );
+    event EditedAsk (uint256 _price, 
+                    address  _currencyCode, 
+                    address _idCollection, 
+                    uint256 _idNFT,
+                    uint8  _active,
+                    uint orderId);
+
+    event  CanceledAsk (address _idCollection, 
+                        uint256 _idNFT, 
+                        uint orderId
+                        );
+
+    event DepositedKSM (uint256 _amount,  address _sender);
+
+    event BoughtNFT4KSM (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice );
+
+    event BoughtNFT (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice );
+
+    event WithdrawnAllKSM (address _sender, uint256 balance); 
+
+    event WithdrawnKSM (address _sender, uint256 balance); 
+
+    event Withdrawn (uint256 _amount, address _currencyCode, address _sender);
+    
+
+   function setOwner  (address _newOwner) public onlyOwner {
+        owner = _newOwner;
+    }
+
+    function setEscrow  (address _escrow, bool _state) public onlyOwner returns (bool) {
+        if (isEscrow[_escrow] != _state)  {
+            isEscrow[_escrow] = _state;
+            return true;
+        }
+        return false;
+    }
+
+    function setNativeCoin  (address _coin) public onlyOwner {
+        nativecoin = _coin;
+    }
+
+
+    function addAsk (uint256 _price, 
+                    address  _currencyCode, 
+                    address _idCollection, 
+                    uint256 _idNFT
+                  ) public  { //
+        address ownerNFT = UniqueNFT(_idCollection).ownerOf(_idNFT);
+        require (ownerNFT == msg.sender, "Only token owner can make ask");
+        string memory nameNFT;
+        string memory symbolNFT;
+        string memory uriNFT;
+        try UniqueNFT(_idCollection).name() returns (string memory name_) {
+            nameNFT = name_;
+        }
+        catch {
+            nameNFT="";
+        }   
+        try UniqueNFT(_idCollection).symbol() returns (string memory symbol_) {
+            symbolNFT = symbol_;
+        }
+        catch {
+            symbolNFT="";
+        }  
+        try UniqueNFT(_idCollection).tokenURI(_idNFT) returns (string memory uri_) {
+            uriNFT = uri_;
+        }
+        catch {
+            uriNFT="";
+        }        
+        orders.push(Order(        
+                    _idNFT,
+                    _currencyCode,
+                    _price,
+                    block.timestamp,
+                    _idCollection,
+                    msg.sender,
+                    1, // 1 = is active
+                    nameNFT, 
+                    symbolNFT,
+                    uriNFT
+             ));
+            
+            uint orderId = orders.length-1;
+            asks[_idCollection][_idNFT] = orderId;
+            asksbySeller[msg.sender].push(orderId);
+            UniqueNFT(_idCollection).transferFrom(msg.sender, address(this), _idNFT);
+            emit AddedAsk(_price, _currencyCode, _idCollection, _idNFT, orderId);     
+    }
+
+    function editAsk (uint256 _price, 
+                    address  _currencyCode, 
+                    address _idCollection, 
+                    uint256 _idNFT,
+                    uint8  _active) public {
+        
+
+        uint orderID =  asks[_idCollection][_idNFT];
+
+        require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask");
+        require (orders[orderID].flagActive != 0, "This ask is closed");
+        if (_price> 0 ) {
+            orders[orderID].price = _price ;  
+        }
+        
+        if (_currencyCode != address(0) ) {
+            orders[orderID].currencyCode = _currencyCode ;  
+        }
+
+        orders[orderID].time = block.timestamp;
+        orders[orderID].flagActive = _active;
+        
+        emit EditedAsk(_price, _currencyCode, _idCollection, _idNFT, _active, orderID);
+        }
+        
+
+    function cancelAsk (address _idCollection, 
+                        uint256 _idNFT
+                        ) public {
+
+        uint orderID =  asks[_idCollection][_idNFT];
+
+        require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask");
+        require (orders[orderID].flagActive != 0, "This ask is closed");
+
+        orders[orderID].time = block.timestamp;
+        orders[orderID].flagActive = 0;
+        UniqueNFT(_idCollection).transferFrom(address(this),orders[orderID].ownerAddr, _idNFT);
+        emit CanceledAsk(_idCollection, _idNFT, orderID);
+        }
+
+
+    function depositKSM (uint256 _amount,  address _sender) public onlyEscrow {
+        balanceKSM[_sender] = balanceKSM[_sender] + _amount;
+        emit DepositedKSM(_amount, _sender);
+    }
+
+    function buyKSM (address _idCollection, uint256 _idNFT, address _buyer, address _receiver ) public {
+        
+        Order memory order = orders[ asks[_idCollection][_idNFT]];
+        require(isEscrow[msg.sender]  || msg.sender == _buyer, "Only escrow or buyer can call buyKSM" );
+        //1. reduce balance
+
+        balanceKSM[_buyer] = balanceKSM[_buyer] - order.price;
+        balanceKSM[order.ownerAddr] = balanceKSM[order.ownerAddr] + order.price;
+        // 2. close order
+        orders[ asks[_idCollection][_idNFT]].flagActive = 0;
+        // 3. transfer NFT to buyer
+        UniqueNFT(_idCollection).transferFrom(address(this), _receiver, _idNFT);
+        emit BoughtNFT4KSM(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price);
+
+    }
+    function buy (address _idCollection, uint256 _idNFT ) public payable returns (bool result) { //buing for UNQ like as ethers 
+        
+        Order memory order = orders[asks[_idCollection][_idNFT]]; 
+        //1. check sent amount and send to seller
+        require (msg.value == order.price, "Not right amount sent, have to be equal price" );     
+        // 2. close order
+        orders[ asks[_idCollection][_idNFT]].flagActive = 0;
+        
+        // 3. transfer NFT to buyer
+        UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
+        //uint balance  = address(this).balance;
+        result = payable(order.ownerAddr).send (order.price); 
+        emit BoughtNFT(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price);
+        
+    }
+
+/* 
+    function buyOther (address _idCollection, uint256 _idNFT, address _currencyCode, uint _amount ) public  { //buy for sny token if seller wants
+        
+        Order memory order = orders[ asks[_idCollection][_idNFT]];
+        //1. check sent amount and transfer from buyer to seller
+        require (order.price == _amount && order.currencyCode == _currencyCode, "Not right amount or currency sent, have to be equal currency and price" );
+        // !!! transfer have to be approved to marketplace!
+        UniqueFungible(order.currencyCode).transferFrom(msg.sender, address(this), order.price); //to not disclojure buyer's address 
+        UniqueFungible(order.currencyCode).transfer(order.ownerAddr, order.price);
+        // 2. close order
+        orders[ asks[_idCollection][_idNFT]].flagActive = 0;
+        // 3. transfer NFT to buyer
+        UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
+
+
+    }
+ */
+
+    function withdrawAllKSM (address _sender) public  nonReentrant returns (uint lastBalance ){
+        require(isEscrow[msg.sender]  || msg.sender == _sender, "Only escrow or balance owner can withdraw all KSM" );
+
+        lastBalance = balanceKSM[_sender];
+        balanceKSM[_sender] =0;
+        emit WithdrawnAllKSM(_sender, lastBalance);
+    }
+
+    function withdrawKSM (uint _amount, address _sender) onlyEscrow public {
+        balanceKSM[_sender] = balanceKSM[_sender] - _amount;
+        emit WithdrawnKSM(_sender, balanceKSM[_sender]);
+        
+    }
+
+    function withdraw (uint256 _amount, address _currencyCode) public  nonReentrant returns (bool result ){ //onlyOwner
+        address payable _sender = payable( msg.sender);
+        if (_currencyCode != nativecoin ) { //erc20 compat. tokens on UNIQUE chain
+            // uint balance = UniqueFungible(_currencyCode).balanceOf(address(this));
+            UniqueFungible(_currencyCode).transfer(_sender, _amount);
+        } else {
+            // uint balance  = address(this).balance;
+
+            result =  (_sender).send(_amount); // for UNQ like as ethers 
+        }
+        emit Withdrawn(_amount, _currencyCode, _sender);
+        return result;
+
+    }
+
+    
+   // event GettedOrder(uint , Order);
+    function getOrder (address _idCollection, uint256 _idNFT) public view returns (Order memory) {
+        uint orderId = asks[_idCollection][_idNFT];
+        Order memory order = orders[orderId];
+ //       emit GettedOrder (orderId, order);
+        return order;
+    }
+
+    function getOrdersLen () public view returns (uint) {
+        return orders.length;
+    }
+
+    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public pure returns(bytes4) {
+            return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
+        }
+     
+    //TODO make destructing function to return all 
+    function terminate(address[] calldata tokens, NFT[] calldata nfts) public onlyOwner {
+    // Transfer tokens to owner (TODO: error handling)
+        for (uint i = 0; i < tokens.length; i++) {
+            address addr = tokens[i];
+            UniqueFungible token = UniqueFungible(addr);
+            uint256 balance = token.balanceOf(address(this));
+            token.transfer(owner, balance);
+        }
+        for (uint i = 0; i < nfts.length; i++) {
+            address addr = nfts[i].collection;
+            UniqueNFT token = UniqueNFT(addr);
+            token.transferFrom(address(this), owner, nfts[i].id);
+        }    
+    // Transfer Eth to owner and terminate contract
+        address payable owner1 = payable (owner);
+        uint balance = address(this).balance;
+        owner1.transfer(balance);
+        
+    } 
+    
+}
addedjs-packages/tests/eth/marketplace/marketplace.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/marketplace/marketplace.test.ts
@@ -0,0 +1,208 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+const {dirname} = makeNames(import.meta.url);
+
+describe('Matcher contract usage', () => {
+  const PRICE = 2000n;
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let aliceMirror: string;
+  let aliceDoubleMirror: string;
+  let seller: IKeyringPair;
+  let sellerMirror: string;
+
+  before(async () => {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  beforeEach(async () => {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      [alice] = await helper.arrange.createAccounts([1000n], donor);
+      aliceMirror = helper.address.substrateToEth(alice.address).toLowerCase();
+      aliceDoubleMirror = helper.address.ethToSubstrate(aliceMirror);
+      seller = await privateKey(`//Seller/${Date.now()}`);
+      sellerMirror = helper.address.substrateToEth(seller.address).toLowerCase();
+
+      await helper.balance.transferToSubstrate(donor, aliceDoubleMirror, 10_000_000_000_000_000_000n);
+    });
+  });
+
+  itEth('With UNQ', async ({helper}) => {
+    const matcherOwner = await helper.eth.createAccountWithBalance(donor);
+    const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2);
+
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner);
+    await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner});
+    await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});
+
+    await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner});
+    await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor});
+
+    const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}});
+    await collection.confirmSponsorship(alice);
+    await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror});
+    const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
+    await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror);
+
+    await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner});
+    await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner});
+
+    const token = await collection.mintToken(alice, {Ethereum: sellerMirror});
+
+    // Token is owned by seller initially
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror});
+
+    // Ask
+    {
+      await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0');
+      await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0');
+    }
+
+    // Token is transferred to matcher
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});
+
+    // Buy
+    {
+      const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address);
+      await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString());
+      expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE);
+    }
+
+    // Token is transferred to evm account of alice
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror});
+
+    // Transfer token to substrate side of alice
+    await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address});
+
+    // Token is transferred to substrate account of alice, seller received funds
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itEth('With escrow', async ({helper}) => {
+    const matcherOwner = await helper.eth.createAccountWithBalance(donor);
+    const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2);
+
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const escrow = await helper.eth.createAccountWithBalance(donor);
+    await matcher.methods.setEscrow(escrow, true).send({from: matcherOwner});
+    const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner);
+    await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner});
+    await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});
+
+    await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner});
+    await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor});
+
+    const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}});
+    await collection.confirmSponsorship(alice);
+    await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror});
+    const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
+    await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror);
+
+
+    await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner});
+
+    await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner});
+
+    const token = await collection.mintToken(alice, {Ethereum: sellerMirror});
+
+    // Token is owned by seller initially
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror});
+
+    // Ask
+    {
+      await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0');
+      await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0');
+    }
+
+    // Token is transferred to matcher
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});
+
+    // Give buyer KSM
+    await matcher.methods.depositKSM(PRICE, aliceMirror).send({from: escrow});
+
+    // Buy
+    {
+      expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal('0');
+      expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal(PRICE.toString());
+
+      await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buyKSM(evmCollection.options.address, token.tokenId, aliceMirror, aliceMirror).encodeABI(), '0');
+
+      // Price is removed from buyer balance, and added to seller
+      expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal('0');
+      expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal(PRICE.toString());
+    }
+
+    // Token is transferred to evm account of alice
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror});
+
+    // Transfer token to substrate side of alice
+    await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address});
+
+    // Token is transferred to substrate account of alice, seller received funds
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itEth('Sell tokens from substrate user via EVM contract', async ({helper}) => {
+    const matcherOwner = await helper.eth.createAccountWithBalance(donor);
+    const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2);
+
+    await helper.eth.transferBalanceFromSubstrate(donor, matcher.options.address);
+
+    const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}});
+    const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
+
+    await helper.balance.transferToSubstrate(donor, seller.address, 100_000_000_000_000_000_000n);
+
+    const token = await collection.mintToken(alice, {Ethereum: sellerMirror});
+
+    // Token is owned by seller initially
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror});
+
+    // Ask
+    {
+      await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0');
+      await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0');
+    }
+
+    // Token is transferred to matcher
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});
+
+    // Buy
+    {
+      const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address);
+      await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString());
+      expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE);
+    }
+
+    // Token is transferred to evm account of alice
+    expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror});
+
+    // Transfer token to substrate side of alice
+    await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address});
+
+    // Token is transferred to substrate account of alice, seller received funds
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+});
addedjs-packages/tests/eth/migration.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/migration.seqtest.ts
@@ -0,0 +1,209 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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, itEth, usingEthPlaygrounds} from './util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {Struct} from '@polkadot/types';
+
+import type {IEvent} from '@unique/playgrounds/types.js';
+import type {InterfaceTypes} from '@polkadot/types/types/registry';
+import {ApiPromise} from '@polkadot/api';
+
+const encodeEvent = (api: ApiPromise, pallet: string, palletEvents: string, event: string, fields: any) => {
+  const palletIndex = api.runtimeMetadata.asV14.pallets.find(p => p.name.toString() == pallet)!.index.toNumber();
+  const eventMeta = api.events[palletEvents][event].meta;
+  const eventIndex = eventMeta.index.toNumber();
+  const data = [
+    palletIndex, eventIndex,
+  ];
+  const metaEvent = api.registry.findMetaEvent(new Uint8Array(data));
+  data.push(...new Struct(api.registry, {data: metaEvent}, {data: fields}).toU8a());
+
+  const typeName = api.registry.lookup.names.find(n => n.endsWith('RuntimeEvent'))!;
+  const obj = api.registry.createType(typeName, new Uint8Array(data)) as InterfaceTypes['RuntimeEvent'];
+  return obj.toHex();
+};
+
+describe('EVM Migrations', () => {
+  let superuser: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      superuser = await privateKey('//Alice');
+      charlie = await privateKey('//Charlie');
+    });
+  });
+
+  // todo:playgrounds requires sudo, look into later
+  itEth('Deploy contract saved state', async ({helper}) => {
+    /*
+      contract StatefulContract {
+        uint counter;
+        mapping (uint => uint) kv;
+
+        function inc() public {
+          counter = counter + 1;
+        }
+        function counterValue() public view returns (uint) {
+          return counter;
+        }
+
+        function set(uint key, uint value) public {
+          kv[key] = value;
+        }
+
+        function get(uint key) public view returns (uint) {
+          return kv[key];
+        }
+      }
+    */
+    const ADDRESS = '0x4956bf52ef9ed8789f21bc600e915e0d961079f6';
+    const CODE = '0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631ab06ee514610051578063371303c01461006d5780637bfdec3b146100775780639507d39a14610095575b600080fd5b61006b60048036038101906100669190610160565b6100c5565b005b6100756100e1565b005b61007f6100f8565b60405161008c91906101af565b60405180910390f35b6100af60048036038101906100aa9190610133565b610101565b6040516100bc91906101af565b60405180910390f35b8060016000848152602001908152602001600020819055505050565b60016000546100f091906101ca565b600081905550565b60008054905090565b600060016000838152602001908152602001600020549050919050565b60008135905061012d8161025e565b92915050565b60006020828403121561014957610148610259565b5b60006101578482850161011e565b91505092915050565b6000806040838503121561017757610176610259565b5b60006101858582860161011e565b92505060206101968582860161011e565b9150509250929050565b6101a981610220565b82525050565b60006020820190506101c460008301846101a0565b92915050565b60006101d582610220565b91506101e083610220565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102155761021461022a565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61026781610220565b811461027257600080fd5b5056fea26469706673582212206a02d2fb5c244105ab884961479c1aee3b4c1011e4b5530ab483eb22344a865664736f6c63430008060033';
+    const DATA = [
+      // counter = 10
+      ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000000000000000000000000000000000a'],
+      // kv = {1: 1, 2: 2, 3: 3, 4: 4},
+      ['0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f', '0x0000000000000000000000000000000000000000000000000000000000000001'],
+      ['0xd9d16d34ffb15ba3a3d852f0d403e2ce1d691fb54de27ac87cd2f993f3ec330f', '0x0000000000000000000000000000000000000000000000000000000000000002'],
+      ['0x7dfe757ecd65cbd7922a9c0161e935dd7fdbcc0e999689c7d31633896b1fc60b', '0x0000000000000000000000000000000000000000000000000000000000000003'],
+      ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'],
+    ];
+
+    const caller = await helper.eth.createAccountWithBalance(superuser);
+
+    const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]);
+    const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]);
+    const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]);
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled;
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled;
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled;
+
+    const web3 = helper.getWeb3();
+    const contract = new web3.eth.Contract([
+      {
+        inputs: [],
+        name: 'counterValue',
+        outputs: [{
+          internalType: 'uint256',
+          name: '',
+          type: 'uint256',
+        }],
+        stateMutability: 'view',
+        type: 'function',
+      },
+      {
+        inputs: [{
+          internalType: 'uint256',
+          name: 'key',
+          type: 'uint256',
+        }],
+        name: 'get',
+        outputs: [{
+          internalType: 'uint256',
+          name: '',
+          type: 'uint256',
+        }],
+        stateMutability: 'view',
+        type: 'function',
+      },
+    ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS});
+
+    expect(await contract.methods.counterValue().call()).to.be.equal('10');
+    for(let i = 1; i <= 4; i++) {
+      expect(await contract.methods.get(i).call()).to.be.equal(i.toString());
+    }
+  });
+  itEth('Fake collection creation on substrate side', async ({helper}) => {
+    const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[
+      encodeEvent(helper.getApi(), 'Common', 'common', 'CollectionCreated', [
+        // Collection Id
+        9999,
+        // Collection mode: NFT
+        1,
+        // Owner
+        charlie.address,
+      ]),
+    ]]);
+    await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contain('common.CollectionCreated');
+  });
+  itEth('Fake token creation on substrate side', async ({helper}) => {
+    const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[
+      encodeEvent(helper.getApi(), 'Common', 'common', 'ItemCreated', [
+        // Collection Id
+        9999,
+        // TokenId
+        9999,
+        // Owner
+        {Substrate: charlie.address},
+        // Amount
+        1,
+      ]),
+    ]]);
+    await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]);
+    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
+    const eventStrings = event.map(e => `${e.section}.${e.method}`);
+
+    expect(eventStrings).to.contain('common.ItemCreated');
+  });
+  itEth('Fake token creation on ethereum side', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(superuser);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const caller = await helper.eth.createAccountWithBalance(superuser);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    {
+      const txInsertEthLogs = helper.constructApiCall('api.tx.evmMigration.insertEthLogs', [[
+        {
+        // Contract, which has emitted this log
+          address: collectionAddress,
+
+          topics: [
+            // First topic - event signature
+            helper.getWeb3().eth.abi.encodeEventSignature('Transfer(address,address,uint256)'),
+            // Rest of topics - indexed event fields in definition order
+            helper.getWeb3().eth.abi.encodeParameter('address', '0x' + '00'.repeat(20)),
+            helper.getWeb3().eth.abi.encodeParameter('address', caller),
+            helper.getWeb3().eth.abi.encodeParameter('uint256', 9999),
+          ],
+
+          // Every field coming from event, which is not marked as indexed, should be encoded here
+          // NFT transfer has no such fields, but here is an example for some other possible event:
+          // data: helper.getWeb3().eth.abi.encodeParameters(['uint256', 'address'], [22, collectionAddress])
+          data: [],
+        },
+      ]]);
+      await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEthLogs]);
+    }
+
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0x' + '00'.repeat(20));
+    expect(event.returnValues.to).to.be.equal(caller);
+    expect(event.returnValues.tokenId).to.be.equal('9999');
+  });
+});
addedjs-packages/tests/eth/nativeFungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/nativeFungible.test.ts
@@ -0,0 +1,173 @@
+// Copyright 2019-2023 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import {UniqueHelper} from '@unique/playgrounds/unique.js';
+
+describe('NativeFungible: ERC20 calls', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('approve()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    await expect(contract.methods.approve(spender, 100).call({from: owner})).to.be.rejectedWith('approve not supported');
+  });
+
+  itEth('balanceOf()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor, 123n);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const balance = await contract.methods.balanceOf(owner).call({from: owner});
+    expect(balance).to.be.eq('123000000000000000000');
+  });
+
+  itEth('decimals()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const realDecimals = (await helper.chain.getChainProperties().tokenDecimals)[0].toString();
+    const decimals = await contract.methods.decimals().call({from: owner});
+    expect(decimals).to.be.eq(realDecimals);
+  });
+
+  itEth('name()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const realName = await UniqueHelper.detectNetwork(helper.getApi());
+    const name = await contract.methods.name().call({from: owner});
+    expect(name).to.be.eq(realName);
+  });
+
+  itEth('symbol()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const realName = (await helper.chain.getChainProperties().tokenSymbol)[0];
+    const name = await contract.methods.symbol().call({from: owner});
+    expect(name).to.be.eq(realName);
+  });
+
+  itEth('totalSupply()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const totalSupplyEth = BigInt(await contract.methods.totalSupply().call({from: owner}));
+    const totalSupplySub = await helper.balance.getTotalIssuance();
+    expect(totalSupplyEth).to.be.eq(totalSupplySub);
+  });
+
+  itEth('transfer()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const balanceOwnerBefore = await helper.balance.getEthereum(owner);
+    const balanceReceiverBefore = await helper.balance.getEthereum(receiver);
+
+    await contract.methods.transfer(receiver, 50).send({from: owner});
+
+    const balanceOwnerAfter = await helper.balance.getEthereum(owner);
+    const balanceReceiverAfter = await helper.balance.getEthereum(receiver);
+
+    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+    expect(balanceReceiverBefore + 50n).to.be.equal(balanceReceiverAfter);
+  });
+
+  itEth('transferFrom()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const balanceOwnerBefore = await helper.balance.getEthereum(owner);
+    const balanceReceiverBefore = await helper.balance.getEthereum(receiver);
+
+    await contract.methods.transferFrom(owner, receiver, 50).send({from: owner});
+
+    const balanceOwnerAfter = await helper.balance.getEthereum(owner);
+    const balanceReceiverAfter = await helper.balance.getEthereum(receiver);
+
+    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+    expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
+
+    await expect(contract.methods.transferFrom(receiver, receiver, 50).call({from: owner})).to.be.rejectedWith('ApprovedValueTooLow');
+  });
+});
+
+describe('NativeFungible: ERC20UniqueExtensions calls', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('transferCross()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+    const balanceOwnerBefore = await helper.balance.getEthereum(owner);
+    const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth);
+
+    await contract.methods.transferCross(receiver, 50).send({from: owner});
+
+    const balanceOwnerAfter = await helper.balance.getEthereum(owner);
+    const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth);
+
+    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+    expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
+  });
+
+  itEth('transferFromCross()', async ({helper}) => {
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(0);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
+
+    const balanceOwnerBefore = await helper.balance.getEthereum(owner.eth);
+    const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth);
+
+    await contract.methods.transferFromCross(owner, receiver, 50).send({from: owner.eth});
+
+    const balanceOwnerAfter = await helper.balance.getEthereum(owner.eth);
+    const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth);
+
+    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
+    expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
+
+    await expect(contract.methods.transferFromCross(receiver, receiver, 50).call({from: owner.eth})).to.be.rejectedWith('no permission');
+  });
+});
addedjs-packages/tests/eth/nativeRpc/estimateGas.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/nativeRpc/estimateGas.test.ts
@@ -0,0 +1,62 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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, itEth, usingEthPlaygrounds} from '../util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+
+
+describe('Ethereum native RPC calls', () => {
+  let donor: IKeyringPair;
+  const NATIVE_TOKEN_ADDRESS = '0x17c4e6453cc49aaaaeaca894e6d9683e00000000';
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('estimate gas', async ({helper}) => {
+    const BALANCE = 100n;
+    const BALANCE_TO_TRANSFER = 90n;
+
+    const owner = await helper.eth.createAccountWithBalance(donor, BALANCE);
+    const recepient = helper.eth.createAccount();
+
+    const web3 = helper.getWeb3();
+    // data: transfer(recepient, 90);
+    const data = web3.eth.abi.encodeFunctionCall({
+      name: 'transfer',
+      type: 'function',
+      inputs: [{
+        type: 'address',
+        name: 'to',
+      },{
+        type: 'uint256',
+        name: 'amount',
+      }],
+    }, [recepient, (BALANCE_TO_TRANSFER * (10n ** 18n)).toString()]);
+
+    const estimateGas = await web3.eth.estimateGas({
+      to: NATIVE_TOKEN_ADDRESS,
+      value: '0x0',
+      data,
+      from: owner,
+      maxFeePerGas: '0x14c9338c61d',
+    });
+
+    expect(estimateGas).to.be.greaterThan(35000).and.to.be.lessThan(50000);
+  });
+});
addedjs-packages/tests/eth/nesting/nest.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/nesting/nest.test.ts
@@ -0,0 +1,289 @@
+import type {IKeyringPair} from '@polkadot/types/types';
+import {Contract} from 'web3-eth-contract';
+
+import {itEth, usingEthPlaygrounds, expect} from '../util/index.js';
+import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js';
+
+const createNestingCollection = async (
+  helper: EthUniqueHelper,
+  owner: string,
+  mergeDeprecated = false,
+): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {
+  const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+
+  const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, mergeDeprecated);
+  await contract.methods.setCollectionNesting([true, false, []]).send({from: owner});
+
+  return {collectionId, collectionAddress, contract};
+};
+
+
+describe('EVM nesting tests group', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  describe('Integration Test: EVM Nesting', () => {
+    itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionId, contract} = await createNestingCollection(helper, owner);
+
+      // Create a token to be nested to
+      const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;
+      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);
+
+      // Create a nested token
+      const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});
+      const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;
+      expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);
+
+      // Create a token to be nested and nest
+      const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;
+
+      await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});
+      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);
+
+      // Unnest token back
+      await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});
+      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);
+    });
+
+    itEth('NFT: collectionNesting()', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+      const unnestedContract = await helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);
+      expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
+
+      const {contract} = await createNestingCollection(helper, owner);
+      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, []]);
+      await contract.methods.setCollectionNesting([true, false, [unnestedCollectionAddress]]).send({from: owner});
+      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress]]);
+      await contract.methods.setCollectionNesting([false, true, [unnestedCollectionAddress]]).send({from: owner});
+      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, true, [unnestedCollectionAddress]]);
+      await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});
+      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
+    });
+
+    // Sof-deprecated
+    itEth('NFT: collectionNestingRestrictedCollectionIds() & collectionNestingPermissions', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const {collectionId: unnestedCollsectionId, collectionAddress: unnsetedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
+      const unnestedContract = await helper.ethNativeContract.collection(unnsetedCollectionAddress, 'nft', owner, true);
+      expect(await unnestedContract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([false, []]);
+
+      const {contract} = await createNestingCollection(helper, owner, true);
+      expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, []]);
+      await contract.methods['setCollectionNesting(bool,address[])'](true, [unnsetedCollectionAddress]).send({from: owner});
+      expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, [unnestedCollsectionId.toString()]]);
+      expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', true]]);
+      await contract.methods['setCollectionNesting(bool)'](false).send({from: owner});
+      expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', false]]);
+    });
+
+    itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner);
+      const {contract: contractB} = await createNestingCollection(helper, owner);
+      await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner});
+
+      // Create a token to nest into
+      const mintingtargetNftTokenIdResult = await contractA.methods.mint(owner).send({from: owner});
+      const targetNftTokenId = mintingtargetNftTokenIdResult.events.Transfer.returnValues.tokenId;
+      const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId);
+
+      // Create a token for nesting in the same collection as the target
+      const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});
+      const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;
+
+      // Create a token for nesting in a different collection
+      const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});
+      const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;
+
+      // Nest
+      await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner});
+      expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1);
+
+      await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner});
+      expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1);
+    });
+  });
+
+  describe('Negative Test: EVM Nesting', () => {
+    itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionId, contract} = await createNestingCollection(helper, owner);
+      await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});
+
+      // Create a token to nest into
+      const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
+      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);
+
+      // Create a token to nest
+      const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;
+
+      // Try to nest
+      await expect(contract.methods
+        .transfer(targetNftTokenAddress, nftTokenId)
+        .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
+    });
+
+    itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const malignant = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionId, contract} = await createNestingCollection(helper, owner);
+
+      // Mint a token
+      const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});
+      const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
+      const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);
+
+      // Mint a token belonging to a different account
+      const mintingTokenIdResult = await contract.methods.mint(malignant).send({from: owner});
+      const tokenId = mintingTokenIdResult.events.Transfer.returnValues.tokenId;
+
+      // Try to nest one token in another as a non-owner account
+      await expect(contract.methods
+        .transfer(targetTokenAddress, tokenId)
+        .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
+    });
+
+    itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const malignant = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner);
+      const {contract: contractB} = await createNestingCollection(helper, owner);
+
+      await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner});
+
+      // Create a token in one collection
+      const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});
+      const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;
+      const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);
+
+      // Create a token in another collection
+      const mintingTokenIdBResult = await contractB.methods.mint(malignant).send({from: owner});
+      const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;
+
+      // Try to drag someone else's token into the other collection and nest
+      await expect(contractB.methods
+        .transfer(nftTokenAddressA, nftTokenIdB)
+        .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
+    });
+
+    itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner);
+      const {contract: contractB} = await createNestingCollection(helper, owner);
+
+      await contractA.methods.setCollectionNesting([true, false, [contractA.options.address]]).send({from: owner});
+
+      // Create a token in one collection
+      const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});
+      const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;
+      const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);
+
+      // Create a token in another collection
+      const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});
+      const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;
+
+
+      // Try to nest into a token in the other collection, disallowed in the first
+      await expect(contractB.methods
+        .transfer(nftTokenAddressA, nftTokenIdB)
+        .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest');
+    });
+  });
+
+  describe('Fungible', () => {
+    async function createFungibleCollection(helper: EthUniqueHelper, owner: string, mode: 'ft' | 'native ft') {
+      if(mode === 'ft') {
+        const {collectionAddress} = await helper.eth.createFungibleCollection(owner, '', 18, '', '');
+        const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+        await contract.methods.mint(owner, 100n).send({from: owner});
+        return {collectionAddress, contract};
+      }
+
+      // native ft
+      const collectionAddress = helper.ethAddress.fromCollectionId(0);
+      const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+      return {collectionAddress, contract};
+    }
+
+    [
+      {mode: 'ft' as const},
+      {mode: 'native ft' as const},
+    ].map(testCase => {
+      itEth(`Allow nest [${testCase.mode}]`, async ({helper}) => {
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner);
+        const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode);
+
+        const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner});
+        const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
+        const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId);
+
+        await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner});
+        expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('10');
+      });
+    });
+
+    [
+      {mode: 'ft' as const},
+      {mode: 'native ft' as const},
+    ].map(testCase => {
+      itEth(`Allow partial/full unnest [${testCase.mode}]`, async ({helper}) => {
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner);
+        const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode);
+
+        const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner});
+        const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
+        const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId);
+
+        await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner});
+
+        await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner});
+        expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('5');
+
+        await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner});
+        expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('0');
+      });
+    });
+
+    [
+      {mode: 'ft' as const},
+      {mode: 'native ft' as const},
+    ].map(testCase => {
+      itEth(`Disallow nest into collection without nesting permission [${testCase.mode}] (except for native fungible collection)`, async ({helper}) => {
+        const owner = await helper.eth.createAccountWithBalance(donor);
+        const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner);
+        await targetContract.methods.setCollectionNesting([false, false, []]).send({from: owner});
+
+        const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode);
+
+        const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner});
+        const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
+        const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId);
+
+        if(testCase.mode === 'ft') {
+          await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
+        } else {
+          await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.not.rejected;
+        }
+      });
+    });
+  });
+});
addedjs-packages/tests/eth/nonFungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/nonFungible.test.ts
@@ -0,0 +1,1127 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {itEth, usingEthPlaygrounds, expect} from './util/index.js';
+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 {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js';
+
+describe('Check ERC721 token URI for NFT', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    const result = await contract.methods.mint(receiver).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+    expect(tokenId).to.be.equal('1');
+
+    if(propertyKey && propertyValue) {
+      // Set URL or suffix
+      await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();
+    }
+
+    const event = result.events.Transfer;
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(tokenId);
+
+    return {contract, nextTokenId: tokenId};
+  }
+
+  itEth('Empty tokenURI', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, '');
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
+  });
+
+  itEth('TokenURI from url', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
+  });
+
+  itEth('TokenURI from baseURI', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');
+  });
+
+  itEth('TokenURI from baseURI + suffix', async ({helper}) => {
+    const suffix = '/some/suffix';
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
+  });
+});
+
+describe('NFT: Plain calls', () => {
+  let donor: IKeyringPair;
+  let minter: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  // TODO combine all minting tests in one place
+  [
+    'substrate' as const,
+    'ethereum' as const,
+  ].map(testCase => {
+    itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
+      const collectionAdmin = await helper.eth.createAccountWithBalance(donor);
+
+      const receiverEth = helper.eth.createAccount();
+      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+      const receiverSub = bob;
+      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
+
+      // const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);
+      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
+      const permissions: ITokenPropertyPermission[] = properties
+        .map(p => ({
+          key: p.key, permission: {
+            tokenOwner: false,
+            collectionAdmin: true,
+            mutable: false,
+          },
+        }));
+
+      const collection = await helper.nft.mintCollection(minter, {
+        tokenPrefix: 'ethp',
+        tokenPropertyPermissions: permissions,
+      });
+      await collection.addAdmin(minter, {Ethereum: collectionAdmin});
+
+      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', collectionAdmin, true);
+      let expectedTokenId = await contract.methods.nextTokenId().call();
+      let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();
+      let tokenId = result.events.Transfer.returnValues.tokenId;
+      expect(tokenId).to.be.equal(expectedTokenId);
+
+      let event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
+      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+
+      expectedTokenId = await contract.methods.nextTokenId().call();
+      result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();
+      event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
+      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+
+      tokenId = result.events.Transfer.returnValues.tokenId;
+
+      expect(tokenId).to.be.equal(expectedTokenId);
+
+      expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
+        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
+
+      expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
+        .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
+    });
+  });
+
+  itEth('Non-owner and non admin cannot mintCross', async ({helper}) => {
+    const nonOwner = await helper.eth.createAccountWithBalance(donor);
+    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+
+    const collection = await helper.nft.mintCollection(minter);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    await expect(collectionEvm.methods.mintCross(nonOwnerCross, []).call({from: nonOwner}))
+      .to.be.rejectedWith('PublicMintingNotAllowed');
+  });
+
+  //TODO: CORE-302 add eth methods
+  itEth.skip('Can perform mintBulk()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(minter);
+    await collection.addAdmin(minter, {Ethereum: caller});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
+    {
+      const bulkSize = 3;
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintBulkWithTokenURI(
+        receiver,
+        Array.from({length: bulkSize}, (_, i) => (
+          [+nextTokenId + i, `Test URI ${i}`]
+        )),
+      ).send({from: caller});
+
+      const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
+      for(let i = 0; i < bulkSize; i++) {
+        const event = events[i];
+        expect(event.address).to.equal(collectionAddress);
+        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+        expect(event.returnValues.to).to.equal(receiver);
+        expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
+
+        expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);
+      }
+    }
+  });
+
+  itEth('Can perform mintBulkCross()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const callerCross = helper.ethCrossAccount.fromAddress(caller);
+    const receiver = helper.eth.createAccount();
+    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
+
+    const permissions = [
+      {code: TokenPermissionField.Mutable, value: true},
+      {code: TokenPermissionField.TokenOwner, value: true},
+      {code: TokenPermissionField.CollectionAdmin, value: true},
+    ];
+    const {collectionAddress} = await helper.eth.createCollection(
+      caller,
+      {
+        ...CREATE_COLLECTION_DATA_DEFAULTS,
+        name: 'A',
+        description: 'B',
+        tokenPrefix: 'C',
+        collectionMode: 'nft',
+        adminList: [callerCross],
+        tokenPropertyPermissions: [
+          {key: 'key_0_0', permissions},
+          {key: 'key_1_0', permissions},
+          {key: 'key_1_1', permissions},
+          {key: 'key_2_0', permissions},
+          {key: 'key_2_1', permissions},
+          {key: 'key_2_2', permissions},
+        ],
+      },
+    ).send();
+
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintBulkCross([
+        {
+          owner: receiverCross,
+          properties: [
+            {key: 'key_0_0', value: Buffer.from('value_0_0')},
+          ],
+        },
+        {
+          owner: receiverCross,
+          properties: [
+            {key: 'key_1_0', value: Buffer.from('value_1_0')},
+            {key: 'key_1_1', value: Buffer.from('value_1_1')},
+          ],
+        },
+        {
+          owner: receiverCross,
+          properties: [
+            {key: 'key_2_0', value: Buffer.from('value_2_0')},
+            {key: 'key_2_1', value: Buffer.from('value_2_1')},
+            {key: 'key_2_2', value: Buffer.from('value_2_2')},
+          ],
+        },
+      ]).send({from: caller});
+      const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
+      const bulkSize = 3;
+      for(let i = 0; i < bulkSize; i++) {
+        const event = events[i];
+        expect(event.address).to.equal(collectionAddress);
+        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+        expect(event.returnValues.to).to.equal(receiver);
+        expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
+      }
+
+      const properties = [
+        await contract.methods.properties(+nextTokenId, []).call(),
+        await contract.methods.properties(+nextTokenId + 1, []).call(),
+        await contract.methods.properties(+nextTokenId + 2, []).call(),
+      ];
+      expect(properties).to.be.deep.equal([
+        [
+          ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],
+        ],
+        [
+          ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],
+          ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],
+        ],
+        [
+          ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
+          ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
+          ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
+        ],
+      ]);
+    }
+  });
+
+  itEth('Can perform burn()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.nft.mintCollection(minter, {});
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
+
+    {
+      const result = await contract.methods.burn(tokenId).send({from: caller});
+
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(caller);
+      expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
+    }
+  });
+
+  itEth('Can perform approve()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(minter, {});
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    {
+      const badTokenId = await contract.methods.nextTokenId().call() + 1;
+      await expect(contract.methods.getApproved(badTokenId).call()).to.be.rejectedWith('revert TokenNotFound');
+    }
+    {
+      const approved = await contract.methods.getApproved(tokenId).call();
+      expect(approved).to.be.equal('0x0000000000000000000000000000000000000000');
+    }
+    {
+      const result = await contract.methods.approve(spender, tokenId).send({from: owner});
+
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.approved).to.be.equal(spender);
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
+    }
+    {
+      const approved = await contract.methods.getApproved(tokenId).call();
+      expect(approved).to.be.equal(spender);
+    }
+  });
+
+  itEth('Can perform setApprovalForAll()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const operator = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(minter, {});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();
+    expect(approvedBefore).to.be.equal(false);
+
+    {
+      const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+
+      expect(result.events.ApprovalForAll).to.be.like({
+        address: collectionAddress,
+        event: 'ApprovalForAll',
+        returnValues: {
+          owner,
+          operator,
+          approved: true,
+        },
+      });
+
+      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+      expect(approvedAfter).to.be.equal(true);
+    }
+
+    {
+      const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});
+
+      expect(result.events.ApprovalForAll).to.be.like({
+        address: collectionAddress,
+        event: 'ApprovalForAll',
+        returnValues: {
+          owner,
+          operator,
+          approved: false,
+        },
+      });
+
+      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+      expect(approvedAfter).to.be.equal(false);
+    }
+  });
+
+  itEth('Can perform burn with ApprovalForAll', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const operator = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft');
+
+    {
+      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});
+      const events = result.events.Transfer;
+
+      expect(events).to.be.like({
+        address,
+        event: 'Transfer',
+        returnValues: {
+          from: owner,
+          to: '0x0000000000000000000000000000000000000000',
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await helper.nft.doesTokenExist(collection.collectionId, token.tokenId)).to.be.false;
+  });
+
+  itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const operator = await helper.eth.createAccountWithBalance(donor);
+    const receiver = charlie;
+
+    const token = await collection.mintToken(minter, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft');
+
+    {
+      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: owner,
+          to: helper.address.substrateToEth(receiver.address),
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
+  });
+
+  itEth('Can perform burnFromCross()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const ownerSub = bob;
+    const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);
+    const ownerEth = await helper.eth.createAccountWithBalance(donor);
+    const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);
+
+    const burnerEth = await helper.eth.createAccountWithBalance(donor);
+    const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth);
+
+    const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address});
+    const token2 = await collection.mintToken(minter, {Ethereum: ownerEth});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    // Approve tokens from substrate and ethereum:
+    await token1.approve(ownerSub, {Ethereum: burnerEth});
+    await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth});
+
+    // can burnFromCross:
+    const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth});
+    const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth});
+    const events1 = result1.events.Transfer;
+    const events2 = result2.events.Transfer;
+
+    // Check events for burnFromCross (substrate and ethereum):
+    [
+      [events1, token1, helper.address.substrateToEth(ownerSub.address)],
+      [events2, token2, ownerEth],
+    ].map(burnData => {
+      expect(burnData[0]).to.be.like({
+        address: collectionAddress,
+        event: 'Transfer',
+        returnValues: {
+          from: burnData[2],
+          to: '0x0000000000000000000000000000000000000000',
+          tokenId: burnData[1].tokenId.toString(),
+        },
+      });
+    });
+
+    expect(await token1.doesExist()).to.be.false;
+    expect(await token2.doesExist()).to.be.false;
+  });
+
+  // TODO combine all approve tests in one place
+  itEth('Can perform approveCross()', async ({helper}) => {
+    // arrange: create accounts
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+    const receiverSub = charlie;
+    const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
+    const receiverEth = await helper.eth.createAccountWithBalance(donor);
+    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+
+    // arrange: create collection and tokens:
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const token1 = await collection.mintToken(minter, {Ethereum: owner});
+    const token2 = await collection.mintToken(minter, {Ethereum: owner});
+
+    const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
+
+    // Can approveCross substrate and ethereum address:
+    const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner});
+    const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner});
+    const eventSub = resultSub.events.Approval;
+    const eventEth = resultEth.events.Approval;
+    expect(eventSub).to.be.like({
+      address: helper.ethAddress.fromCollectionId(collection.collectionId),
+      event: 'Approval',
+      returnValues: {
+        owner,
+        approved: helper.address.substrateToEth(receiverSub.address),
+        tokenId: token1.tokenId.toString(),
+      },
+    });
+    expect(eventEth).to.be.like({
+      address: helper.ethAddress.fromCollectionId(collection.collectionId),
+      event: 'Approval',
+      returnValues: {
+        owner,
+        approved: receiverEth,
+        tokenId: token2.tokenId.toString(),
+      },
+    });
+
+    // Substrate address can transferFrom approved tokens:
+    await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address});
+    expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address});
+    // Ethereum address can transferFromCross approved tokens:
+    await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth});
+    expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});
+  });
+
+  itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
+    const nonOwner = await helper.eth.createAccountWithBalance(donor);
+    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
+    const token = await collection.mintToken(minter, {Ethereum: owner});
+
+    await expect(collectionEvm.methods.approveCross(nonOwnerCross, token.tokenId).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
+  });
+
+  itEth('Can reaffirm approved address', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);
+    const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor);
+    const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1);
+    const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2);
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const token1 = await collection.mintToken(minter, {Ethereum: owner});
+    const token2 = await collection.mintToken(minter, {Ethereum: owner});
+    const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
+
+    // Can approve and reaffirm approved address:
+    await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner});
+    await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner});
+
+    // receiver1 cannot transferFrom:
+    await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;
+    // receiver2 can transferFrom:
+    await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address});
+
+    // can set approved address to self address to remove approval:
+    await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner});
+    await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner});
+
+    // receiver1 cannot transfer token anymore:
+    await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;
+  });
+
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(minter, {});
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    await contract.methods.approve(spender, tokenId).send({from: owner});
+
+    {
+      const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});
+
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(owner).call();
+      expect(+balance).to.equal(0);
+    }
+  });
+
+  itEth('Can perform transferFromCross()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, {Substrate: owner.address});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft');
+
+    await token.approve(owner, {Ethereum: spender});
+
+    {
+      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: helper.address.substrateToEth(receiver.address),
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
+  });
+
+  itEth('Can perform transfer()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    {
+      const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});
+
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiver);
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(owner).call();
+      expect(+balance).to.equal(0);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+  });
+
+  itEth('Can perform transferCross()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiverEth = await helper.eth.createAccountWithBalance(donor);
+    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
+
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+
+    {
+      // Can transferCross to ethereum address:
+      const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner});
+      // Check events:
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(owner);
+      expect(event.returnValues.to).to.be.equal(receiverEth);
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
+
+      // owner has balance = 0:
+      const ownerBalance = await collectionEvm.methods.balanceOf(owner).call();
+      expect(+ownerBalance).to.equal(0);
+      // receiver owns token:
+      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
+      expect(+receiverBalance).to.equal(1);
+      expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});
+    }
+
+    {
+      // Can transferCross to substrate address:
+      const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth});
+      // Check events:
+      const event = substrateResult.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(receiverEth);
+      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));
+      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
+
+      // owner has balance = 0:
+      const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
+      expect(+ownerBalance).to.equal(0);
+      // receiver owns token:
+      const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});
+      expect(receiverBalance).to.contain(tokenId);
+    }
+  });
+
+  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+    const tokenOwner = await helper.eth.createAccountWithBalance(donor);
+    const receiverSub = minter;
+    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
+
+    const collection = await helper.nft.mintCollection(minter, {});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', sender);
+
+    await collection.mintToken(minter, {Ethereum: sender});
+    const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner});
+
+    // Cannot transferCross someone else's token:
+    const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;
+    await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;
+    // Cannot transfer token if it does not exist:
+    await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;
+  }));
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+
+    for(let i = 1; i < 10; i++) {
+      await collection.mintToken(minter, {Ethereum: owner.eth});
+      expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
+    }
+  });
+
+  itEth('Check ownerOfCross()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+    let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner.eth});
+
+    for(let i = 1n; i < 10n; i++) {
+      const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+      expect(ownerCross.eth).to.be.eq(owner.eth);
+      expect(ownerCross.sub).to.be.eq(owner.sub);
+
+      const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+      await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
+      owner = newOwner;
+    }
+  });
+});
+
+describe('NFT: Fees', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    });
+  });
+
+  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
+
+    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
+
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+
+  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
+
+    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
+
+    await contract.methods.approve(spender, tokenId).send({from: owner});
+
+    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+
+  itEth('Can perform transferFromCross()', async ({helper}) => {
+    const collectionMinter = alice;
+    const owner = bob;
+    const receiver = charlie;
+    const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft');
+
+    await token.approve(owner, {Ethereum: spender});
+
+    {
+      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: helper.address.substrateToEth(receiver.address),
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
+  });
+
+  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(alice, {});
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
+
+    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
+
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+});
+
+describe('NFT: Substrate calls', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([20n], donor);
+    });
+  });
+
+  itEth('Events emitted for mint()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    const {tokenId} = await collection.mintToken(alice);
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());
+  });
+
+  itEth('Events emitted for burn()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await token.burn(alice);
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
+  });
+
+  itEth('Events emitted for approve()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await token.approve(alice, {Ethereum: receiver});
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.approved).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
+  });
+
+  itEth('Events emitted for transferFrom()', async ({helper}) => {
+    const [bob] = await helper.arrange.createAccounts([10n], donor);
+    const receiver = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
+    await token.approve(alice, {Substrate: bob.address});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});
+
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
+  });
+
+  itEth('Events emitted for transfer()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await token.transfer(alice, {Ethereum: receiver});
+
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
+  });
+});
+
+describe('Common metadata', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([20n], donor);
+    });
+  });
+
+  itEth('Returns collection name', async ({helper}) => {
+    const caller = helper.eth.createAccount();
+    const tokenPropertyPermissions = [{
+      key: 'URI',
+      permission: {
+        mutable: true,
+        collectionAdmin: true,
+        tokenOwner: false,
+      },
+    }];
+    const collection = await helper.nft.mintCollection(
+      alice,
+      {
+        name: 'oh River',
+        tokenPrefix: 'CHANGE',
+        properties: [{key: 'ERC721Metadata', value: '1'}],
+        tokenPropertyPermissions,
+      },
+    );
+
+    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
+    const name = await contract.methods.name().call();
+    expect(name).to.equal('oh River');
+  });
+
+  itEth('Returns symbol name', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const tokenPropertyPermissions = [{
+      key: 'URI',
+      permission: {
+        mutable: true,
+        collectionAdmin: true,
+        tokenOwner: false,
+      },
+    }];
+    const collection = await helper.nft.mintCollection(
+      alice,
+      {
+        name: 'oh River',
+        tokenPrefix: 'CHANGE',
+        properties: [{key: 'ERC721Metadata', value: '1'}],
+        tokenPropertyPermissions,
+      },
+    );
+
+    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
+    const symbol = await contract.methods.symbol().call();
+    expect(symbol).to.equal('CHANGE');
+  });
+});
+
+describe('Negative tests', () => {
+  let donor: IKeyringPair;
+  let minter: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itEth('[negative] Cant perform burn without approval', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft');
+
+    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
+
+    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
+    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
+
+    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
+  });
+
+  itEth('[negative] Cant perform transfer without approval', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const receiver = alice;
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft');
+
+    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+    const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+
+    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
+
+    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
+    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
+
+    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
+  });
+});
addedjs-packages/tests/eth/payable.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/payable.test.ts
@@ -0,0 +1,273 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+
+import {itEth, expect, usingEthPlaygrounds} from './util/index.js';
+import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
+import {makeNames} from '../util/index.js';
+
+const {dirname} = makeNames(import.meta.url);
+
+describe('EVM payable contracts', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Evm contract can receive wei from eth account', async ({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const contract = await helper.eth.deployCollectorContract(deployer);
+
+    const web3 = helper.getWeb3();
+
+    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS});
+
+    expect(await contract.methods.getCollected().call()).to.be.equal('10000');
+  });
+
+  itEth('Evm contract can receive wei from substrate account', async ({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const contract = await helper.eth.deployCollectorContract(deployer);
+    const [alice] = await helper.arrange.createAccounts([40n], donor);
+
+    const weiCount = '10000';
+
+    // Transaction fee/value will be payed from subToEth(sender) evm balance,
+    // which is backed by evmToAddress(subToEth(sender)) substrate balance
+    await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n);
+
+
+    await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);
+
+    expect(await contract.methods.getCollected().call()).to.be.equal(weiCount);
+  });
+
+  // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible
+  itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const contract = await helper.eth.deployCollectorContract(deployer);
+    const [alice] = await helper.arrange.createAccounts([10n], donor);
+
+    const weiCount = 10_000n;
+
+    await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false);
+
+    expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString());
+  });
+
+  itEth('Balance can be retrieved from evm contract', async({helper}) => {
+    const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal();
+    const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal();
+
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const contract = await helper.eth.deployCollectorContract(deployer);
+    const [alice] = await helper.arrange.createAccounts([20n], donor);
+
+    const web3 = helper.getWeb3();
+
+    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});
+
+    const [receiver] = await helper.arrange.createAccounts([0n], donor);
+
+    // First receive balance on eth balance of bob
+    {
+      const ethReceiver = helper.address.substrateToEth(receiver.address);
+      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0');
+      await contract.methods.withdraw(ethReceiver).send({from: deployer});
+      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString());
+    }
+
+    // Some balance is required to pay fee for evm.withdraw call
+    await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE);
+    // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString());
+
+    // Withdraw balance from eth to substrate
+    {
+      const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address);
+      await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true);
+      const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address);
+
+      expect(finalReceiverBalance > initialReceiverBalance).to.be.true;
+    }
+  });
+});
+
+describe('EVM transaction fees', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Fee is withdrawn from the user', async({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const contract = await helper.eth.deployFlipper(deployer);
+
+    const initialCallerBalance = await helper.balance.getEthereum(caller);
+    await contract.methods.flip().send({from: caller});
+    const finalCallerBalance = await helper.balance.getEthereum(caller);
+    expect(finalCallerBalance < initialCallerBalance).to.be.true;
+  });
+
+  itEth('Fee for nested calls is withdrawn from the user', async({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const contract = await deployProxyContract(helper, deployer);
+
+    const initialCallerBalance = await helper.balance.getEthereum(caller);
+    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
+    await contract.methods.flip().send({from: caller});
+    const finalCallerBalance = await helper.balance.getEthereum(caller);
+    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
+    expect(finalCallerBalance < initialCallerBalance).to.be.true;
+    expect(finalContractBalance == initialContractBalance).to.be.true;
+  });
+
+  itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => {
+    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();
+
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const contract = await deployProxyContract(helper, deployer);
+
+    const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;
+    const initialCallerBalance = await helper.balance.getEthereum(caller);
+    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
+    await contract.methods.mintNftToken(collectionAddress).send({from: caller});
+    const finalCallerBalance = await helper.balance.getEthereum(caller);
+    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
+    expect(finalCallerBalance < initialCallerBalance).to.be.true;
+    expect(finalContractBalance == initialContractBalance).to.be.true;
+  });
+
+  itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => {
+    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const contract = await deployProxyContract(helper, deployer);
+
+    const initialCallerBalance = await helper.balance.getEthereum(caller);
+    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
+    await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});
+    const finalCallerBalance = await helper.balance.getEthereum(caller);
+    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
+    expect(finalCallerBalance < initialCallerBalance).to.be.true;
+    expect(finalContractBalance == initialContractBalance).to.be.true;
+  });
+
+  itEth('Negative test: call createNFTCollection with wrong fee', async({helper}) => {
+    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();
+    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);
+
+    await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+    await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+  });
+
+  itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => {
+    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();
+    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);
+
+    await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+    await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
+  });
+
+  itEth('Get collection creation fee', async({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    expect(await helper.eth.getCollectionCreationFee(deployer)).to.be.equal(String(2n * helper.balance.getOneTokenNominal()));
+  });
+
+  async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {
+    return await helper.ethContract.deployByCode(
+      deployer,
+      'ProxyContract',
+      `
+      // SPDX-License-Identifier: UNLICENSED
+      pragma solidity ^0.8.6;
+
+      import {CollectionHelpers} from "../api/CollectionHelpers.sol";
+      import {UniqueNFT} from "../api/UniqueNFT.sol";
+
+      error Value(uint256 value);
+
+      contract ProxyContract {
+        bool value = false;
+        address innerContract;
+
+        event CollectionCreated(address collection);
+        event TokenMinted(uint256 tokenId);
+
+        receive() external payable {}
+
+        constructor() {
+          innerContract = address(new InnerContract());
+        }
+
+        function flip() public {
+          value = !value;
+          InnerContract(innerContract).flip();
+        }
+
+        function createNFTCollection() external payable {
+          address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
+		      address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C");
+          emit CollectionCreated(nftCollection);
+        }
+
+        function mintNftToken(address collectionAddress) external  {
+          UniqueNFT collection = UniqueNFT(collectionAddress);
+          uint256 tokenId = collection.mint(msg.sender);
+          emit TokenMinted(tokenId);
+        }
+
+        function getValue() external view returns (bool) {
+          return InnerContract(innerContract).getValue();
+        }
+      }
+
+      contract InnerContract {
+        bool value = false;
+        function flip() external {
+          value = !value;
+        }
+        function getValue() external view returns (bool) {
+          return value;
+        }
+      }
+      `,
+      [
+        {
+          solPath: 'api/CollectionHelpers.sol',
+          fsPath: `${dirname}/api/CollectionHelpers.sol`,
+        },
+        {
+          solPath: 'api/UniqueNFT.sol',
+          fsPath: `${dirname}/api/UniqueNFT.sol`,
+        },
+      ],
+    );
+  }
+});
addedjs-packages/tests/eth/precompile.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/precompile.test.ts
@@ -0,0 +1,112 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+
+import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+
+describe('Precompiles', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('ecrecover is supported', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const ecrecoverCompiledСontract = await helper.ethContract.compile(
+      'ECRECOVER',
+      `
+      // SPDX-License-Identifier: MIT
+      pragma solidity ^0.8.17;
+
+      contract ECRECOVER{
+        address addressTest = 0x12Cb274aAD8251C875c0bf6872b67d9983E53fDd;
+        bytes32 msgHash1 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d50;
+        bytes32 msgHash2 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d51;
+        uint8 v = 0x1b;
+        bytes32 r = 0x44287513919034a471a7dc2b2ed121f95984ae23b20f9637ba8dff471b6719ef;
+        bytes32 s = 0x7d7dc30309a3baffbfd9342b97d0e804092c0aeb5821319aa732bc09146eafb4;
+
+
+        function verifyValid() public view returns(bool) {
+           // Use ECRECOVER to verify address
+           return ecrecover(msgHash1, v, r, s) == (addressTest);
+        }
+
+        function verifyInvalid() public view returns(bool) {
+          // Use ECRECOVER to verify address
+          return ecrecover(msgHash2, v, r, s) == (addressTest);
+        }
+      }
+      `,
+    );
+
+    const ecrecoverСontract = await helper.ethContract.deployByAbi(owner, ecrecoverCompiledСontract.abi, ecrecoverCompiledСontract.object);
+    expect(await ecrecoverСontract.methods.verifyValid().call({from: owner})).to.be.true;
+    expect(await ecrecoverСontract.methods.verifyInvalid().call({from: owner})).to.be.false;
+  });
+
+  itEth('sr25519 is supported', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sr25519CompiledСontract = await helper.ethContract.compile(
+      'SR25519Contract',
+      `
+      // SPDX-License-Identifier: MIT
+      pragma solidity ^0.8.17;
+
+      /**
+       * @title SR25519 signature interface.
+       */
+      interface SR25519 {
+          /**
+           * @dev Verify signed message using SR25519 crypto.
+           * @return A boolean confirming whether the public key is signer for the message. 
+           */
+          function verify(
+            bytes32 public_key,
+            bytes calldata signature,
+            bytes calldata message
+          ) external view returns (bool);
+      }
+
+      contract SR25519Contract{
+        SR25519 public constant sr25519 = SR25519(0x0000000000000000000000000000000000005002);
+
+        bytes32 public_key = 0x96b2f9237ed0890fbeed891ebb81b91ac0d5d5b6e3afcdbc95df1b68d9f14036;
+        bytes signature = hex"4a5d733d7c568f2e88abf0467fd497f87c1be3e940ed897efdf9da72eaad394ef9918cb574ee99c54485775b17a0deaf46ff7a1f10346cea39fff0e4ede97689";
+        bytes message1 = hex"7372323535313920697320737570706f72746564";
+        bytes message2 = hex"7372323535313920697320737570706f7274656401";
+
+
+        function verifyValid() public view returns(bool) {
+          return sr25519.verify(public_key, signature, message1);
+        }
+
+        function verifyInvalid() public view returns(bool) {
+          return sr25519.verify(public_key, signature, message2);
+        }
+      }
+      `,
+    );
+
+    const sr25519Сontract = await helper.ethContract.deployByAbi(owner, sr25519CompiledСontract.abi, sr25519CompiledСontract.object);
+    expect(await sr25519Сontract.methods.verifyValid().call({from: owner})).to.be.true;
+    expect(await sr25519Сontract.methods.verifyInvalid().call({from: owner})).to.be.false;
+  });
+});
addedjs-packages/tests/eth/proxy/UniqueFungibleProxy.abidiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/UniqueFungibleProxy.abi
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"interfaceId","type":"uint32"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedjs-packages/tests/eth/proxy/UniqueFungibleProxy.bindiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/UniqueFungibleProxy.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b5060405161098038038061098083398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b6108ed806100936000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c806370a082311161006657806370a082311461012757806395d89b411461013a578063a9059cbb14610142578063dd62ed3e14610155578063f4f4b5001461016857600080fd5b806306fdde03146100a3578063095ea7b3146100c157806318160ddd146100e457806323b872dd146100fa578063313ce5671461010d575b600080fd5b6100ab61017b565b6040516100b8919061083e565b60405180910390f35b6100d46100cf3660046106e3565b610200565b60405190151581526020016100b8565b6100ec61028f565b6040519081526020016100b8565b6100d46101083660046106a7565b610316565b6101156103ad565b60405160ff90911681526020016100b8565b6100ec610135366004610659565b610434565b6100ab6104b8565b6100d46101503660046106e3565b6104fc565b6100ec610163366004610674565b610536565b6100d46101763660046107f5565b6105bc565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde039260048082019391829003018186803b1580156101bf57600080fd5b505afa1580156101d3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101fb919081019061072f565b905090565b6000805460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b3906044015b602060405180830381600087803b15801561025057600080fd5b505af1158015610264573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610288919061070d565b9392505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156102de57600080fd5b505afa1580156102f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb91906107dc565b600080546040516323b872dd60e01b81526001600160a01b038681166004830152858116602483015260448201859052909116906323b872dd90606401602060405180830381600087803b15801561036d57600080fd5b505af1158015610381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a5919061070d565b949350505050565b60008060009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103fc57600080fd5b505afa158015610410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb919061081b565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240160206040518083038186803b15801561047a57600080fd5b505afa15801561048e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b291906107dc565b92915050565b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b419260048082019391829003018186803b1580156101bf57600080fd5b6000805460405163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529091169063a9059cbb90604401610236565b60008054604051636eb1769f60e11b81526001600160a01b03858116600483015284811660248301529091169063dd62ed3e9060440160206040518083038186803b15801561058457600080fd5b505afa158015610598573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061028891906107dc565b6000805460405162f4f4b560e81b815263ffffffff841660048201526001600160a01b039091169063f4f4b5009060240160206040518083038186803b15801561060557600080fd5b505afa158015610619573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b2919061070d565b80356001600160a01b038116811461065457600080fd5b919050565b60006020828403121561066b57600080fd5b6102888261063d565b6000806040838503121561068757600080fd5b6106908361063d565b915061069e6020840161063d565b90509250929050565b6000806000606084860312156106bc57600080fd5b6106c58461063d565b92506106d36020850161063d565b9150604084013590509250925092565b600080604083850312156106f657600080fd5b6106ff8361063d565b946020939093013593505050565b60006020828403121561071f57600080fd5b8151801515811461028857600080fd5b60006020828403121561074157600080fd5b815167ffffffffffffffff8082111561075957600080fd5b818401915084601f83011261076d57600080fd5b81518181111561077f5761077f6108a1565b604051601f8201601f19908116603f011681019083821181831017156107a7576107a76108a1565b816040528281528760208487010111156107c057600080fd5b6107d1836020830160208801610871565b979650505050505050565b6000602082840312156107ee57600080fd5b5051919050565b60006020828403121561080757600080fd5b813563ffffffff8116811461028857600080fd5b60006020828403121561082d57600080fd5b815160ff8116811461028857600080fd5b602081526000825180602084015261085d816040850160208701610871565b601f01601f19169190910160400192915050565b60005b8381101561088c578181015183820152602001610874565b8381111561089b576000848401525b50505050565b634e487b7160e01b600052604160045260246000fdfea2646970667358221220c05e4cb7ab439b86c0b6ac8a84eec6a230b592fa63d1ab2e3a7f1474c27338f364736f6c63430008070033
\ No newline at end of file
addedjs-packages/tests/eth/proxy/UniqueFungibleProxy.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/UniqueFungibleProxy.sol
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: OTHER
+
+pragma solidity >=0.8.0 <0.9.0;
+
+import "../api/UniqueFungible.sol";
+
+contract UniqueFungibleProxy is UniqueFungible {
+    UniqueFungible proxied;
+
+    constructor(address _proxied) UniqueFungible() {
+        proxied = UniqueFungible(_proxied);
+    }
+
+    function name() external view override returns (string memory) {
+        return proxied.name();
+    }
+
+    function symbol() external view override returns (string memory) {
+        return proxied.symbol();
+    }
+
+    function totalSupply() external view override returns (uint256) {
+        return proxied.totalSupply();
+    }
+
+    function supportsInterface(uint32 interfaceId)
+        external
+        view
+        override
+        returns (bool)
+    {
+        return proxied.supportsInterface(interfaceId);
+    }
+
+    function decimals() external view override returns (uint8) {
+        return proxied.decimals();
+    }
+
+    function balanceOf(address owner) external view override returns (uint256) {
+        return proxied.balanceOf(owner);
+    }
+
+    function transfer(address to, uint256 amount)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.transfer(to, amount);
+    }
+
+    function transferFrom(
+        address from,
+        address to,
+        uint256 amount
+    ) external override returns (bool) {
+        return proxied.transferFrom(from, to, amount);
+    }
+
+    function approve(address spender, uint256 amount)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.approve(spender, amount);
+    }
+
+    function allowance(address owner, address spender)
+        external
+        view
+        override
+        returns (uint256)
+    {
+        return proxied.allowance(owner, spender);
+    }
+}
addedjs-packages/tests/eth/proxy/UniqueNFTProxy.abidiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/UniqueNFTProxy.abi
@@ -0,0 +1 @@
+[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[],"name":"MintingFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"approved","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"deleteProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finishMinting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"mintBulk","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"field_0","type":"uint256"},{"internalType":"string","name":"field_1","type":"string"}],"internalType":"struct Tuple0[]","name":"tokens","type":"tuple[]"}],"name":"mintBulkWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"tokenUri","type":"string"}],"name":"mintWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintingFinished","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFromWithData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"setProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
addedjs-packages/tests/eth/proxy/UniqueNFTProxy.bindiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/UniqueNFTProxy.bin
@@ -0,0 +1 @@
+608060405234801561001057600080fd5b506040516116bb3803806116bb83398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b611628806100936000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80634f6ccce7116100f957806379cc679011610097578063a22cb46511610071578063a22cb46514610399578063a9059cbb146103ac578063c87b56dd146103bf578063e985e9c5146103d257600080fd5b806379cc6790146103765780637d64bcb41461038957806395d89b411461039157600080fd5b806362d9491f116100d357806362d9491f146103355780636352211e1461034857806370a082311461035b57806375794a3c1461036e57600080fd5b80634f6ccce7146102fc57806350bb4e7f1461030f57806360a116721461032257600080fd5b80632f745c591161016657806340c10f191161014057806340c10f19146102b057806342842e0e146102c357806342966c68146102d657806344a9945e146102e957600080fd5b80632f745c5914610277578063342419141461028a578063365430061461029d57600080fd5b8063081812fc116101a2578063081812fc1461020e578063095ea7b31461023957806318160ddd1461024e57806323b872dd1461026457600080fd5b806301ffc9a7146101c957806305d2035b146101f157806306fdde03146101f9575b600080fd5b6101dc6101d7366004610dca565b6103e5565b60405190151581526020015b60405180910390f35b6101dc610462565b6102016104df565b6040516101e89190610e4c565b61022161021c366004610e5f565b610550565b6040516001600160a01b0390911681526020016101e8565b61024c610247366004610e90565b6105bf565b005b61025661062a565b6040519081526020016101e8565b61024c610272366004610ebc565b6106a2565b610256610285366004610e90565b610716565b61024c610298366004610ff3565b610793565b6101dc6102ab36600461104c565b6107f8565b6101dc6102be366004610e90565b61086e565b61024c6102d1366004610ebc565b6108a8565b61024c6102e4366004610e5f565b6108e9565b6101dc6102f736600461114f565b61091a565b61025661030a366004610e5f565b61094d565b6101dc61031d3660046111f5565b6109bc565b61024c61033036600461124e565b610a3c565b61024c6103433660046112ce565b610aab565b610221610356366004610e5f565b610add565b610256610369366004611332565b610b0f565b610256610b42565b61024c610384366004610e90565b610b96565b6101dc610bcf565b610201610c25565b61024c6103a736600461135d565b610c6e565b61024c6103ba366004610e90565b610ca8565b6102016103cd366004610e5f565b610ce1565b6102216103e0366004611396565b610d53565b600080546040516301ffc9a760e01b81526001600160e01b0319841660048201526001600160a01b03909116906301ffc9a790602401602060405180830381865afa158015610438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c91906113c4565b92915050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166305d2035b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da91906113c4565b905090565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde0392600480820193918290030181865afa158015610528573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104da91908101906113e1565b6000805460405163020604bf60e21b8152600481018490526001600160a01b039091169063081812fc906024015b602060405180830381865afa15801561059b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611458565b60005460405163095ea7b360e01b81526001600160a01b038481166004830152602482018490529091169063095ea7b3906044015b600060405180830381600087803b15801561060e57600080fd5b505af1158015610622573d6000803e3d6000fd5b505050505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da9190611475565b6000546040516323b872dd60e01b81526001600160a01b038581166004830152848116602483015260448201849052909116906323b872dd906064015b600060405180830381600087803b1580156106f957600080fd5b505af115801561070d573d6000803e3d6000fd5b50505050505050565b60008054604051632f745c5960e01b81526001600160a01b0385811660048301526024820185905290911690632f745c5990604401602060405180830381865afa158015610768573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611475565b9392505050565b600054604051630d09064560e21b81526001600160a01b03909116906334241914906107c3908490600401610e4c565b600060405180830381600087803b1580156107dd57600080fd5b505af11580156107f1573d6000803e3d6000fd5b5050505050565b60008054604051631b2a180360e11b81526001600160a01b039091169063365430069061082b908690869060040161148e565b6020604051808303816000875af115801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c91906113c4565b600080546040516340c10f1960e01b81526001600160a01b03858116600483015260248201859052909116906340c10f199060440161082b565b600054604051632142170760e11b81526001600160a01b038581166004830152848116602483015260448201849052909116906342842e0e906064016106df565b600054604051630852cd8d60e31b8152600481018390526001600160a01b03909116906342966c68906024016107c3565b60008054604051632254ca2f60e11b81526001600160a01b03909116906344a9945e9061082b9086908690600401611513565b60008054604051634f6ccce760e01b8152600481018490526001600160a01b0390911690634f6ccce7906024015b602060405180830381865afa158015610998573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611475565b600080546040516350bb4e7f60e01b81526001600160a01b03909116906350bb4e7f906109f190879087908790600401611569565b6020604051808303816000875af1158015610a10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3491906113c4565b949350505050565b6000546040516330508b3960e11b81526001600160a01b03909116906360a1167290610a72908790879087908790600401611590565b600060405180830381600087803b158015610a8c57600080fd5b505af1158015610aa0573d6000803e3d6000fd5b505050505b50505050565b6000546040516362d9491f60e01b81526001600160a01b03909116906362d9491f906105f490859085906004016115cd565b600080546040516331a9108f60e11b8152600481018490526001600160a01b0390911690636352211e9060240161057e565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240161097b565b60008060009054906101000a90046001600160a01b03166001600160a01b03166375794a3c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b60005460405163079cc67960e41b81526001600160a01b03848116600483015260248201849052909116906379cc6790906044016105f4565b60008060009054906101000a90046001600160a01b03166001600160a01b0316637d64bcb46040518163ffffffff1660e01b81526004016020604051808303816000875af11580156104b6573d6000803e3d6000fd5b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b4192600480820193918290030181865afa158015610528573d6000803e3d6000fd5b60005460405163a22cb46560e01b81526001600160a01b03848116600483015283151560248301529091169063a22cb465906044016105f4565b60005460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016105f4565b60005460405163c87b56dd60e01b8152600481018390526060916001600160a01b03169063c87b56dd90602401600060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261045c91908101906113e1565b6000805460405163e985e9c560e01b81526001600160a01b03858116600483015284811660248301529091169063e985e9c590604401602060405180830381865afa158015610da6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611458565b600060208284031215610ddc57600080fd5b81356001600160e01b03198116811461078c57600080fd5b60005b83811015610e0f578181015183820152602001610df7565b83811115610aa55750506000910152565b60008151808452610e38816020860160208601610df4565b601f01601f19169290920160200192915050565b60208152600061078c6020830184610e20565b600060208284031215610e7157600080fd5b5035919050565b6001600160a01b0381168114610e8d57600080fd5b50565b60008060408385031215610ea357600080fd5b8235610eae81610e78565b946020939093013593505050565b600080600060608486031215610ed157600080fd5b8335610edc81610e78565b92506020840135610eec81610e78565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610f3657610f36610efd565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610f6557610f65610efd565b604052919050565b600067ffffffffffffffff821115610f8757610f87610efd565b50601f01601f191660200190565b6000610fa8610fa384610f6d565b610f3c565b9050828152838383011115610fbc57600080fd5b828260208301376000602084830101529392505050565b600082601f830112610fe457600080fd5b61078c83833560208501610f95565b60006020828403121561100557600080fd5b813567ffffffffffffffff81111561101c57600080fd5b610a3484828501610fd3565b600067ffffffffffffffff82111561104257611042610efd565b5060051b60200190565b600080604080848603121561106057600080fd5b833561106b81610e78565b925060208481013567ffffffffffffffff8082111561108957600080fd5b818701915087601f83011261109d57600080fd5b81356110ab610fa382611028565b81815260059190911b8301840190848101908a8311156110ca57600080fd5b8585015b8381101561113d578035858111156110e65760008081fd5b8601808d03601f19018913156110fc5760008081fd5b611104610f13565b888201358152898201358781111561111c5760008081fd5b61112a8f8b83860101610fd3565b828b0152508452509186019186016110ce565b50809750505050505050509250929050565b6000806040838503121561116257600080fd5b823561116d81610e78565b915060208381013567ffffffffffffffff81111561118a57600080fd5b8401601f8101861361119b57600080fd5b80356111a9610fa382611028565b81815260059190911b820183019083810190888311156111c857600080fd5b928401925b828410156111e6578335825292840192908401906111cd565b80955050505050509250929050565b60008060006060848603121561120a57600080fd5b833561121581610e78565b925060208401359150604084013567ffffffffffffffff81111561123857600080fd5b61124486828701610fd3565b9150509250925092565b6000806000806080858703121561126457600080fd5b843561126f81610e78565b9350602085013561127f81610e78565b925060408501359150606085013567ffffffffffffffff8111156112a257600080fd5b8501601f810187136112b357600080fd5b6112c287823560208401610f95565b91505092959194509250565b600080604083850312156112e157600080fd5b823567ffffffffffffffff808211156112f957600080fd5b61130586838701610fd3565b9350602085013591508082111561131b57600080fd5b5061132885828601610fd3565b9150509250929050565b60006020828403121561134457600080fd5b813561078c81610e78565b8015158114610e8d57600080fd5b6000806040838503121561137057600080fd5b823561137b81610e78565b9150602083013561138b8161134f565b809150509250929050565b600080604083850312156113a957600080fd5b82356113b481610e78565b9150602083013561138b81610e78565b6000602082840312156113d657600080fd5b815161078c8161134f565b6000602082840312156113f357600080fd5b815167ffffffffffffffff81111561140a57600080fd5b8201601f8101841361141b57600080fd5b8051611429610fa382610f6d565b81815285602083850101111561143e57600080fd5b61144f826020830160208601610df4565b95945050505050565b60006020828403121561146a57600080fd5b815161078c81610e78565b60006020828403121561148757600080fd5b5051919050565b6001600160a01b0383168152604060208083018290528351828401819052600092916060600583901b860181019290860190878301865b8281101561150457888603605f190184528151805187528501518587018890526114f188880182610e20565b96505092840192908401906001016114c5565b50939998505050505050505050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b8181101561155c57845183529383019391830191600101611540565b5090979650505050505050565b60018060a01b038416815282602082015260606040820152600061144f6060830184610e20565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906115c390830184610e20565b9695505050505050565b6040815260006115e06040830185610e20565b828103602084015261144f8185610e2056fea26469706673582212205bdf4275f4b714a1029ccfe77c0f9a0e20e121a932e1e5840cace97dfa886da964736f6c634300080d0033
\ No newline at end of file
addedjs-packages/tests/eth/proxy/UniqueNFTProxy.soldiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/UniqueNFTProxy.sol
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: OTHER
+
+pragma solidity >=0.8.0 <0.9.0;
+
+import "../api/UniqueNFT.sol";
+
+contract UniqueNFTProxy is UniqueNFT {
+    UniqueNFT proxied;
+
+    constructor(address _proxied) UniqueNFT() {
+        proxied = UniqueNFT(_proxied);
+    }
+
+    function name() external view override returns (string memory) {
+        return proxied.name();
+    }
+
+    function symbol() external view override returns (string memory) {
+        return proxied.symbol();
+    }
+
+    function totalSupply() external view override returns (uint256) {
+        return proxied.totalSupply();
+    }
+
+    function balanceOf(address owner) external view override returns (uint256) {
+        return proxied.balanceOf(owner);
+    }
+
+    function ownerOf(uint256 tokenId) external view override returns (address) {
+        return proxied.ownerOf(tokenId);
+    }
+
+    function safeTransferFromWithData(
+        address from,
+        address to,
+        uint256 tokenId,
+        bytes memory data
+    ) external override {
+        return proxied.safeTransferFromWithData(from, to, tokenId, data);
+    }
+
+    function safeTransferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external override {
+        return proxied.safeTransferFrom(from, to, tokenId);
+    }
+
+    function transferFrom(
+        address from,
+        address to,
+        uint256 tokenId
+    ) external override {
+        return proxied.transferFrom(from, to, tokenId);
+    }
+
+    function approve(address approved, uint256 tokenId) external override {
+        return proxied.approve(approved, tokenId);
+    }
+
+    function setApprovalForAll(address operator, bool approved)
+        external
+        override
+    {
+        return proxied.setApprovalForAll(operator, approved);
+    }
+
+    function getApproved(uint256 tokenId)
+        external
+        view
+        override
+        returns (address)
+    {
+        return proxied.getApproved(tokenId);
+    }
+
+    function isApprovedForAll(address owner, address operator)
+        external
+        view
+        override
+        returns (address)
+    {
+        return proxied.isApprovedForAll(owner, operator);
+    }
+
+    function burn(uint256 tokenId) external override {
+        return proxied.burn(tokenId);
+    }
+
+    function tokenByIndex(uint256 index)
+        external
+        view
+        override
+        returns (uint256)
+    {
+        return proxied.tokenByIndex(index);
+    }
+
+    function tokenOfOwnerByIndex(address owner, uint256 index)
+        external
+        view
+        override
+        returns (uint256)
+    {
+        return proxied.tokenOfOwnerByIndex(owner, index);
+    }
+
+    function tokenURI(uint256 tokenId)
+        external
+        view
+        override
+        returns (string memory)
+    {
+        return proxied.tokenURI(tokenId);
+    }
+
+    function mintingFinished() external view override returns (bool) {
+        return proxied.mintingFinished();
+    }
+
+    function mint(address to)
+        external
+        override
+        returns (uint256)
+    {
+        return proxied.mint(to);
+    }
+
+    function mintWithTokenURI(
+        address to,
+        string memory tokenUri
+    ) external override returns (uint256) {
+        return proxied.mintWithTokenURI(to, tokenUri);
+    }
+
+    function finishMinting() external override returns (bool) {
+        return proxied.finishMinting();
+    }
+
+    function transfer(address to, uint256 tokenId) external override {
+        return proxied.transfer(to, tokenId);
+    }
+
+    function nextTokenId() external view override returns (uint256) {
+        return proxied.nextTokenId();
+    }
+
+    function burnFrom(address from, uint256 tokenId) external override {
+        return proxied.burnFrom(from, tokenId);
+    }
+
+    function supportsInterface(bytes4 interfaceId)
+        external
+        view
+        override
+        returns (bool)
+    {
+        return proxied.supportsInterface(interfaceId);
+    }
+
+    function mintBulk(address to, uint256[] memory tokenIds)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.mintBulk(to, tokenIds);
+    }
+
+    function mintBulkWithTokenURI(address to, TokenUri[] memory tokens)
+        external
+        override
+        returns (bool)
+    {
+        return proxied.mintBulkWithTokenURI(to, tokens);
+    }
+
+    function setProperty(string memory key, string memory value) external override {
+        return proxied.setProperty(key, value);
+    }
+
+	function deleteProperty(string memory key) external override {
+        return proxied.deleteProperty(key);
+    }
+}
addedjs-packages/tests/eth/proxy/fungibleProxy.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/fungibleProxy.test.ts
@@ -0,0 +1,207 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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} from 'chai';
+import {readFile} from 'fs/promises';
+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';
+
+const {dirname} = makeNames(import.meta.url);
+
+async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) {
+  // Proxy owner has no special privilegies, we don't need to reuse them
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const web3 = helper.getWeb3();
+  const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueFungibleProxy.abi`)).toString()), undefined, {
+    from: owner,
+    gas: helper.eth.DEFAULT_GAS,
+  });
+  const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueFungibleProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner});
+  return proxy;
+}
+
+describe('Fungible (Via EVM proxy): Information getting', () => {
+  let alice: IKeyringPair;
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itEth('totalSupply', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    await collection.mint(alice, 200n, {Substrate: alice.address});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const totalSupply = await contract.methods.totalSupply().call();
+
+    expect(totalSupply).to.equal('200');
+  });
+
+  itEth('balanceOf', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+
+    await collection.mint(alice, 200n, {Ethereum: caller});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const balance = await contract.methods.balanceOf(caller).call();
+
+    expect(balance).to.equal('200');
+  });
+});
+
+describe('Fungible (Via EVM proxy): Plain calls', () => {
+  let alice: IKeyringPair;
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itEth('Can perform approve()', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    await collection.mint(alice, 200n, {Ethereum: contract.options.address});
+
+    {
+      const result = await contract.methods.approve(spender, 100).send({from: caller});
+      const events = helper.eth.normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Approval',
+          args: {
+            owner: contract.options.address,
+            spender,
+            value: '100',
+          },
+        },
+      ]);
+    }
+
+    {
+      const allowance = await contract.methods.allowance(contract.options.address, spender).call();
+      expect(+allowance).to.equal(100);
+    }
+  });
+
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    await collection.mint(alice, 200n, {Ethereum: owner});
+    const receiver = helper.eth.createAccount();
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+
+    await evmCollection.methods.approve(contract.options.address, 100).send({from: owner});
+
+    {
+      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: caller});
+      const events = helper.eth.normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: owner,
+            to: receiver,
+            value: '49',
+          },
+        },
+        {
+          address,
+          event: 'Approval',
+          args: {
+            owner,
+            spender: contract.options.address,
+            value: '51',
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(49);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(owner).call();
+      expect(+balance).to.equal(151);
+    }
+  });
+
+  itEth('Can perform transfer()', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    await collection.mint(alice, 200n, {Ethereum: contract.options.address});
+
+    {
+      const result = await contract.methods.transfer(receiver, 50).send({from: caller});
+      const events = helper.eth.normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: contract.options.address,
+            to: receiver,
+            value: '50',
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(contract.options.address).call();
+      expect(+balance).to.equal(150);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(50);
+    }
+  });
+});
addedjs-packages/tests/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxy/nonFungibleProxy.test.ts
@@ -0,0 +1,372 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {readFile} from 'fs/promises';
+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';
+
+const {dirname} = makeNames(import.meta.url);
+
+
+async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) {
+  // Proxy owner has no special privilegies, we don't need to reuse them
+  const owner = await helper.eth.createAccountWithBalance(donor);
+  const web3 = helper.getWeb3();
+  const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueNFTProxy.abi`)).toString()), undefined, {
+    from: owner,
+    gas: helper.eth.DEFAULT_GAS,
+  });
+  const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueNFTProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner});
+  return proxy;
+}
+
+describe('NFT (Via EVM proxy): Information getting', () => {
+  let alice: IKeyringPair;
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itEth('totalSupply', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    await collection.mintToken(alice, {Substrate: alice.address});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const totalSupply = await contract.methods.totalSupply().call();
+
+    expect(totalSupply).to.equal('1');
+  });
+
+  itEth('balanceOf', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    await collection.mintMultipleTokens(alice, [
+      {owner: {Ethereum: caller}},
+      {owner: {Ethereum: caller}},
+      {owner: {Ethereum: caller}},
+    ]);
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const balance = await contract.methods.balanceOf(caller).call();
+
+    expect(balance).to.equal('3');
+  });
+
+  itEth('ownerOf', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const owner = await contract.methods.ownerOf(tokenId).call();
+
+    expect(owner).to.equal(caller);
+  });
+});
+
+describe('NFT (Via EVM proxy): Plain calls', () => {
+  let alice: IKeyringPair;
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  // Soft-deprecated
+  itEth('[eth] Can perform mint()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', '');
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true);
+    const contract = await proxyWrap(helper, collectionEvm, donor);
+    await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+      expect(tokenId).to.be.equal('1');
+
+      const event = helper.eth.normalizeEvents(result.events)
+        .find(event => event.event === 'Transfer')!;
+      event.address = event.address.toLocaleLowerCase();
+
+      expect(event).to.be.deep.equal({
+        address: collectionAddress.toLocaleLowerCase(),
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: receiver,
+          tokenId,
+        },
+      });
+
+      expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+    }
+  });
+
+  itEth('[cross] Can perform mint()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', '');
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
+    const contract = await proxyWrap(helper, collectionEvm, donor);
+    const contractAddressCross = helper.ethCrossAccount.fromAddress(contract.options.address);
+    await collectionEvmOwned.methods.addCollectionAdminCross(contractAddressCross).send();
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+      expect(tokenId).to.be.equal('1');
+
+      const event = helper.eth.normalizeEvents(result.events)
+        .find(event => event.event === 'Transfer')!;
+      event.address = event.address.toLocaleLowerCase();
+
+      expect(event).to.be.deep.equal({
+        address: collectionAddress.toLocaleLowerCase(),
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: receiver,
+          tokenId,
+        },
+      });
+
+      expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+    }
+  });
+
+  //TODO: CORE-302 add eth methods
+  itEth.skip('Can perform mintBulk()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(donor, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
+
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    await collection.addAdmin(donor, {Ethereum: contract.options.address});
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintBulkWithTokenURI(
+        receiver,
+        [
+          [nextTokenId, 'Test URI 0'],
+          [+nextTokenId + 1, 'Test URI 1'],
+          [+nextTokenId + 2, 'Test URI 2'],
+        ],
+      ).send({from: caller});
+      const events = helper.eth.normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: nextTokenId,
+          },
+        },
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: String(+nextTokenId + 1),
+          },
+        },
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: receiver,
+            tokenId: String(+nextTokenId + 2),
+          },
+        },
+      ]);
+
+      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
+      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
+      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
+    }
+  });
+
+  itEth('Can perform burn()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const caller = await helper.eth.createAccountWithBalance(donor);
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});
+    await collection.addAdmin(alice, {Ethereum: contract.options.address});
+
+    {
+      const result = await contract.methods.burn(tokenId).send({from: caller});
+      const events = helper.eth.normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: contract.options.address,
+            to: '0x0000000000000000000000000000000000000000',
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+  });
+
+  itEth('Can perform approve()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});
+
+    {
+      const result = await contract.methods.approve(spender, tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS});
+      const events = helper.eth.normalizeEvents(result.events);
+
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Approval',
+          args: {
+            owner: contract.options.address,
+            approved: spender,
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+  });
+
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const receiver = helper.eth.createAccount();
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
+
+    await evmCollection.methods.approve(contract.options.address, tokenId).send({from: owner});
+
+    {
+      const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: caller});
+      const events = helper.eth.normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: owner,
+            to: receiver,
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(contract.options.address).call();
+      expect(+balance).to.equal(0);
+    }
+  });
+
+  itEth('Can perform transfer()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
+    const contract = await proxyWrap(helper, evmCollection, donor);
+    const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});
+
+    {
+      const result = await contract.methods.transfer(receiver, tokenId).send({from: caller});
+      const events = helper.eth.normalizeEvents(result.events);
+      expect(events).to.be.deep.equal([
+        {
+          address,
+          event: 'Transfer',
+          args: {
+            from: contract.options.address,
+            to: receiver,
+            tokenId: tokenId.toString(),
+          },
+        },
+      ]);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(contract.options.address).call();
+      expect(+balance).to.equal(0);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+  });
+});
addedjs-packages/tests/eth/proxyContract.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/proxyContract.test.ts
@@ -0,0 +1,153 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+
+import {itEth, expect, usingEthPlaygrounds} from './util/index.js';
+import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
+
+describe('EVM payable contracts', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Update proxy contract', async({helper}) => {
+    const deployer = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const proxyContract = await deployProxyContract(helper, deployer);
+
+    const realContractV1 = await deployRealContractV1(helper, deployer);
+    const realContractV1proxy = new helper.web3!.eth.Contract(realContractV1.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS});
+    await proxyContract.methods.updateVersion(realContractV1.options.address).send();
+
+    await realContractV1proxy.methods.flip().send();
+    await realContractV1proxy.methods.flip().send();
+    await realContractV1proxy.methods.flip().send();
+    const value1 = await realContractV1proxy.methods.getValue().call();
+    const flipCount1 = await realContractV1proxy.methods.getFlipCount().call();
+    expect(flipCount1).to.be.equal('3');
+    expect(value1).to.be.equal(true);
+
+    const realContractV2 = await deployRealContractV2(helper, deployer);
+    const realContractV2proxy = new helper.web3!.eth.Contract(realContractV2.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS});
+    await proxyContract.methods.updateVersion(realContractV2.options.address).send();
+
+    await realContractV2proxy.methods.flip().send();
+    await realContractV2proxy.methods.flip().send();
+    await realContractV2proxy.methods.setStep(5).send();
+    await realContractV2proxy.methods.increaseFlipCount().send();
+    const value2 = await realContractV2proxy.methods.getValue().call();
+    const flipCount2 = await realContractV2proxy.methods.getFlipCount().call();
+    expect(value2).to.be.equal(true);
+    expect(flipCount2).to.be.equal('6');
+  });
+
+  async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {
+    return await helper.ethContract.deployByCode(deployer, 'ProxyContract', `
+      // SPDX-License-Identifier: UNLICENSED
+      pragma solidity ^0.8.6;
+      
+      contract ProxyContract {
+        event NewEvent(uint data);
+        receive() external payable {}
+        bytes32 private constant implementationSlot =  bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1);
+        constructor() {}
+        function updateVersion(address newContractAddress) external {
+          bytes32 slot = implementationSlot;
+          assembly {
+            sstore(slot, newContractAddress)
+          }
+        }
+        fallback() external {
+          bytes32 slot = implementationSlot;
+          assembly {
+            let ptr := mload(0x40)
+            let contractAddress := sload(slot)
+            
+            calldatacopy(ptr, 0, calldatasize())
+            
+            let result := delegatecall(gas(), contractAddress, ptr, calldatasize(), 0, 0)
+            let size := returndatasize()
+            
+            returndatacopy(ptr, 0, size)
+            
+            switch result
+            case 0 { revert(ptr, size) }
+            default { return(ptr, size) }
+          }
+        }
+      }
+      
+      interface RealContract {
+        function flip() external;
+        function getValue() external view returns (bool);
+        function getFlipCount() external view returns (uint);
+      }`);
+  }
+
+  async function deployRealContractV1(helper: EthUniqueHelper, deployer: string) {
+    return await helper.ethContract.deployByCode(deployer, 'RealContractV1', `
+      // SPDX-License-Identifier: UNLICENSED
+      pragma solidity ^0.8.6;
+  
+      contract RealContractV1 {
+        bool value = false;
+        uint flipCount = 0;
+        function flip() external {
+          value = !value;
+          flipCount++;
+        }
+        function getValue() external view returns (bool) {
+          return value;
+        }
+        function getFlipCount() external view returns (uint) {
+          return flipCount;
+        }
+      }`);
+  }
+
+  async function deployRealContractV2(helper: EthUniqueHelper, deployer: string) {
+    return await helper.ethContract.deployByCode(deployer, 'RealContractV2', `
+      // SPDX-License-Identifier: UNLICENSED
+      pragma solidity ^0.8.6;
+  
+      contract RealContractV2 {
+        bool value = false;
+        uint flipCount = 10;
+        uint step = 1;
+        function flip() external {
+          value = !value;
+          flipCount--;
+        }
+        function setStep(uint value) external {
+          step = value;
+        }
+        function increaseFlipCount() external {
+          flipCount = flipCount + step;
+        }
+        function getValue() external view returns (bool) {
+          return value;
+        }
+        function getFlipCount() external view returns (uint) {
+          return flipCount;
+        }
+      }`);
+  }
+});
addedjs-packages/tests/eth/reFungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/reFungible.test.ts
@@ -0,0 +1,1019 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import type {ITokenPropertyPermission} from '@unique/playgrounds/types.js';
+import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js';
+
+describe('Refungible: Plain calls', () => {
+  let donor: IKeyringPair;
+  let minter: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  [
+    'substrate' as const,
+    'ethereum' as const,
+  ].map(testCase => {
+    itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
+      const collectionAdmin = await helper.eth.createAccountWithBalance(donor);
+
+      const receiverEth = helper.eth.createAccount();
+      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+      const receiverSub = bob;
+      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
+
+      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
+      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {
+        tokenOwner: false,
+        collectionAdmin: true,
+        mutable: false}}));
+
+
+      const collection = await helper.rft.mintCollection(minter, {
+        tokenPrefix: 'ethp',
+        tokenPropertyPermissions: permissions,
+      });
+      await collection.addAdmin(minter, {Ethereum: collectionAdmin});
+
+      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', collectionAdmin, true);
+      let expectedTokenId = await contract.methods.nextTokenId().call();
+      let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();
+      let tokenId = result.events.Transfer.returnValues.tokenId;
+      expect(tokenId).to.be.equal(expectedTokenId);
+
+      let event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
+      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+
+      expectedTokenId = await contract.methods.nextTokenId().call();
+      result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();
+      event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
+      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
+
+      tokenId = result.events.Transfer.returnValues.tokenId;
+
+      expect(tokenId).to.be.equal(expectedTokenId);
+
+      expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
+        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
+
+      expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
+        .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
+    });
+  });
+
+  itEth.skip('Can perform mintBulk()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', '');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+    {
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await contract.methods.mintBulkWithTokenURI(
+        receiver,
+        [
+          [nextTokenId, 'Test URI 0'],
+          [+nextTokenId + 1, 'Test URI 1'],
+          [+nextTokenId + 2, 'Test URI 2'],
+        ],
+      ).send();
+
+      const events = result.events.Transfer;
+      for(let i = 0; i < 2; i++) {
+        const event = events[i];
+        expect(event.address).to.equal(collectionAddress);
+        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+        expect(event.returnValues.to).to.equal(receiver);
+        expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));
+      }
+
+      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
+      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
+      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
+    }
+  });
+
+  itEth('Can perform mintBulkCross() with multiple tokens', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const callerCross = helper.ethCrossAccount.fromAddress(caller);
+    const receiver = helper.eth.createAccount();
+    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
+
+    const permissions = [
+      {code: TokenPermissionField.Mutable, value: true},
+      {code: TokenPermissionField.TokenOwner, value: true},
+      {code: TokenPermissionField.CollectionAdmin, value: true},
+    ];
+    const {collectionAddress} = await helper.eth.createCollection(
+      caller,
+      {
+        ...CREATE_COLLECTION_DATA_DEFAULTS,
+        name: 'A',
+        description: 'B',
+        tokenPrefix: 'C',
+        collectionMode: 'rft',
+        adminList: [callerCross],
+        tokenPropertyPermissions: [
+          {key: 'key_0_0', permissions},
+          {key: 'key_1_0', permissions},
+          {key: 'key_1_1', permissions},
+          {key: 'key_2_0', permissions},
+          {key: 'key_2_1', permissions},
+          {key: 'key_2_2', permissions},
+        ],
+      },
+    ).send();
+
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+    const nextTokenId = await contract.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+    const result = await contract.methods.mintBulkCross([
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 1,
+        }],
+        properties: [
+          {key: 'key_0_0', value: Buffer.from('value_0_0')},
+        ],
+      },
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 2,
+        }],
+        properties: [
+          {key: 'key_1_0', value: Buffer.from('value_1_0')},
+          {key: 'key_1_1', value: Buffer.from('value_1_1')},
+        ],
+      },
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 1,
+        }],
+        properties: [
+          {key: 'key_2_0', value: Buffer.from('value_2_0')},
+          {key: 'key_2_1', value: Buffer.from('value_2_1')},
+          {key: 'key_2_2', value: Buffer.from('value_2_2')},
+        ],
+      },
+    ]).send({from: caller});
+    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
+    const bulkSize = 3;
+    for(let i = 0; i < bulkSize; i++) {
+      const event = events[i];
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.equal(receiver);
+      expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
+    }
+
+    const properties = [
+      await contract.methods.properties(+nextTokenId, []).call(),
+      await contract.methods.properties(+nextTokenId + 1, []).call(),
+      await contract.methods.properties(+nextTokenId + 2, []).call(),
+    ];
+    expect(properties).to.be.deep.equal([
+      [
+        ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],
+      ],
+      [
+        ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],
+        ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],
+      ],
+      [
+        ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
+        ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
+        ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
+      ],
+    ]);
+  });
+
+  itEth('Can perform mintBulkCross() with multiple owners', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const callerCross = helper.ethCrossAccount.fromAddress(caller);
+    const receiver = helper.eth.createAccount();
+    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
+    const receiver2 = helper.eth.createAccount();
+    const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);
+
+    const permissions = [
+      {code: TokenPermissionField.Mutable, value: true},
+      {code: TokenPermissionField.TokenOwner, value: true},
+      {code: TokenPermissionField.CollectionAdmin, value: true},
+    ];
+    const {collectionAddress} = await helper.eth.createCollection(
+      caller,
+      {
+        ...CREATE_COLLECTION_DATA_DEFAULTS,
+        name: 'A',
+        description: 'B',
+        tokenPrefix: 'C',
+        collectionMode: 'rft',
+        adminList: [callerCross],
+        tokenPropertyPermissions: [
+          {key: 'key_2_0', permissions},
+          {key: 'key_2_1', permissions},
+          {key: 'key_2_2', permissions},
+        ],
+      },
+    ).send();
+
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+    const nextTokenId = await contract.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+    const result = await contract.methods.mintBulkCross([{
+      owners: [
+        {
+          owner: receiverCross,
+          pieces: 1,
+        },
+        {
+          owner: receiver2Cross,
+          pieces: 2,
+        },
+      ],
+      properties: [
+        {key: 'key_2_0', value: Buffer.from('value_2_0')},
+        {key: 'key_2_1', value: Buffer.from('value_2_1')},
+        {key: 'key_2_2', value: Buffer.from('value_2_2')},
+      ],
+    }]).send({from: caller});
+    const event = result.events.Transfer;
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+    expect(event.returnValues.tokenId).to.equal(`${+nextTokenId}`);
+
+    const properties = [
+      await contract.methods.properties(+nextTokenId, []).call(),
+    ];
+    expect(properties).to.be.deep.equal([[
+      ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
+      ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
+      ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
+    ]]);
+  });
+
+  itEth('Can perform setApprovalForAll()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const operator = helper.eth.createAccount();
+
+    const collection = await helper.rft.mintCollection(minter, {});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+    const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();
+    expect(approvedBefore).to.be.equal(false);
+
+    {
+      const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+
+      expect(result.events.ApprovalForAll).to.be.like({
+        address: collectionAddress,
+        event: 'ApprovalForAll',
+        returnValues: {
+          owner,
+          operator,
+          approved: true,
+        },
+      });
+
+      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+      expect(approvedAfter).to.be.equal(true);
+    }
+
+    {
+      const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});
+
+      expect(result.events.ApprovalForAll).to.be.like({
+        address: collectionAddress,
+        event: 'ApprovalForAll',
+        returnValues: {
+          owner,
+          operator,
+          approved: false,
+        },
+      });
+
+      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
+      expect(approvedAfter).to.be.equal(false);
+    }
+  });
+
+  itEth('Can perform burn with ApprovalForAll', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const operator = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft');
+
+    {
+      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});
+      const events = result.events.Transfer;
+
+      expect(events).to.be.like({
+        address,
+        event: 'Transfer',
+        returnValues: {
+          from: owner,
+          to: '0x0000000000000000000000000000000000000000',
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+  });
+
+  itEth('Can perform burn with approve and approvalForAll', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const operator = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft');
+
+    const rftToken = await helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner, true);
+
+    {
+      await rftToken.methods.approve(operator, 15n).send({from: owner});
+      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+      await rftToken.methods.burnFrom(owner, 10n).send({from: operator});
+    }
+    {
+      const allowance = await rftToken.methods.allowance(owner, operator).call();
+      expect(+allowance).to.be.equal(5);
+    }
+    {
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const operatorCross = helper.ethCrossAccount.fromAddress(operator);
+      const allowance = await rftToken.methods.allowanceCross(ownerCross, operatorCross).call();
+      expect(+allowance).to.equal(5);
+    }
+  });
+
+  itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const operator = await helper.eth.createAccountWithBalance(donor);
+    const receiver = charlie;
+
+    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft');
+
+    {
+      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: owner,
+          to: helper.address.substrateToEth(receiver.address),
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);
+  });
+
+  itEth('Can perform burn()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+    {
+      const result = await contract.methods.burn(tokenId).send();
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
+    }
+  });
+
+  itEth('Can perform transferFrom()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
+
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller);
+    await tokenContract.methods.repartition(15).send();
+
+    {
+      const tokenEvents: any = [];
+      tokenContract.events.allEvents((_: any, event: any) => {
+        tokenEvents.push(event);
+      });
+      const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();
+      if(tokenEvents.length == 0) await helper.wait.newBlocks(1);
+
+      let event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal(receiver);
+      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
+
+      event = tokenEvents[0];
+      expect(event.address).to.equal(tokenAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal(receiver);
+      expect(event.returnValues.value).to.equal('15');
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(caller).call();
+      expect(+balance).to.equal(0);
+    }
+  });
+
+  // Soft-deprecated
+  itEth('Can perform burnFrom()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft', spender, true);
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+    await tokenContract.methods.repartition(15).send();
+    await tokenContract.methods.approve(spender, 15).send();
+
+    {
+      const result = await contract.methods.burnFrom(owner, token.tokenId).send();
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: owner,
+          to: '0x0000000000000000000000000000000000000000',
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);
+  });
+
+  itEth('Can perform burnFromCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = bob;
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft');
+
+    await token.repartition(owner, 15n);
+    await token.approve(owner, {Ethereum: spender}, 15n);
+
+    {
+      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: '0x0000000000000000000000000000000000000000',
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);
+  });
+
+  itEth('Can perform transferFromCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = bob;
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const receiver = charlie;
+
+    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft');
+
+    await token.repartition(owner, 15n);
+    await token.approve(owner, {Ethereum: spender}, 15n);
+
+    {
+      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
+      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
+      const event = result.events.Transfer;
+      expect(event).to.be.like({
+        address: helper.ethAddress.fromCollectionId(collection.collectionId),
+        event: 'Transfer',
+        returnValues: {
+          from: helper.address.substrateToEth(owner.address),
+          to: helper.address.substrateToEth(receiver.address),
+          tokenId: token.tokenId.toString(),
+        },
+      });
+    }
+
+    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);
+  });
+
+  itEth('Can perform transfer()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+
+    {
+      const result = await contract.methods.transfer(receiver, tokenId).send();
+
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal(caller);
+      expect(event.returnValues.to).to.equal(receiver);
+      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(caller).call();
+      expect(+balance).to.equal(0);
+    }
+
+    {
+      const balance = await contract.methods.balanceOf(receiver).call();
+      expect(+balance).to.equal(1);
+    }
+  });
+
+  itEth('Can perform transferCross()', async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+    const receiverEth = await helper.eth.createAccountWithBalance(donor);
+    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
+
+    const collection = await helper.rft.mintCollection(minter, {});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender);
+
+    const token = await collection.mintToken(minter, 50n, {Ethereum: sender});
+
+    {
+      // Can transferCross to ethereum address:
+      const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender});
+      // Check events:
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal(sender);
+      expect(event.returnValues.to).to.equal(receiverEth);
+      expect(event.returnValues.tokenId).to.equal(token.tokenId.toString());
+      // Sender's balance decreased:
+      const senderBalance = await collectionEvm.methods.balanceOf(sender).call();
+      expect(+senderBalance).to.equal(0);
+      expect(await token.getBalance({Ethereum: sender})).to.eq(0n);
+      // Receiver's balance increased:
+      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
+      expect(+receiverBalance).to.equal(1);
+      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n);
+    }
+
+    {
+      // Can transferCross to substrate address:
+      const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth});
+      // Check events:
+      const event = substrateResult.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal(receiverEth);
+      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));
+      expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
+      // Sender's balance decreased:
+      const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
+      expect(+senderBalance).to.equal(0);
+      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n);
+      // Receiver's balance increased:
+      const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});
+      expect(receiverBalance).to.contain(token.tokenId);
+      expect(await token.getBalance({Substrate: minter.address})).to.eq(50n);
+    }
+  });
+
+  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {
+    const sender = await helper.eth.createAccountWithBalance(donor);
+    const tokenOwner = await helper.eth.createAccountWithBalance(donor);
+    const receiverSub = minter;
+    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
+
+    const collection = await helper.rft.mintCollection(minter, {});
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender);
+
+    await collection.mintToken(minter, 50n, {Ethereum: sender});
+    const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner});
+
+    // Cannot transferCross someone else's token:
+    const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;
+    await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;
+    // Cannot transfer token if it does not exist:
+    await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;
+  }));
+
+  itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+
+    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
+
+    await tokenContract.methods.repartition(2).send();
+    await tokenContract.methods.transfer(receiver, 1).send();
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await tokenContract.methods.transfer(receiver, 1).send();
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+    expect(event.returnValues.to).to.equal(receiver);
+    expect(event.returnValues.tokenId).to.equal(tokenId.toString());
+  });
+
+  itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+
+    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
+
+    await tokenContract.methods.repartition(2).send();
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    await tokenContract.methods.transfer(receiver, 1).send();
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal(caller);
+    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+    expect(event.returnValues.tokenId).to.equal(tokenId.toString());
+  });
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+
+    for(let i = 1n; i < 10n; i++) {
+      await collection.mintToken(minter, 100n, {Ethereum: owner.eth});
+      expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
+    }
+  });
+
+  itEth('Check ownerOfCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {});
+    let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+    const {tokenId} = await collection.mintToken(minter, 100n,{Ethereum: owner.eth});
+
+    for(let i = 1n; i < 10n; i++) {
+      const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+      expect(ownerCross.eth).to.be.eq(owner.eth);
+      expect(ownerCross.sub).to.be.eq(owner.sub);
+
+      const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+      await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
+      owner = newOwner;
+    }
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth, true);
+    const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    await tokenContract.methods.transferCross(newOwner, 50).send({from: owner.eth});
+    const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+    expect(ownerCross.eth.toUpperCase()).to.be.eq('0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
+    expect(ownerCross.sub).to.be.eq('0');
+  });
+});
+
+describe('RFT: Fees', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+
+    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+    expect(cost > 0n);
+  });
+
+  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+
+    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+    expect(cost > 0n);
+  });
+});
+
+describe('Common metadata', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([1000n], donor);
+    });
+  });
+
+  itEth('Returns collection name', async ({helper}) => {
+    const caller = helper.eth.createAccount();
+    const tokenPropertyPermissions = [{
+      key: 'URI',
+      permission: {
+        mutable: true,
+        collectionAdmin: true,
+        tokenOwner: false,
+      },
+    }];
+    const collection = await helper.rft.mintCollection(
+      alice,
+      {
+        name: 'Leviathan',
+        tokenPrefix: '11',
+        properties: [{key: 'ERC721Metadata', value: '1'}],
+        tokenPropertyPermissions,
+      },
+    );
+
+    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);
+    const name = await contract.methods.name().call();
+    expect(name).to.equal('Leviathan');
+  });
+
+  itEth('Returns symbol name', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const tokenPropertyPermissions = [{
+      key: 'URI',
+      permission: {
+        mutable: true,
+        collectionAdmin: true,
+        tokenOwner: false,
+      },
+    }];
+    const {collectionId} = await helper.rft.mintCollection(
+      alice,
+      {
+        name: 'Leviathan',
+        tokenPrefix: '12',
+        properties: [{key: 'ERC721Metadata', value: '1'}],
+        tokenPropertyPermissions,
+      },
+    );
+
+    const contract = await helper.ethNativeContract.collectionById(collectionId, 'rft', caller);
+    const symbol = await contract.methods.symbol().call();
+    expect(symbol).to.equal('12');
+  });
+});
+
+describe('Negative tests', () => {
+  let donor: IKeyringPair;
+  let minter: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itEth('[negative] Cant perform burn without approval', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft');
+
+    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+
+    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
+
+    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
+    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
+
+    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
+  });
+
+  itEth('[negative] Cant perform transfer without approval', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = alice;
+
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'rft');
+
+    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+    const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
+
+    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
+
+    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
+    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
+
+    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
+  });
+
+  itEth('[negative] Can perform mintBulkCross() with multiple owners and multiple tokens', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const callerCross = helper.ethCrossAccount.fromAddress(caller);
+    const receiver = helper.eth.createAccount();
+    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
+    const receiver2 = helper.eth.createAccount();
+    const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);
+
+    const permissions = [
+      {code: TokenPermissionField.Mutable, value: true},
+      {code: TokenPermissionField.TokenOwner, value: true},
+      {code: TokenPermissionField.CollectionAdmin, value: true},
+    ];
+    const {collectionAddress} = await helper.eth.createCollection(
+      caller,
+      {
+        ...CREATE_COLLECTION_DATA_DEFAULTS,
+        name: 'A',
+        description: 'B',
+        tokenPrefix: 'C',
+        collectionMode: 'rft',
+        adminList: [callerCross],
+        tokenPropertyPermissions: [
+          {key: 'key_0_0', permissions},
+          {key: 'key_2_0', permissions},
+          {key: 'key_2_1', permissions},
+          {key: 'key_2_2', permissions},
+        ],
+      },
+    ).send();
+
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+    const nextTokenId = await contract.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+    const createData = [
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 1,
+        }],
+        properties: [
+          {key: 'key_0_0', value: Buffer.from('value_0_0')},
+        ],
+      },
+      {
+        owners: [
+          {
+            owner: receiverCross,
+            pieces: 1,
+          },
+          {
+            owner: receiver2Cross,
+            pieces: 2,
+          },
+        ],
+        properties: [
+          {key: 'key_2_0', value: Buffer.from('value_2_0')},
+          {key: 'key_2_1', value: Buffer.from('value_2_1')},
+          {key: 'key_2_2', value: Buffer.from('value_2_2')},
+        ],
+      },
+    ];
+
+    await expect(contract.methods.mintBulkCross(createData).call({from: caller})).to.be.rejectedWith('creation of multiple tokens supported only if they have single owner each');
+  });
+});
addedjs-packages/tests/eth/reFungibleToken.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/reFungibleToken.test.ts
@@ -0,0 +1,677 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {expect, itEth, usingEthPlaygrounds} from './util/index.js';
+import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {Contract} from 'web3-eth-contract';
+
+// FIXME: Need erc721 for ReFubgible.
+describe('Check ERC721 token URI for ReFungible', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+
+    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+    const result = await contract.methods.mint(receiver).send();
+
+    const event = result.events.Transfer;
+    const tokenId = event.returnValues.tokenId;
+    expect(tokenId).to.be.equal('1');
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.be.equal(receiver);
+
+    if(propertyKey && propertyValue) {
+      // Set URL or suffix
+
+      await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();
+    }
+
+    return {contract, nextTokenId: tokenId};
+  }
+
+  itEth('Empty tokenURI', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, '');
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
+  });
+
+  itEth('TokenURI from url', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
+  });
+
+  itEth('TokenURI from baseURI', async ({helper}) => {
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');
+  });
+
+  itEth('TokenURI from baseURI + suffix', async ({helper}) => {
+    const suffix = '/some/suffix';
+    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);
+    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
+  });
+});
+
+describe('Refungible: Plain calls', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([50n], donor);
+    });
+  });
+
+  itEth('Can perform approve()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    {
+      const result = await contract.methods.approve(spender, 100).send({from: owner});
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(tokenAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(spender);
+      expect(event.returnValues.value).to.be.equal('100');
+    }
+
+    {
+      const allowance = await contract.methods.allowance(owner, spender).call();
+      expect(+allowance).to.equal(100);
+    }
+  });
+
+  itEth('Can perform approveCross()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0];
+    const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
+    const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub);
+
+
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    {
+      const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(tokenAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(spender);
+      expect(event.returnValues.value).to.be.equal('100');
+    }
+
+    {
+      const allowance = await contract.methods.allowance(owner, spender).call();
+      expect(+allowance).to.equal(100);
+    }
+
+
+    {
+      const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner});
+      const event = result.events.Approval;
+      expect(event.address).to.be.equal(tokenAddress);
+      expect(event.returnValues.owner).to.be.equal(owner);
+      expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address));
+      expect(event.returnValues.value).to.be.equal('100');
+    }
+
+    {
+      const allowance = await collection.getTokenApprovedPieces(tokenId, {Ethereum: owner}, {Substrate: spenderSub.address});
+      expect(allowance).to.equal(100n);
+    }
+
+    {
+      //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call()
+    }
+  });
+
+  itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
+    const nonOwner = await helper.eth.createAccountWithBalance(donor);
+    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+    const token = await collection.mintToken(alice, 100n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+    const tokenEvm = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    await expect(tokenEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
+  });
+
+  [
+    'transferFrom',
+    'transferFromCross',
+  ].map(testCase =>
+    itEth(`Can perform ${testCase}`, async ({helper}) => {
+      const isCross = testCase === 'transferFromCross';
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const spender = await helper.eth.createAccountWithBalance(donor);
+      const receiverEth = helper.eth.createAccount();
+      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+      const [receiverSub] = await helper.arrange.createAccounts([1n], donor);
+      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
+
+      const collection = await helper.rft.mintCollection(alice);
+      const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
+
+      const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+      const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+      await contract.methods.approve(spender, 100).send({from: owner});
+
+      // 1. Can transfer from
+      // 1.1 Plain ethereum or cross address:
+      {
+        const result = await contract.methods[testCase](
+          isCross ? ownerCross : owner,
+          isCross ? receiverCrossEth : receiverEth,
+          49,
+        ).send({from: spender});
+
+        // Check events:
+        const transferEvent = result.events.Transfer;
+        expect(transferEvent.address).to.be.equal(tokenAddress);
+        expect(transferEvent.returnValues.from).to.be.equal(owner);
+        expect(transferEvent.returnValues.to).to.be.equal(receiverEth);
+        expect(transferEvent.returnValues.value).to.be.equal('49');
+
+        const approvalEvent = result.events.Approval;
+        expect(approvalEvent.address).to.be.equal(tokenAddress);
+        expect(approvalEvent.returnValues.owner).to.be.equal(owner);
+        expect(approvalEvent.returnValues.spender).to.be.equal(spender);
+        expect(approvalEvent.returnValues.value).to.be.equal('51');
+
+        // Check balances:
+        const receiverBalance = await contract.methods.balanceOf(receiverEth).call();
+        const ownerBalance = await contract.methods.balanceOf(owner).call();
+
+        expect(+receiverBalance).to.equal(49);
+        expect(+ownerBalance).to.equal(151);
+      }
+
+      // 1.2 Cross substrate address:
+      if(testCase === 'transferFromCross') {
+        const result = await contract.methods.transferFromCross(ownerCross, receiverCrossSub, 51).send({from: spender});
+        // Check events:
+        const transferEvent = result.events.Transfer;
+        expect(transferEvent.address).to.be.equal(tokenAddress);
+        expect(transferEvent.returnValues.from).to.be.equal(owner);
+        expect(transferEvent.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));
+        expect(transferEvent.returnValues.value).to.be.equal('51');
+
+        const approvalEvent = result.events.Approval;
+        expect(approvalEvent.address).to.be.equal(tokenAddress);
+        expect(approvalEvent.returnValues.owner).to.be.equal(owner);
+        expect(approvalEvent.returnValues.spender).to.be.equal(spender);
+        expect(approvalEvent.returnValues.value).to.be.equal('0');
+
+        // Check balances:
+        const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
+        const ownerBalance = await contract.methods.balanceOf(owner).call();
+        expect(receiverBalance).to.equal(51n);
+        expect(+ownerBalance).to.equal(100);
+      }
+    }));
+
+  [
+    'transfer',
+    'transferCross',
+  ].map(testCase =>
+    itEth(`Can perform ${testCase}()`, async ({helper}) => {
+      const isCross = testCase === 'transferCross';
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiverEth = helper.eth.createAccount();
+      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+      const [receiverSub] = await helper.arrange.createAccounts([1n], donor);
+      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
+      const collection = await helper.rft.mintCollection(alice);
+      const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
+
+      const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+      const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+      // 1. Can transfer to plain ethereum or cross-ethereum account:
+      {
+        const result = await contract.methods[testCase](isCross ? receiverCrossEth : receiverEth, 50).send({from: owner});
+        // Check events
+        const transferEvent = result.events.Transfer;
+        expect(transferEvent.address).to.be.equal(tokenAddress);
+        expect(transferEvent.returnValues.from).to.be.equal(owner);
+        expect(transferEvent.returnValues.to).to.be.equal(receiverEth);
+        expect(transferEvent.returnValues.value).to.be.equal('50');
+        // Check balances:
+        const ownerBalance = await contract.methods.balanceOf(owner).call();
+        const receiverBalance = await contract.methods.balanceOf(receiverEth).call();
+        expect(+ownerBalance).to.equal(150);
+        expect(+receiverBalance).to.equal(50);
+      }
+
+      // 2. Can transfer to cross-substrate account:
+      if(isCross) {
+        const result = await contract.methods.transferCross(receiverCrossSub, 50).send({from: owner});
+        // Check events:
+        const event = result.events.Transfer;
+        expect(event.address).to.be.equal(tokenAddress);
+        expect(event.returnValues.from).to.be.equal(owner);
+        expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));
+        expect(event.returnValues.value).to.be.equal('50');
+        // Check balances:
+        const ownerBalance = await contract.methods.balanceOf(owner).call();
+        const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
+        expect(+ownerBalance).to.equal(100);
+        expect(receiverBalance).to.equal(50n);
+      }
+    }));
+
+  [
+    'transfer',
+    'transferCross',
+  ].map(testCase =>
+    itEth(`Cannot ${testCase}() non-owned token`, async ({helper}) => {
+      const isCross = testCase === 'transferCross';
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+      const receiverEth = await helper.eth.createAccountWithBalance(donor);
+      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
+      const collection = await helper.rft.mintCollection(alice);
+      const rftOwner = await collection.mintToken(alice, 10n, {Ethereum: owner});
+      const rftReceiver = await collection.mintToken(alice, 10n, {Ethereum: receiverEth});
+      const tokenIdNonExist = 9999999;
+
+      const tokenAddress1 = helper.ethAddress.fromTokenId(collection.collectionId, rftOwner.tokenId);
+      const tokenAddress2 = helper.ethAddress.fromTokenId(collection.collectionId, rftReceiver.tokenId);
+      const tokenAddressNonExist = helper.ethAddress.fromTokenId(collection.collectionId, tokenIdNonExist);
+      const tokenEvmOwner = await helper.ethNativeContract.rftToken(tokenAddress1, owner);
+      const tokenEvmReceiver = await helper.ethNativeContract.rftToken(tokenAddress2, owner);
+      const tokenEvmNonExist = await helper.ethNativeContract.rftToken(tokenAddressNonExist, owner);
+
+      // 1. Can transfer zero amount (EIP-20):
+      await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 0).send({from: owner});
+      // 2. Cannot transfer non-owned token:
+      await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected;
+      await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected;
+      // 3. Cannot transfer non-existing token:
+      await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected;
+      await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected;
+
+      // 4. Storage is not corrupted:
+      expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);
+      expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]);
+      expect(await helper.rft.getTokenTop10Owners(collection.collectionId, tokenIdNonExist)).to.deep.eq([]);
+
+      // 4.1 Tokens can be transferred:
+      await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 10).send({from: owner});
+      await tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 10).send({from: receiverEth});
+      expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]);
+      expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);
+    }));
+
+  itEth('Can perform repartition()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    await contract.methods.repartition(200).send({from: owner});
+    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);
+    await contract.methods.transfer(receiver, 110).send({from: owner});
+    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);
+    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);
+
+    await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted
+
+    await contract.methods.transfer(receiver, 90).send({from: owner});
+    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);
+    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);
+
+    await contract.methods.repartition(150).send({from: receiver});
+    await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted
+    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);
+  });
+
+  itEth('Can repartition with increased amount', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    const result = await contract.methods.repartition(200).send();
+
+    const event = result.events.Transfer;
+    expect(event.address).to.be.equal(tokenAddress);
+    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.be.equal(owner);
+    expect(event.returnValues.value).to.be.equal('100');
+  });
+
+  itEth('Can repartition with decreased amount', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    const result = await contract.methods.repartition(50).send();
+    const event = result.events.Transfer;
+    expect(event.address).to.be.equal(tokenAddress);
+    expect(event.returnValues.from).to.be.equal(owner);
+    expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.value).to.be.equal('50');
+  });
+
+  itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = await helper.eth.createAccountWithBalance(donor);
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller, true);
+
+    await tokenContract.methods.repartition(2).send();
+    await tokenContract.methods.transfer(receiver, 1).send();
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+    await tokenContract.methods.burnFrom(caller, 1).send();
+
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+    expect(event.address).to.be.equal(collectionAddress);
+    expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.tokenId).to.be.equal(tokenId);
+  });
+
+  itEth('Can perform burnFromCross()', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const ownerSub = (await helper.arrange.createAccounts([10n], donor))[0];
+    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+
+    const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
+    const ownerSubCross = helper.ethCrossAccount.fromKeyringPair(ownerSub);
+
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
+
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    {
+      await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
+
+      await expect(contract.methods.burnFromCross(ownerCross, 50).send({from: spender})).to.be.fulfilled;
+      await expect(contract.methods.burnFromCross(ownerCross, 100).send({from: spender})).to.be.rejected;
+      expect(await contract.methods.balanceOf(owner).call({from: owner})).to.be.equal('150');
+    }
+    {
+      const {tokenId} = await collection.mintToken(alice, 200n, {Substrate: ownerSub.address});
+      await collection.approveToken(ownerSub, tokenId, {Ethereum: spender}, 100n);
+      const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+      const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+      await expect(contract.methods.burnFromCross(ownerSubCross, 50).send({from: spender})).to.be.fulfilled;
+      await expect(contract.methods.burnFromCross(ownerSubCross, 100).send({from: spender})).to.be.rejected;
+      expect(await collection.getTokenBalance(tokenId, {Substrate: ownerSub.address})).to.be.equal(150n);
+    }
+  });
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const other = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner.eth});
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth);
+
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('200');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await tokenContract.methods.repartition(100n).send({from: owner.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
+
+    await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
+
+    await tokenContract.methods.repartition(1000n).send({from: other.eth});
+    await tokenContract.methods.transferCross(owner, 500n).send({from: other.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('500');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('500');
+  });
+});
+
+describe('Refungible: Fees', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([50n], donor);
+    });
+  });
+
+  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = helper.eth.createAccount();
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+
+  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const spender = await helper.eth.createAccountWithBalance(donor);
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    await contract.methods.approve(spender, 100).send({from: owner});
+
+    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+
+  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.rft.mintCollection(alice);
+    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
+    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
+  });
+});
+
+describe('Refungible: Substrate calls', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([50n], donor);
+    });
+  });
+
+  itEth('Events emitted for approve()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.rft.mintCollection(alice);
+    const token = await collection.mintToken(alice, 200n);
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress);
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true;
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(tokenAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.spender).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('100');
+  });
+
+  itEth('Events emitted for transferFrom()', async ({helper}) => {
+    const [bob] = await helper.arrange.createAccounts([10n], donor);
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.rft.mintCollection(alice);
+    const token = await collection.mintToken(alice, 200n);
+    await token.approve(alice, {Substrate: bob.address}, 100n);
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress);
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver},  51n)).to.be.true;
+    if(events.length == 0) await helper.wait.newBlocks(1);
+
+    let event = events[0];
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(tokenAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('51');
+
+    event = events[1];
+    expect(event.event).to.be.equal('Approval');
+    expect(event.address).to.be.equal(tokenAddress);
+    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
+    expect(event.returnValues.value).to.be.equal('49');
+  });
+
+  itEth('Events emitted for transfer()', async ({helper}) => {
+    const receiver = helper.eth.createAccount();
+    const collection = await helper.rft.mintCollection(alice);
+    const token = await collection.mintToken(alice, 200n);
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
+    const contract = await helper.ethNativeContract.rftToken(tokenAddress);
+
+    const events: any = [];
+    contract.events.allEvents((_: any, event: any) => {
+      events.push(event);
+    });
+
+    expect(await token.transfer(alice, {Ethereum: receiver},  51n)).to.be.true;
+    if(events.length == 0) await helper.wait.newBlocks(1);
+    const event = events[0];
+
+    expect(event.event).to.be.equal('Transfer');
+    expect(event.address).to.be.equal(tokenAddress);
+    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
+    expect(event.returnValues.to).to.be.equal(receiver);
+    expect(event.returnValues.value).to.be.equal('51');
+  });
+});
+
+describe('ERC 1633 implementation', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Default parent token address and id', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN');
+    const collectionContract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+    const result = await collectionContract.methods.mint(owner).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
+
+    expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress);
+    expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId);
+  });
+});
addedjs-packages/tests/eth/scheduling.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/scheduling.test.ts
@@ -0,0 +1,64 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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} from 'chai';
+import {itSchedEth} from './util/index.js';
+import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
+import {Pallets, usingPlaygrounds} from '../util/index.js';
+
+describe('Scheduing EVM smart contracts', () => {
+
+  before(async () => {
+    await usingPlaygrounds(async (helper) => {
+      await helper.testUtils.enable(Pallets.TestUtils);
+    });
+  });
+
+  itSchedEth.ifWithPallets('Successfully schedules and periodically executes an EVM contract', [Pallets.UniqueScheduler], async (scheduleKind, {helper, privateKey}) => {
+    const donor = await privateKey({url: import.meta.url});
+    const [alice] = await helper.arrange.createAccounts([1000n], donor);
+
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+
+    const deployer = await helper.eth.createAccountWithBalance(alice);
+    const flipper = await helper.eth.deployFlipper(deployer);
+
+    const initialValue = await flipper.methods.getValue().call();
+    await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address));
+
+    const waitForBlocks = 4;
+    const periodic = {
+      period: 2,
+      repetitions: 2,
+    };
+
+    await helper.scheduler.scheduleAfter<EthUniqueHelper>(waitForBlocks, {scheduledId, periodic})
+      .eth.sendEVM(
+        alice,
+        flipper.options.address,
+        flipper.methods.flip().encodeABI(),
+        '0',
+      );
+
+    expect(await flipper.methods.getValue().call()).to.be.equal(initialValue);
+
+    await helper.wait.newBlocks(waitForBlocks + 1);
+    expect(await flipper.methods.getValue().call()).to.be.not.equal(initialValue);
+
+    await helper.wait.newBlocks(periodic.period);
+    expect(await flipper.methods.getValue().call()).to.be.equal(initialValue);
+  });
+});
addedjs-packages/tests/eth/sponsoring.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/sponsoring.test.ts
@@ -0,0 +1,97 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itEth, expect, SponsoringMode} from './util/index.js';
+import {usingPlaygrounds} from '../util/index.js';
+
+describe('EVM sponsoring', () => {
+  let donor: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Fee is deducted from contract if sponsoring is enabled', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const caller = helper.eth.createAccount();
+    const originalCallerBalance = await helper.balance.getEthereum(caller);
+
+    expect(originalCallerBalance).to.be.equal(0n);
+
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+
+    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
+    await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
+
+    await helpers.methods.setSponsor(flipper.options.address, sponsor).send({from: owner});
+    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
+
+    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
+    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
+    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
+
+    const originalSponsorBalance = await helper.balance.getEthereum(sponsor);
+    expect(originalSponsorBalance).to.be.not.equal(0n);
+
+    await flipper.methods.flip().send({from: caller});
+    expect(await flipper.methods.getValue().call()).to.be.true;
+
+    // Balance should be taken from flipper instead of caller
+    expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance);
+    expect(await helper.balance.getEthereum(sponsor)).to.be.not.equal(originalSponsorBalance);
+  });
+
+  itEth('...but this doesn\'t applies to payable value', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const originalCallerBalance = await helper.balance.getEthereum(caller);
+
+    expect(originalCallerBalance).to.be.not.equal(0n);
+
+    const collector = await helper.eth.deployCollectorContract(owner);
+
+    const helpers = await helper.ethNativeContract.contractHelpers(owner);
+
+    await helpers.methods.toggleAllowlist(collector.options.address, true).send({from: owner});
+    await helpers.methods.toggleAllowed(collector.options.address, caller, true).send({from: owner});
+
+    expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.false;
+    await helpers.methods.setSponsoringMode(collector.options.address, SponsoringMode.Allowlisted).send({from: owner});
+    await helpers.methods.setSponsoringRateLimit(collector.options.address, 0).send({from: owner});
+    expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.true;
+
+    await helpers.methods.setSponsor(collector.options.address, sponsor).send({from: owner});
+    await helpers.methods.confirmSponsorship(collector.options.address).send({from: sponsor});
+
+    const originalSponsorBalance = await helper.balance.getEthereum(sponsor);
+    expect(originalSponsorBalance).to.be.not.equal(0n);
+
+    await collector.methods.giveMoney().send({from: caller, value: '10000'});
+
+    // Balance will be taken from both caller (value) and from collector (fee)
+    expect(await helper.balance.getEthereum(caller)).to.be.equals((originalCallerBalance - 10000n));
+    expect(await helper.balance.getEthereum(sponsor)).to.be.not.equals(originalSponsorBalance);
+    expect(await collector.methods.getCollected().call()).to.be.equal('10000');
+  });
+});
addedjs-packages/tests/eth/tokenProperties.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/tokenProperties.test.ts
@@ -0,0 +1,625 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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 {CreateCollectionData, TokenPermissionField} from './util/playgrounds/types.js';
+
+describe('EVM token properties', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([1000n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
+      for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
+        const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+        await collection.methods.addCollectionAdminCross(caller).send({from: owner});
+
+        await collection.methods.setTokenPropertyPermissions([
+          ['testKey', [
+            [TokenPermissionField.Mutable, mutable],
+            [TokenPermissionField.TokenOwner, tokenOwner],
+            [TokenPermissionField.CollectionAdmin, collectionAdmin]],
+          ],
+        ]).send({from: caller.eth});
+
+        expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
+          key: 'testKey',
+          permission: {mutable, collectionAdmin, tokenOwner},
+        }]);
+
+        expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
+          ['testKey', [
+            [TokenPermissionField.Mutable.toString(), mutable],
+            [TokenPermissionField.TokenOwner.toString(), tokenOwner],
+            [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]],
+          ],
+        ]);
+      }
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      await collection.methods.setTokenPropertyPermissions([
+        ['testKey_0', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+        ['testKey_1', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, false],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+        ['testKey_2', [
+          [TokenPermissionField.Mutable, false],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, false]],
+        ],
+      ]).send({from: owner});
+
+      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
+        {
+          key: 'testKey_0',
+          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_1',
+          permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_2',
+          permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
+        },
+      ]);
+
+      expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([
+        ['testKey_0', [
+          [TokenPermissionField.Mutable.toString(), true],
+          [TokenPermissionField.TokenOwner.toString(), true],
+          [TokenPermissionField.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_1', [
+          [TokenPermissionField.Mutable.toString(), true],
+          [TokenPermissionField.TokenOwner.toString(), false],
+          [TokenPermissionField.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_2', [
+          [TokenPermissionField.Mutable.toString(), false],
+          [TokenPermissionField.TokenOwner.toString(), true],
+          [TokenPermissionField.CollectionAdmin.toString(), false]],
+        ],
+      ]);
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
+
+      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+      await collection.methods.addCollectionAdminCross(caller).send({from: owner});
+
+      await collection.methods.setTokenPropertyPermissions([
+        ['testKey_0', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+        ['testKey_1', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, false],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+        ['testKey_2', [
+          [TokenPermissionField.Mutable, false],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, false]],
+        ],
+      ]).send({from: caller.eth});
+
+      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
+        {
+          key: 'testKey_0',
+          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_1',
+          permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_2',
+          permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
+        },
+      ]);
+
+      expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
+        ['testKey_0', [
+          [TokenPermissionField.Mutable.toString(), true],
+          [TokenPermissionField.TokenOwner.toString(), true],
+          [TokenPermissionField.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_1', [
+          [TokenPermissionField.Mutable.toString(), true],
+          [TokenPermissionField.TokenOwner.toString(), false],
+          [TokenPermissionField.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_2', [
+          [TokenPermissionField.Mutable.toString(), false],
+          [TokenPermissionField.TokenOwner.toString(), true],
+          [TokenPermissionField.CollectionAdmin.toString(), false]],
+        ],
+      ]);
+
+    }));
+
+  [
+    {
+      method: 'setProperties',
+      methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]],
+      expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}],
+    },
+    {
+      method: 'setProperty' /*Soft-deprecated*/,
+      methodParams: ['testKey1', Buffer.from('testValue1')],
+      expectedProps: [{key: 'testKey1', value: 'testValue1'}],
+    },
+  ].map(testCase =>
+    itEth(`[${testCase.method}] Can be set`, async({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const collection = await helper.nft.mintCollection(alice, {
+        tokenPropertyPermissions: [{
+          key: 'testKey1',
+          permission: {
+            collectionAdmin: true,
+          },
+        }, {
+          key: 'testKey2',
+          permission: {
+            collectionAdmin: true,
+          },
+        }],
+      });
+
+      await collection.addAdmin(alice, {Ethereum: caller});
+      const token = await collection.mintToken(alice);
+
+      const collectionEvm = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller, testCase.method === 'setProperty');
+
+      await collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller});
+
+      const properties = await token.getProperties();
+      expect(properties).to.deep.equal(testCase.expectedProps);
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`Can be multiple set/read for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+
+      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
+      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true,
+        collectionAdmin: true,
+        mutable: true}}));
+
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPrefix: 'ethp',
+        tokenPropertyPermissions: permissions,
+      }) as UniqueNFTCollection | UniqueRFTCollection;
+
+      const token = await collection.mintToken(alice);
+
+      const valuesBefore = await token.getProperties(properties.map(p => p.key));
+      expect(valuesBefore).to.be.deep.equal([]);
+
+
+      await collection.addAdmin(alice, {Ethereum: caller});
+
+      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
+
+      expect(await contract.methods.properties(token.tokenId, []).call()).to.be.deep.equal([]);
+
+      await contract.methods.setProperties(token.tokenId, properties).send({from: caller});
+
+      const values = await token.getProperties(properties.map(p => p.key));
+      expect(values).to.be.deep.equal(properties.map(p => ({key: p.key, value: p.value.toString()})));
+
+      expect(await contract.methods.properties(token.tokenId, []).call()).to.be.like(properties
+        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
+
+      expect(await contract.methods.properties(token.tokenId, [properties[0].key]).call())
+        .to.be.like([helper.ethProperty.property(properties[0].key, properties[0].value.toString())]);
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [{
+          key: 'testKey',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+          },
+        },
+        {
+          key: 'testKey_1',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+          },
+        }],
+      });
+
+      const token = await collection.mintToken(alice);
+      await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}, {key: 'testKey_1', value: 'testValue_1'}]);
+      expect(await token.getProperties()).to.has.length(2);
+
+      await collection.addAdmin(alice, {Ethereum: caller});
+
+      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
+
+      await contract.methods.deleteProperties(token.tokenId, ['testKey', 'testKey_1']).send({from: caller});
+
+      const result = await token.getProperties(['testKey', 'testKey_1']);
+      expect(result.length).to.equal(0);
+    }));
+
+  itEth('Can be read', async({helper}) => {
+    const caller = helper.eth.createAccount();
+    const collection = await helper.nft.mintCollection(alice, {
+      tokenPropertyPermissions: [{
+        key: 'testKey',
+        permission: {
+          collectionAdmin: true,
+        },
+      }],
+    });
+
+    const token = await collection.mintToken(alice);
+    await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}]);
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
+
+    const value = await contract.methods.property(token.tokenId, 'testKey').call();
+    expect(value).to.equal(helper.getWeb3().utils.toHex('testValue'));
+  });
+});
+
+describe('EVM token properties negative', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let caller: string;
+  let aliceCollection: UniqueNFTCollection;
+  let token: UniqueNFToken;
+  const tokenProps = [{key: 'testKey_1', value: 'testValue_1'}, {key: 'testKey_2', value: 'testValue_2'}];
+  let collectionEvm: Contract;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  beforeEach(async () => {
+    // 1. create collection with props: testKey_1, testKey_2
+    // 2. create token and set props testKey_1, testKey_2
+    await usingEthPlaygrounds(async (helper) => {
+      aliceCollection = await helper.nft.mintCollection(alice, {
+        tokenPropertyPermissions: [{
+          key: 'testKey_1',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+          },
+        },
+        {
+          key: 'testKey_2',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+          },
+        }],
+      });
+      token = await aliceCollection.mintToken(alice);
+      await token.setProperties(alice, tokenProps);
+      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
+    });
+  });
+
+  [
+    {method: 'setProperty', methodParams: [tokenProps[1].key, Buffer.from('newValue')]},
+    {method: 'setProperties', methodParams: [[{key: tokenProps[1].key, value: Buffer.from('newValue')}]]},
+  ].map(testCase =>
+    itEth(`[${testCase.method}] Cannot set properties of non-owned collection`, async ({helper}) => {
+      caller = await helper.eth.createAccountWithBalance(donor);
+      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
+      // Caller not an owner and not an admin, so he cannot set properties:
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
+
+      // Props have not changed:
+      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
+      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
+      expect(actualProps).to.deep.eq(expectedProps);
+    }));
+
+  [
+    {method: 'setProperty', methodParams: ['testKey_3', Buffer.from('testValue3')]},
+    {method: 'setProperties', methodParams: [[{key: 'testKey_3', value: Buffer.from('testValue3')}]]},
+  ].map(testCase =>
+    itEth(`[${testCase.method}] Cannot set non-existing properties`, async ({helper}) => {
+      caller = await helper.eth.createAccountWithBalance(donor);
+      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
+      await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller});
+
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
+
+      // Props have not changed:
+      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
+      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
+      expect(actualProps).to.deep.eq(expectedProps);
+    }));
+
+  [
+    {method: 'deleteProperty', methodParams: ['testKey_2']},
+    {method: 'deleteProperties', methodParams: [['testKey_2']]},
+  ].map(testCase =>
+    itEth(`[${testCase.method}] Cannot delete properties of non-owned collection`, async ({helper}) => {
+      caller = await helper.eth.createAccountWithBalance(donor);
+      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty');
+      // Caller not an owner and not an admin, so he cannot set properties:
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
+
+      // Props have not changed:
+      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
+      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
+      expect(actualProps).to.deep.eq(expectedProps);
+    }));
+
+  [
+    {method: 'deleteProperty', methodParams: ['testKey_3']},
+    {method: 'deleteProperties', methodParams: [['testKey_3']]},
+  ].map(testCase =>
+    itEth(`[${testCase.method}] Cannot delete non-existing properties`, async ({helper}) => {
+      caller = await helper.eth.createAccountWithBalance(donor);
+      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty');
+      await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller});
+      // Caller cannot delete non-existing properties:
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
+      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
+      // Props have not changed:
+      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
+      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
+      expect(actualProps).to.deep.eq(expectedProps);
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const caller = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      await expect(collection.methods.setTokenPropertyPermissions([
+        ['testKey_0', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+      ]).call({from: caller})).to.be.rejectedWith('NoPermission');
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      await expect(collection.methods.setTokenPropertyPermissions([
+        // "Space" is invalid character
+        ['testKey 0', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+      ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey');
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Can reconfigure token property permissions to stricter ones`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      // 1. Owner sets strict property-permissions:
+      await collection.methods.setTokenPropertyPermissions([
+        ['testKey', [
+          [TokenPermissionField.Mutable, true],
+          [TokenPermissionField.TokenOwner, true],
+          [TokenPermissionField.CollectionAdmin, true]],
+        ],
+      ]).send({from: owner});
+
+      // 2. Owner can set stricter property-permissions:
+      for(const values of [[true, true, false], [true, false, false], [false, false, false]]) {
+        await collection.methods.setTokenPropertyPermissions([
+          ['testKey', [
+            [TokenPermissionField.Mutable, values[0]],
+            [TokenPermissionField.TokenOwner, values[1]],
+            [TokenPermissionField.CollectionAdmin, values[2]]],
+          ],
+        ]).send({from: owner});
+      }
+
+      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
+        key: 'testKey',
+        permission: {mutable: false, collectionAdmin: false, tokenOwner: false},
+      }]);
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Cannot reconfigure token property permissions to less strict ones`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      // 1. Owner sets strict property-permissions:
+      await collection.methods.setTokenPropertyPermissions([
+        ['testKey', [
+          [TokenPermissionField.Mutable, false],
+          [TokenPermissionField.TokenOwner, false],
+          [TokenPermissionField.CollectionAdmin, false]],
+        ],
+      ]).send({from: owner});
+
+      // 2. Owner cannot set less strict property-permissions:
+      for(const values of [[true, false, false], [false, true, false], [false, false, true]]) {
+        await expect(collection.methods.setTokenPropertyPermissions([
+          ['testKey', [
+            [TokenPermissionField.Mutable, values[0]],
+            [TokenPermissionField.TokenOwner, values[1]],
+            [TokenPermissionField.CollectionAdmin, values[2]]],
+          ],
+        ]).call({from: owner})).to.be.rejectedWith('NoPermission');
+      }
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Can't be multiple set/read for non-existent token`, testCase.requiredPallets, async({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+
+      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
+      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true,
+        collectionAdmin: true,
+        mutable: true}}));
+
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPrefix: 'ethp',
+        tokenPropertyPermissions: permissions,
+      }) as UniqueNFTCollection | UniqueRFTCollection;
+
+      await collection.addAdmin(alice, {Ethereum: caller});
+
+      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
+
+      await expect(contract.methods.setProperties(1, properties).call({from: caller})).to.be.rejectedWith('TokenNotFound');
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Can't be deleted for non-existent token`, testCase.requiredPallets, async({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [{
+          key: 'testKey',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+          },
+        },
+        {
+          key: 'testKey_1',
+          permission: {
+            mutable: true,
+            collectionAdmin: true,
+          },
+        }],
+      });
+
+
+      await collection.addAdmin(alice, {Ethereum: caller});
+
+      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
+
+      await expect(contract.methods.deleteProperties(1, ['testKey', 'testKey_1']).call({from: caller})).to.be.rejectedWith('TokenNotFound');
+    }));
+});
+
+
+type ElementOf<A> = A extends readonly (infer T)[] ? T : never;
+function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {
+  if(args.length === 0) {
+    yield internalRest as any;
+    return;
+  }
+  for(const value of args[0]) {
+    yield* cartesian([...internalRest, value], ...args.slice(1)) as any;
+  }
+}
addedjs-packages/tests/eth/tokens/callMethodsERC20.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/tokens/callMethodsERC20.test.ts
@@ -0,0 +1,92 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {CreateCollectionData} from '../util/playgrounds/types.js';
+
+[
+  {mode: 'ft' as const, requiredPallets: []},
+  {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+].map(testCase => {
+  describe(`${testCase.mode.toUpperCase()}: ERC-20 call methods`, () => {
+    let donor: IKeyringPair;
+
+    before(async function() {
+      await usingEthPlaygrounds(async (helper, privateKey) => {
+        requirePalletsOrSkip(this, helper, testCase.requiredPallets);
+        donor = await privateKey({url: import.meta.url});
+      });
+    });
+
+    itEth('totalSupply', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
+
+      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send();
+      if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
+
+      // Use collection contract for FT or token contract for RFT:
+      const contract = testCase.mode === 'ft'
+        ? collection
+        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
+
+      // Mint tokens:
+      testCase.mode === 'ft'
+        ? await contract.methods.mint(...mintingParams).send({from: caller})
+        : await contract.methods.repartition(200).send({from: caller});
+
+      const totalSupply = await contract.methods.totalSupply().call();
+      expect(totalSupply).to.equal('200');
+    });
+
+    itEth('balanceOf', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
+
+      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send();
+      if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
+
+      // Use collection contract for FT or token contract for RFT:
+      const contract = testCase.mode === 'ft'
+        ? collection
+        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
+
+      // Mint tokens:
+      testCase.mode === 'ft'
+        ? await contract.methods.mint(...mintingParams).send({from: caller})
+        : await contract.methods.repartition(200).send({from: caller});
+
+      const balance = await contract.methods.balanceOf(caller).call();
+      expect(balance).to.equal('200');
+    });
+
+    itEth('decimals', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send();
+      if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
+
+      // Use collection contract for FT or token contract for RFT:
+      const contract = testCase.mode === 'ft'
+        ? collection
+        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
+
+      const decimals = await contract.methods.decimals().call();
+      expect(decimals).to.equal(testCase.mode === 'rft' ? '0' : '18');
+    });
+  });
+});
addedjs-packages/tests/eth/tokens/callMethodsERC721.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/tokens/callMethodsERC721.test.ts
@@ -0,0 +1,144 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
+import type {IKeyringPair} from '@polkadot/types/types';
+import {CreateCollectionData} from '../util/playgrounds/types.js';
+
+
+describe('ERC-721 call methods', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: name/symbol/description`, testCase.requiredPallets, async ({helper}) => {
+      const callerEth = await helper.eth.createAccountWithBalance(donor);
+      const [callerSub] = await helper.arrange.createAccounts([100n], donor);
+      const [name, description, tokenPrefix] = ['Name', 'Description', 'Symbol'];
+
+      const {collection: collectionEth} = await helper.eth.createCollection(callerEth, new CreateCollectionData(name, description, tokenPrefix, testCase.mode)).send();
+      await collectionEth.methods.mint(callerEth).send({from: callerEth});
+      const {collectionId} = await helper[testCase.mode].mintCollection(callerSub, {name, description, tokenPrefix});
+      const collectionSub = await helper.ethNativeContract.collectionById(collectionId, testCase.mode, callerEth);
+
+      // Can get name/symbol/description for Eth collection
+      expect(await collectionEth.methods.name().call()).to.eq(name);
+      expect(await collectionEth.methods.symbol().call()).to.eq(tokenPrefix);
+      expect(await collectionEth.methods.description().call()).to.eq(description);
+      // Can get name/symbol/description for Sub collection
+      expect(await collectionSub.methods.name().call()).to.eq(name);
+      expect(await collectionSub.methods.symbol().call()).to.eq(tokenPrefix);
+      expect(await collectionSub.methods.description().call()).to.eq(description);
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: totalSupply`, testCase.requiredPallets, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+
+      const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send();
+      await collection.methods.mint(caller).send({from: caller});
+
+      const totalSupply = await collection.methods.totalSupply().call();
+      expect(totalSupply).to.equal('1');
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: balanceOf`, testCase.requiredPallets, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+
+      const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send();
+      await collection.methods.mint(caller).send({from: caller});
+      await collection.methods.mint(caller).send({from: caller});
+      await collection.methods.mint(caller).send({from: caller});
+
+      const balance = await collection.methods.balanceOf(caller).call();
+      expect(balance).to.equal('3');
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf`, testCase.requiredPallets, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf', '6', '6', testCase.mode)).send();
+
+      const result = await collection.methods.mint(caller).send();
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+
+      const owner = await collection.methods.ownerOf(tokenId).call();
+      expect(owner).to.equal(caller);
+    });
+  });
+
+  [
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    // TODO {mode: 'nft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf after burn`, testCase.requiredPallets, async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf-AfterBurn', '6', '6', testCase.mode)).send();
+
+      const result = await collection.methods.mint(caller).send();
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+      const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller, true);
+
+      await tokenContract.methods.repartition(2).send();
+      await tokenContract.methods.transfer(receiver, 1).send();
+
+      await tokenContract.methods.burnFrom(caller, 1).send();
+
+      const owner = await collection.methods.ownerOf(tokenId).call();
+      expect(owner).to.equal(receiver);
+    });
+  });
+
+  itEth.ifWithPallets('RFT: ownerOf for partial ownership', [Pallets.ReFungible], async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
+    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+
+    const result = await contract.methods.mint(caller).send();
+    const tokenId = result.events.Transfer.returnValues.tokenId;
+    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
+
+    await tokenContract.methods.repartition(2).send();
+    await tokenContract.methods.transfer(receiver, 1).send();
+
+    const owner = await contract.methods.ownerOf(tokenId).call();
+    expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+  });
+});
addedjs-packages/tests/eth/tokens/minting.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/tokens/minting.test.ts
@@ -0,0 +1,168 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../../util/index.js';
+import {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
+import {CreateCollectionData} from '../util/playgrounds/types.js';
+
+
+describe('Minting tokens', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([30n, 20n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Substrate collection`, testCase.requiredPallets, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
+
+      const collection = await helper[testCase.mode].mintCollection(alice);
+      await collection.addAdmin(alice, {Ethereum: owner});
+
+      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      const result = await contract.methods.mint(...mintingParams).send({from: owner});
+
+      // Check events:
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.equal(receiver);
+      if(testCase.mode === 'ft')
+        expect(event.returnValues.value).to.equal('100');
+
+      // Check token exist:
+      if(testCase.mode === 'ft') {
+        expect(await helper.ft.getBalance(collection.collectionId, {Ethereum: receiver})).to.eq(100n);
+      } else {
+        const tokenId = event.returnValues.tokenId;
+        expect(tokenId).to.be.equal('1');
+        expect(await helper.collection.getLastTokenId(collection.collectionId)).to.eq(1);
+        expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
+
+      const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send();
+
+      const result = await collection.methods.mint(...mintingParams).send({from: owner});
+
+      // Check events:
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.equal(receiver);
+      if(testCase.mode === 'ft')
+        expect(event.returnValues.value).to.equal('100');
+
+      // Check token exist:
+      if(testCase.mode === 'ft') {
+        expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n);
+      } else {
+        const tokenId = event.returnValues.tokenId;
+        expect(tokenId).to.be.equal('1');
+        expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
+        expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'ft' as const, requiredPallets: []},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
+
+      const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send();
+
+      const result = await collection.methods.mint(...mintingParams).send({from: owner});
+
+      // Check events:
+      const event = result.events.Transfer;
+      expect(event.address).to.equal(collectionAddress);
+      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.equal(receiver);
+      if(testCase.mode === 'ft')
+        expect(event.returnValues.value).to.equal('100');
+
+      // Check token exist:
+      if(testCase.mode === 'ft') {
+        expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n);
+      } else {
+        const tokenId = event.returnValues.tokenId;
+        expect(tokenId).to.be.equal('1');
+        expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
+        expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase => {
+    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mintWithTokenURI() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+
+      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');
+      const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+      expect(tokenId).to.be.equal('1');
+
+      const event = result.events.Transfer;
+      expect(event.address).to.be.equal(collectionAddress);
+      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
+      expect(event.returnValues.to).to.be.equal(receiver);
+
+      expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+      expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
+      // TODO: this wont work right now, need release 919000 first
+      // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();
+      // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();
+      // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);
+    });
+  });
+});
addedjs-packages/tests/eth/transferValue.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/transferValue.test.ts
@@ -0,0 +1,64 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itEth, usingEthPlaygrounds} from './util/index.js';
+import {expect} from 'chai';
+
+describe('Send value to contract', () => {
+  let donor: IKeyringPair;
+
+  before(async () => {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Send to and from contract', async ({helper}) => {
+    const contractOwner = await helper.eth.createAccountWithBalance(donor, 600n);
+    const [buyer, receiver] = await helper.arrange.createAccounts([600n, 600n], donor);
+    const receiverMirror = helper.address.substrateToEth(receiver.address);
+    const contract = await helper.ethContract.deployByCode(
+      contractOwner,
+      'Test',
+      `
+      // SPDX-License-Identifier: UNLICENSED
+      pragma solidity ^0.8.18;
+      
+      contract Test {
+        function send() public payable {
+          if (msg.value < 1000000000000000000)
+            revert("Not enough gold");
+        }
+      
+        function withdraw(address to) public {
+          payable(to).transfer(1000000000000000000);
+        }
+      }
+      `,
+    );
+
+    const balanceBefore = await helper.balance.getSubstrate(buyer.address);
+    await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.send().encodeABI(), '2000000000000000000');
+    const balanceAfter = await helper.balance.getSubstrate(buyer.address);
+    expect(balanceBefore - balanceAfter > 2000000000000000000n).to.be.true;
+    expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(2000000000000000000n);
+
+    await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.withdraw(receiverMirror).encodeABI(), '0');
+    expect(await helper.balance.getEthereum(receiverMirror)).to.be.equal(1000000000000000000n);
+    expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(1000000000000000000n);
+  });
+});
addedjs-packages/tests/eth/util/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/util/index.ts
@@ -0,0 +1,107 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+import * as path from 'path';
+import type {IKeyringPair} from '@polkadot/types/types';
+
+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 chai from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+import chaiLike from 'chai-like';
+import {getTestSeed, MINIMUM_DONOR_FUND, requirePalletsOrSkip} from '../../util/index.js';
+
+chai.use(chaiAsPromised);
+chai.use(chaiLike);
+export const expect = chai.expect;
+
+export enum SponsoringMode {
+  Disabled = 0,
+  Allowlisted = 1,
+  Generous = 2,
+}
+
+type PrivateKeyFn = (seed: string | {filename?: string, url?: string}) => Promise<IKeyringPair>;
+
+export const usingEthPlaygrounds = async (code: (helper: EthUniqueHelper, privateKey: PrivateKeyFn) => Promise<void>) => {
+  const silentConsole = new SilentConsole();
+  silentConsole.enable();
+
+  const helper = new EthUniqueHelper(new SilentLogger());
+
+  try {
+    await helper.connect(config.substrateUrl);
+    await helper.connectWeb3(config.substrateUrl);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const privateKey: PrivateKeyFn = async (seed) => {
+      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);
+      if(await helper.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;
+    };
+    await code(helper, privateKey);
+  }
+  finally {
+    await helper.disconnect();
+    silentConsole.disable();
+  }
+};
+
+export function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+  (opts.only ? it.only :
+    opts.skip ? it.skip : it)(name, async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      if(opts.requiredPallets) {
+        requirePalletsOrSkip(this, helper, opts.requiredPallets);
+      }
+
+      await cb({helper, privateKey});
+    });
+  });
+}
+
+export function itEthIfWithPallet(name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+  return itEth(name, cb, {requiredPallets: required, ...opts});
+}
+
+itEth.only = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEth(name, cb, {only: true});
+itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any) => itEth(name, cb, {skip: true});
+
+itEthIfWithPallet.only = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {only: true});
+itEthIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {skip: true});
+itEth.ifWithPallets = itEthIfWithPallet;
+
+export function itSchedEth(
+  name: string,
+  cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any,
+  opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {},
+) {
+  itEth(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts);
+  itEth(name + ' (named scheduling)', (apis) => cb('named', apis), opts);
+}
+itSchedEth.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any) => itSchedEth(name, cb, {only: true});
+itSchedEth.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any) => itSchedEth(name, cb, {skip: true});
+itSchedEth.ifWithPallets = itSchedIfWithPallets;
+
+function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+  return itSchedEth(name, cb, {requiredPallets: required, ...opts});
+}
addedjs-packages/tests/eth/util/playgrounds/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/util/playgrounds/types.ts
@@ -0,0 +1,133 @@
+import {CollectionFlag} from '@unique/playgrounds/types.js';
+import type {TCollectionMode} from '@unique/playgrounds/types.js';
+
+export interface ContractImports {
+  solPath: string;
+  fsPath: string;
+}
+
+export interface CompiledContract {
+  abi: any;
+  object: string;
+}
+
+export type NormalizedEvent = {
+  address: string,
+  event: string,
+  args: { [key: string]: string }
+};
+
+export interface OptionUint {
+  status: boolean,
+  value: bigint,
+}
+
+export type EthAddress = string;
+
+export interface CrossAddress {
+  readonly eth: EthAddress,
+  readonly sub: string | Uint8Array,
+}
+
+export type EthProperty = string[];
+
+export enum TokenPermissionField {
+  Mutable,
+  TokenOwner,
+  CollectionAdmin
+}
+
+export enum CollectionLimitField {
+  AccountTokenOwnership,
+	SponsoredDataSize,
+	SponsoredDataRateLimit,
+	TokenLimit,
+	SponsorTransferTimeout,
+	SponsorApproveTimeout,
+	OwnerCanTransfer,
+	OwnerCanDestroy,
+	TransferEnabled
+}
+
+export interface CollectionLimit {
+  field: CollectionLimitField,
+  value: OptionUint,
+}
+
+export interface CollectionLimitValue {
+  field: CollectionLimitField,
+  value: bigint,
+}
+
+export enum CollectionMode {
+	Nonfungible,
+	Fungible,
+	Refungible,
+}
+
+export interface PropertyPermission {
+  code: TokenPermissionField,
+  value: boolean,
+}
+export interface TokenPropertyPermission {
+  key: string,
+  permissions: PropertyPermission[],
+}
+export interface CollectionNestingAndPermission {
+  token_owner: boolean,
+  collection_admin: boolean,
+  restricted: string[],
+}
+
+export const emptyAddress: [string, string] = [
+  '0x0000000000000000000000000000000000000000',
+  '0',
+];
+
+export const CREATE_COLLECTION_DATA_DEFAULTS = {
+  decimals: 0,
+  properties: [],
+  tokenPropertyPermissions: [],
+  adminList: [],
+  nestingSettings: {token_owner: false, collection_admin: false, restricted: []},
+  limits: [],
+  pendingSponsor: emptyAddress,
+  flags: 0,
+};
+
+export interface Property {
+  key: string;
+  value: Buffer;
+}
+
+export class CreateCollectionData {
+  name: string;
+  description: string;
+  tokenPrefix: string;
+  collectionMode: TCollectionMode;
+  decimals? = 0;
+  properties?: Property[] = [];
+  tokenPropertyPermissions?: TokenPropertyPermission[] = [];
+  adminList?: CrossAddress[] = [];
+  nestingSettings?: CollectionNestingAndPermission = {token_owner: false, collection_admin: false, restricted: []};
+  limits?: CollectionLimitValue[] = [];
+  pendingSponsor?: [string, string] = emptyAddress;
+  flags?: number | CollectionFlag[] = [0];
+
+  constructor(
+    name: string,
+    description: string,
+    tokenPrefix: string,
+    collectionMode: TCollectionMode,
+    decimals = 18,
+  ) {
+    this.name = name;
+    this.description = description;
+    this.tokenPrefix = tokenPrefix;
+    this.collectionMode = collectionMode;
+    if(collectionMode == 'ft')
+      this.decimals = decimals;
+    else
+      this.decimals = 0;
+  }
+}
addedjs-packages/tests/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/eth/util/playgrounds/unique.dev.ts
@@ -0,0 +1,616 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+/* eslint-disable function-call-argument-newline */
+
+import {readFile} from 'fs/promises';
+
+import * as web3 from 'web3';
+import {WebsocketProvider} from 'web3-core';
+import {Contract} from 'web3-eth-contract';
+
+// @ts-ignore
+import solc from 'solc';
+
+import {evmToAddress} from '@polkadot/util-crypto';
+import type {IKeyringPair} from '@polkadot/types/types';
+
+import {ArrangeGroup, DevUniqueHelper} from '@unique/playgrounds/unique.dev.js';
+
+import type {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types.js';
+import {CollectionMode, CreateCollectionData} from './types.js';
+
+// Native contracts ABI
+import collectionHelpersAbi from '../../abi/collectionHelpers.json' assert {type: 'json'};
+import nativeFungibleAbi from '../../abi/nativeFungible.json' assert {type: 'json'};
+import fungibleAbi from '../../abi/fungible.json' assert {type: 'json'};
+import fungibleDeprecatedAbi from '../../abi/fungibleDeprecated.json' assert {type: 'json'};
+import nonFungibleAbi from '../../abi/nonFungible.json' assert {type: 'json'};
+import nonFungibleDeprecatedAbi from '../../abi/nonFungibleDeprecated.json' assert {type: 'json'};
+import refungibleAbi from '../../abi/reFungible.json' assert {type: 'json'};
+import refungibleDeprecatedAbi from '../../abi/reFungibleDeprecated.json' assert {type: 'json'};
+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';
+
+class EthGroupBase {
+  helper: EthUniqueHelper;
+  gasPrice?: string;
+
+  constructor(helper: EthUniqueHelper) {
+    this.helper = helper;
+  }
+  async getGasPrice() {
+    if(this.gasPrice)
+      return this.gasPrice;
+    this.gasPrice = await this.helper.getWeb3().eth.getGasPrice();
+    return this.gasPrice;
+  }
+}
+
+class ContractGroup extends EthGroupBase {
+  async findImports(imports?: ContractImports[]) {
+    if(!imports) return function(path: string) {
+      return {error: `File not found: ${path}`};
+    };
+
+    const knownImports = {} as { [key: string]: string };
+    for(const imp of imports) {
+      knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString();
+    }
+
+    return function(path: string) {
+      if(path in knownImports) return {contents: knownImports[path]};
+      return {error: `File not found: ${path}`};
+    };
+  }
+
+  async compile(name: string, src: string, imports?: ContractImports[]): Promise<CompiledContract> {
+    const compiled = JSON.parse(solc.compile(JSON.stringify({
+      language: 'Solidity',
+      sources: {
+        [`${name}.sol`]: {
+          content: src,
+        },
+      },
+      settings: {
+        outputSelection: {
+          '*': {
+            '*': ['*'],
+          },
+        },
+      },
+    }), {import: await this.findImports(imports)}));
+
+    const hasErrors = compiled['errors']
+      && compiled['errors'].length > 0
+      && compiled.errors.some(function(err: any) {
+        return err.severity == 'error';
+      });
+
+    if(hasErrors) {
+      throw compiled.errors;
+    }
+    const out = compiled.contracts[`${name}.sol`][name];
+
+    return {
+      abi: out.abi,
+      object: '0x' + out.evm.bytecode.object,
+    };
+  }
+
+  async deployByCode(signer: string, name: string, src: string, imports?: ContractImports[], gas?: number, args?: any[]): Promise<Contract> {
+    const compiledContract = await this.compile(name, src, imports);
+    return this.deployByAbi(signer, compiledContract.abi, compiledContract.object, gas, args);
+  }
+
+  async deployByAbi(signer: string, abi: any, object: string, gas?: number, args?: any[]): Promise<Contract> {
+    const web3 = this.helper.getWeb3();
+    const contract = new web3.eth.Contract(abi, undefined, {
+      data: object,
+      from: signer,
+      gas: gas ?? this.helper.eth.DEFAULT_GAS,
+    });
+    return await contract.deploy({data: object, arguments: args}).send({from: signer});
+  }
+
+}
+
+class NativeContractGroup extends EthGroupBase {
+
+  contractHelpers(caller: string) {
+    const web3 = this.helper.getWeb3();
+    return new web3.eth.Contract(contractHelpersAbi as any, this.helper.getApi().consts.evmContractHelpers.contractAddress.toString(), {
+      from: caller,
+      gas: this.helper.eth.DEFAULT_GAS,
+    });
+  }
+
+  collectionHelpers(caller: string) {
+    const web3 = this.helper.getWeb3();
+    return new web3.eth.Contract(collectionHelpersAbi as any, this.helper.getApi().consts.common.contractAddress.toString(), {
+      from: caller,
+      gas: this.helper.eth.DEFAULT_GAS,
+    });
+  }
+
+  collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) {
+    let abi;
+    if(address === this.helper.ethAddress.fromCollectionId(0)) {
+      abi = nativeFungibleAbi;
+    } else {
+      abi ={
+        'nft': nonFungibleAbi,
+        'rft': refungibleAbi,
+        'ft': fungibleAbi,
+      }[mode];
+    }
+    if(mergeDeprecated) {
+      const deprecated = {
+        'nft': nonFungibleDeprecatedAbi,
+        'rft': refungibleDeprecatedAbi,
+        'ft': fungibleDeprecatedAbi,
+      }[mode];
+      abi = [...abi, ...deprecated];
+    }
+    const web3 = this.helper.getWeb3();
+    return new web3.eth.Contract(abi as any, address, {
+      gas: this.helper.eth.DEFAULT_GAS,
+      ...(caller ? {from: caller} : {}),
+    });
+  }
+
+  collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string, mergeDeprecated = false) {
+    return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller, mergeDeprecated);
+  }
+
+  rftToken(address: string, caller?: string, mergeDeprecated = false) {
+    const web3 = this.helper.getWeb3();
+    const abi = mergeDeprecated ? [...refungibleTokenAbi, ...refungibleTokenDeprecatedAbi] : refungibleTokenAbi;
+    return new web3.eth.Contract(abi as any, address, {
+      gas: this.helper.eth.DEFAULT_GAS,
+      ...(caller ? {from: caller} : {}),
+    });
+  }
+
+  rftTokenById(collectionId: number, tokenId: number, caller?: string, mergeDeprecated = false) {
+    return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller, mergeDeprecated);
+  }
+}
+
+class CreateCollectionTransaction {
+  signer: string;
+  data: CreateCollectionData;
+  mergeDeprecated: boolean;
+  helper: EthUniqueHelper;
+
+  constructor(helper: EthUniqueHelper, signer: string, data: CreateCollectionData, mergeDeprecated = false) {
+    this.helper = helper;
+    this.signer = signer;
+
+    let flags = 0;
+    // convert CollectionFlags to number and join them in one number
+    if(!data.flags) {
+      flags = 0;
+    } else if(typeof data.flags == 'number') {
+      flags = data.flags;
+    } else {
+      for(let i = 0; i < data.flags.length; i++){
+        const flag = data.flags[i];
+        flags = flags | flag;
+      }
+    }
+    data.flags = flags;
+
+    this.data = data;
+    this.mergeDeprecated = mergeDeprecated;
+  }
+
+  // eslint-disable-next-line require-await
+  private async createTransaction() {
+    const collectionHelper = this.helper.ethNativeContract.collectionHelpers(this.signer);
+    let collectionMode;
+    switch (this.data.collectionMode) {
+      case 'nft': collectionMode = CollectionMode.Nonfungible; break;
+      case 'rft': collectionMode = CollectionMode.Refungible; break;
+      case 'ft': collectionMode = CollectionMode.Fungible; break;
+    }
+
+    const tx = collectionHelper.methods.createCollection([
+      this.data.name,
+      this.data.description,
+      this.data.tokenPrefix,
+      collectionMode,
+      this.data.decimals,
+      this.data.properties,
+      this.data.tokenPropertyPermissions,
+      this.data.adminList,
+      this.data.nestingSettings,
+      this.data.limits,
+      this.data.pendingSponsor,
+      this.data.flags,
+    ]);
+    return tx;
+  }
+
+  async send(options?: any): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[], collection: Contract }> {
+    const collectionCreationPrice = {
+      value: Number(this.helper.balance.getCollectionCreationPrice()),
+    };
+    const tx = await this.createTransaction();
+    const result = await tx.send({...options, ...collectionCreationPrice});
+
+    const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
+    const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
+    const events = this.helper.eth.normalizeEvents(result.events);
+    const collection = await this.helper.ethNativeContract.collectionById(collectionId, this.data.collectionMode, this.signer, this.mergeDeprecated);
+
+    return {collectionId, collectionAddress, events, collection};
+  }
+
+  async call(options?: any) {
+    const collectionCreationPrice = {
+      value: Number(this.helper.balance.getCollectionCreationPrice()),
+    };
+    const tx = await this.createTransaction();
+
+    return await tx.call({...options, ...collectionCreationPrice});
+  }
+}
+
+
+class EthGroup extends EthGroupBase {
+  DEFAULT_GAS = 2_500_000;
+
+  createAccount() {
+    const web3 = this.helper.getWeb3();
+    const account = web3.eth.accounts.create();
+    web3.eth.accounts.wallet.add(account.privateKey);
+    return account.address;
+  }
+
+  async createAccountWithBalance(donor: IKeyringPair, amount = 600n) {
+    const account = this.createAccount();
+    await this.transferBalanceFromSubstrate(donor, account, amount);
+
+    return account;
+  }
+
+  async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount = 100n, inTokens = true) {
+    return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n));
+  }
+
+  async getCollectionCreationFee(signer: string) {
+    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
+    return await collectionHelper.methods.collectionCreationFee().call();
+  }
+
+  async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) {
+    if(!gasLimit) gasLimit = this.DEFAULT_GAS;
+    const web3 = this.helper.getWeb3();
+    // FIXME: can't send legacy transaction using tx.evm.call
+    const gasPrice = await web3.eth.getGasPrice();
+    // TODO: check execution status
+    await this.helper.executeExtrinsic(
+      signer,
+      'api.tx.evm.call', [this.helper.address.substrateToEth(signer.address), contractAddress, abi, value, gasLimit, gasPrice, null, null, []],
+      true,
+    );
+  }
+
+  async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) {
+    return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);
+  }
+
+  createCollectionMethodName(mode: TCollectionMode) {
+    switch (mode) {
+      case 'ft':
+        return 'createFTCollection';
+      case 'nft':
+        return 'createNFTCollection';
+      case 'rft':
+        return 'createRFTCollection';
+    }
+  }
+
+  createCollection(signer: string, data: CreateCollectionData, mergeDeprecated = false): CreateCollectionTransaction {
+    return new CreateCollectionTransaction(this.helper, signer, data, mergeDeprecated);
+  }
+
+  createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
+    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();
+  }
+
+  async createERC721MetadataCompatibleCollection(signer: string, data: CreateCollectionData, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
+    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
+
+    const {collectionId, collectionAddress, events} = await this.createCollection(signer, data).send();
+
+    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
+
+    return {collectionId, collectionAddress, events};
+  }
+
+  async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
+    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
+
+    const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();
+
+    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
+
+    return {collectionId, collectionAddress, events};
+  }
+
+  createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
+    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();
+  }
+
+  createFungibleCollection(signer: string, name: string, _decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
+    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'ft')).send();
+  }
+
+  async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
+    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
+
+    const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();
+
+    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
+
+    return {collectionId, collectionAddress, events};
+  }
+
+  async deployCollectorContract(signer: string): Promise<Contract> {
+    return await this.helper.ethContract.deployByCode(signer, 'Collector', `
+    // SPDX-License-Identifier: UNLICENSED
+    pragma solidity ^0.8.6;
+
+    contract Collector {
+      uint256 collected;
+      fallback() external payable {
+        giveMoney();
+      }
+      function giveMoney() public payable {
+        collected += msg.value;
+      }
+      function getCollected() public view returns (uint256) {
+        return collected;
+      }
+      function getUnaccounted() public view returns (uint256) {
+        return address(this).balance - collected;
+      }
+
+      function withdraw(address payable target) public {
+        target.transfer(collected);
+        collected = 0;
+      }
+    }
+  `);
+  }
+
+  async deployFlipper(signer: string): Promise<Contract> {
+    return await this.helper.ethContract.deployByCode(signer, 'Flipper', `
+    // SPDX-License-Identifier: UNLICENSED
+    pragma solidity ^0.8.6;
+
+    contract Flipper {
+      bool value = false;
+      function flip() public {
+        value = !value;
+      }
+      function getValue() public view returns (bool) {
+        return value;
+      }
+    }
+  `);
+  }
+
+  async recordCallFee(user: string, call: () => Promise<any>): Promise<bigint> {
+    const before = await this.helper.balance.getEthereum(user);
+    await call();
+    // In dev mode, the transaction might not finish processing in time
+    await this.helper.wait.newBlocks(1);
+    const after = await this.helper.balance.getEthereum(user);
+
+    return before - after;
+  }
+
+  normalizeEvents(events: any): NormalizedEvent[] {
+    const output = [];
+    for(const key of Object.keys(events)) {
+      if(key.match(/^[0-9]+$/)) {
+        output.push(events[key]);
+      } else if(Array.isArray(events[key])) {
+        output.push(...events[key]);
+      } else {
+        output.push(events[key]);
+      }
+    }
+    output.sort((a, b) => a.logIndex - b.logIndex);
+    return output.map(({address, event, returnValues}) => {
+      const args: { [key: string]: string } = {};
+      for(const key of Object.keys(returnValues)) {
+        if(!key.match(/^[0-9]+$/)) {
+          args[key] = returnValues[key];
+        }
+      }
+      return {
+        address,
+        event,
+        args,
+      };
+    });
+  }
+
+  async calculateFee(address: ICrossAccountId, code: () => Promise<any>): Promise<bigint> {
+    const wrappedCode = async () => {
+      await code();
+      // In dev mode, the transaction might not finish processing in time
+      await this.helper.wait.newBlocks(1);
+    };
+    return await this.helper.arrange.calculcateFee(address, wrappedCode);
+  }
+}
+
+class EthAddressGroup extends EthGroupBase {
+  extractCollectionId(address: string): number {
+    if(!(address.length === 42 || address.length === 40)) throw new Error('address wrong format');
+    return parseInt(address.slice(address.length - 8), 16);
+  }
+
+  fromCollectionId(collectionId: number): string {
+    if(collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow');
+    return (web3 as any).utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8, '0')}`);
+  }
+
+  extractTokenId(address: string): { collectionId: number, tokenId: number } {
+    if(!address.startsWith('0x'))
+      throw 'address not starts with "0x"';
+    if(address.length > 42)
+      throw 'address length is more than 20 bytes';
+    return {
+      collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)),
+      tokenId: Number('0x' + address.substring(address.length - 8)),
+    };
+  }
+
+  fromTokenId(collectionId: number, tokenId: number): string {
+    return this.helper.util.getTokenAddress({collectionId, tokenId});
+  }
+
+  normalizeAddress(address: string): string {
+    return '0x' + address.substring(address.length - 40);
+  }
+}
+export class EthPropertyGroup extends EthGroupBase {
+  property(key: string, value: string): EthProperty {
+    return [
+      key,
+      '0x' + Buffer.from(value).toString('hex'),
+    ];
+  }
+}
+export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper;
+
+export class EthCrossAccountGroup extends EthGroupBase {
+  createAccount(): CrossAddress {
+    return this.fromAddress(this.helper.eth.createAccount());
+  }
+
+  async createAccountWithBalance(donor: IKeyringPair, amount = 100n) {
+    return this.fromAddress(await this.helper.eth.createAccountWithBalance(donor, amount));
+  }
+
+  fromAddress(address: TEthereumAccount): CrossAddress {
+    return {
+      eth: address,
+      sub: '0',
+    };
+  }
+
+  fromAddr(address: TEthereumAccount): [string, string] {
+    return [
+      address,
+      '0',
+    ];
+  }
+
+  fromKeyringPair(keyring: IKeyringPair): CrossAddress {
+    return {
+      eth: '0x0000000000000000000000000000000000000000',
+      sub: keyring.addressRaw,
+    };
+  }
+}
+
+export class FeeGas {
+  fee: number | bigint = 0n;
+
+  gas: number | bigint = 0n;
+
+  public static async build(helper: EthUniqueHelper, fee: bigint): Promise<FeeGas> {
+    const instance = new FeeGas();
+    instance.fee = instance.convertToTokens(fee);
+    instance.gas = await instance.convertToGas(fee, helper);
+    return instance;
+  }
+
+  private async convertToGas(fee: bigint, helper: EthUniqueHelper): Promise<bigint> {
+    const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());
+    return fee / gasPrice;
+  }
+
+  private convertToTokens(value: bigint, nominal = 1_000_000_000_000_000_000n): number {
+    return Number((value * 1000n) / nominal) / 1000;
+  }
+}
+
+class EthArrangeGroup extends ArrangeGroup {
+  declare helper: EthUniqueHelper;
+
+  constructor(helper: EthUniqueHelper) {
+    super(helper);
+    this.helper = helper;
+  }
+
+  async calculcateFeeGas(payer: ICrossAccountId, promise: () => Promise<any>): Promise<FeeGas> {
+    const fee = await this.calculcateFee(payer, promise);
+    return await FeeGas.build(this.helper, fee);
+  }
+}
+export class EthUniqueHelper extends DevUniqueHelper {
+  web3: web3.default | null = null;
+  web3Provider: WebsocketProvider | null = null;
+
+  eth: EthGroup;
+  ethAddress: EthAddressGroup;
+  ethCrossAccount: EthCrossAccountGroup;
+  ethNativeContract: NativeContractGroup;
+  ethContract: ContractGroup;
+  ethProperty: EthPropertyGroup;
+  declare arrange: EthArrangeGroup;
+  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: { [key: string]: any } = {}) {
+    options.helperBase = options.helperBase ?? EthUniqueHelper;
+
+    super(logger, options);
+    this.eth = new EthGroup(this);
+    this.ethAddress = new EthAddressGroup(this);
+    this.ethCrossAccount = new EthCrossAccountGroup(this);
+    this.ethNativeContract = new NativeContractGroup(this);
+    this.ethContract = new ContractGroup(this);
+    this.ethProperty = new EthPropertyGroup(this);
+    this.arrange = new EthArrangeGroup(this);
+    super.arrange = this.arrange;
+  }
+
+  getWeb3(): web3.default {
+    if(this.web3 === null) throw Error('Web3 not connected');
+    return this.web3;
+  }
+
+  connectWeb3(wsEndpoint: string) {
+    if(this.web3 !== null) return;
+    this.web3Provider = new (web3 as any).providers.WebsocketProvider(wsEndpoint);
+    this.web3 = new (web3 as any)(this.web3Provider);
+  }
+
+  override async disconnect() {
+    if(this.web3 === null) return;
+    this.web3Provider?.connection.close();
+
+    await super.disconnect();
+  }
+
+  override clearApi() {
+    super.clearApi();
+    this.web3 = null;
+  }
+
+  override clone(helperCls: EthUniqueHelperConstructor, options?: { [key: string]: any; }): EthUniqueHelper {
+    const newHelper = super.clone(helperCls, options) as EthUniqueHelper;
+    newHelper.web3 = this.web3;
+    newHelper.web3Provider = this.web3Provider;
+
+    return newHelper;
+  }
+}
addedjs-packages/tests/fungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/fungible.test.ts
@@ -0,0 +1,186 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect, requirePalletsOrSkip, Pallets} from './util/index.js';
+
+const U128_MAX = (1n << 128n) - 1n;
+
+describe('integration test: Fungible functionality:', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
+    });
+  });
+
+  itSub('Create fungible collection and token', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'trest'});
+    const defaultTokenId = await collection.getLastTokenId();
+    expect(defaultTokenId).to.be.equal(0);
+
+    await collection.mint(alice, U128_MAX);
+    const aliceBalance = await collection.getBalance({Substrate: alice.address});
+    const itemCountAfter = await collection.getLastTokenId();
+
+    expect(itemCountAfter).to.be.equal(defaultTokenId);
+    expect(aliceBalance).to.be.equal(U128_MAX);
+  });
+
+  itSub('RPC method tokenOnewrs for fungible collection and token', async ({helper}) => {
+    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address}));
+
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    await collection.mint(alice, U128_MAX);
+
+    await collection.transfer(alice, {Substrate: bob.address}, 1000n);
+    await collection.transfer(alice, ethAcc, 900n);
+
+    for(let i = 0; i < 7; i++) {
+      await collection.transfer(alice, facelessCrowd[i], 1n);
+    }
+
+    const owners = await collection.getTop10Owners();
+
+    // What to expect
+    expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]);
+    expect(owners.length).to.be.equal(10);
+
+    const [eleven] = await helper.arrange.createAccounts([0n], donor);
+    expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true;
+    expect((await collection.getTop10Owners()).length).to.be.equal(10);
+  });
+
+  itSub('Transfer token', async ({helper}) => {
+    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    await collection.mint(alice, 500n);
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n);
+    expect(await collection.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true;
+    expect(await collection.transfer(alice, ethAcc, 140n)).to.be.true;
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(300n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n);
+    expect(await collection.getBalance(ethAcc)).to.be.equal(140n);
+
+    await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub('Tokens multiple creation', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    await collection.mintWithOneOwner(alice, [
+      {value: 500n},
+      {value: 400n},
+      {value: 300n},
+    ]);
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1200n);
+  });
+
+  itSub('Burn some tokens ', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    await collection.mint(alice, 500n);
+
+    expect(await collection.doesTokenExist(0)).to.be.true;
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n);
+    expect(await collection.burnTokens(alice, 499n)).to.be.true;
+    expect(await collection.doesTokenExist(0)).to.be.true;
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n);
+  });
+
+  itSub('Burn all tokens ', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    await collection.mint(alice, 500n);
+
+    expect(await collection.doesTokenExist(0)).to.be.true;
+    expect(await collection.burnTokens(alice, 500n)).to.be.true;
+    expect(await collection.doesTokenExist(0)).to.be.true;
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(0n);
+    expect(await collection.getTotalPieces()).to.be.equal(0n);
+  });
+
+  itSub('Set allowance for token', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+    await collection.mint(alice, 100n);
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(100n);
+
+    expect(await collection.approveTokens(alice, {Substrate: bob.address}, 60n)).to.be.true;
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n);
+
+    expect(await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true;
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(80n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(20n);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n);
+
+    await collection.burnTokensFrom(bob, {Substrate: alice.address}, 10n);
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(70n);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(30n);
+    expect(await collection.transferFrom(bob, {Substrate: alice.address}, ethAcc, 10n)).to.be.true;
+    expect(await collection.getBalance(ethAcc)).to.be.equal(10n);
+  });
+});
+
+describe('Fungible negative tests', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub('Cannot transfer incorrect amount of tokens', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const nonExistingCollection = helper.ft.getCollectionObject(99999);
+    await collection.mint(alice, 10n, {Substrate: bob.address});
+
+    // 1. Alice cannot transfer more than 0 tokens if balance low:
+    await expect(collection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow');
+    await expect(collection.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow');
+
+    // 2. Alice cannot transfer non-existing token:
+    await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.CollectionNotFound');
+    await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.CollectionNotFound');
+
+    // 3. Zero transfer allowed (EIP-20):
+    await collection.transfer(bob, {Substrate: charlie.address}, 0n);
+    // 3.1 even if the balance = 0
+    await collection.transfer(alice, {Substrate: charlie.address}, 0n);
+
+    expect(await collection.getBalance({Substrate: alice.address})).to.eq(0n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.eq(10n);
+    expect(await collection.getBalance({Substrate: charlie.address})).to.eq(0n);
+  });
+});
addedjs-packages/tests/getPropertiesRpc.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/getPropertiesRpc.test.ts
@@ -0,0 +1,150 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {UniqueHelper, UniqueNFTCollection} from '@unique/playgrounds/unique.js';
+
+const collectionProps = [
+  {key: 'col-0', value: 'col-0-value'},
+  {key: 'col-1', value: 'col-1-value'},
+];
+
+const tokenProps = [
+  {key: 'tok-0', value: 'tok-0-value'},
+  {key: 'tok-1', value: 'tok-1-value'},
+];
+
+const tokPropPermission = {
+  mutable: false,
+  tokenOwner: true,
+  collectionAdmin: false,
+};
+
+const tokenPropPermissions = [
+  {
+    key: 'tok-0',
+    permission: tokPropPermission,
+  },
+  {
+    key: 'tok-1',
+    permission: tokPropPermission,
+  },
+];
+
+describe('query properties RPC', () => {
+  let alice: IKeyringPair;
+
+  const mintCollection = async (helper: UniqueHelper) => await helper.nft.mintCollection(alice, {
+    tokenPrefix: 'prps',
+    properties: collectionProps,
+    tokenPropertyPermissions: tokenPropPermissions,
+  });
+
+  const mintToken = async (collection: UniqueNFTCollection) => await collection.mintToken(alice, {Substrate: alice.address}, tokenProps);
+
+
+  before(async () => {
+    await usingPlaygrounds(async (_, privateKey) => {
+      alice = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itSub('query empty collection key set', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const props = await collection.getProperties([]);
+    expect(props).to.be.empty;
+  });
+
+  itSub('query empty token key set', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const token = await mintToken(collection);
+    const props = await token.getProperties([]);
+    expect(props).to.be.empty;
+  });
+
+  itSub('query empty token key permissions set', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const propPermissions = await collection.getPropertyPermissions([]);
+    expect(propPermissions).to.be.empty;
+  });
+
+  itSub('query all collection props by null arg', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const props = await collection.getProperties(null);
+    expect(props).to.be.deep.equal(collectionProps);
+  });
+
+  itSub('query all token props by null arg', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const token = await mintToken(collection);
+    const props = await token.getProperties(null);
+    expect(props).to.be.deep.equal(tokenProps);
+  });
+
+  itSub('query empty token key permissions by null arg', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const propPermissions = await collection.getPropertyPermissions(null);
+    expect(propPermissions).to.be.deep.equal(tokenPropPermissions);
+  });
+
+  itSub('query all collection props by undefined arg', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const props = await collection.getProperties();
+    expect(props).to.be.deep.equal(collectionProps);
+  });
+
+  itSub('query all token props by undefined arg', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const token = await mintToken(collection);
+    const props = await token.getProperties();
+    expect(props).to.be.deep.equal(tokenProps);
+  });
+
+  itSub('query empty token key permissions by undefined arg', async ({helper}) => {
+    const collection = await mintCollection(helper);
+    const propPermissions = await collection.getPropertyPermissions();
+    expect(propPermissions).to.be.deep.equal(tokenPropPermissions);
+  });
+});
+
+[
+  {mode: 'nft' as const},
+  {mode: 'rft' as const},
+].map(testCase =>
+  describe('negative properties', () => {
+    let alice: IKeyringPair;
+
+    before(async () => {
+      await usingPlaygrounds(async (_, privateKey) => {
+        alice = await privateKey({url: import.meta.url});
+      });
+    });
+
+    itSub(`[${testCase.mode}] set token property for non-existent token`, async ({helper}) => {
+      const collection = await helper[testCase.mode].mintCollection(alice);
+      await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]);
+      await expect(collection.setTokenProperties(alice, 1, [{key: 'key', value: 'value'}])).to.be.rejectedWith('common.TokenNotFound');
+      expect(await collection.getTokenProperties(1, ['key'])).to.be.empty;
+    });
+
+    itSub(`[${testCase.mode}] delete token property for non-existent token`, async ({helper}) => {
+      const collection = await helper[testCase.mode].mintCollection(alice);
+      await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]);
+      await expect(collection.deleteTokenProperties(alice, 1, ['key'])).to.be.rejectedWith('common.TokenNotFound');
+      expect(await collection.getTokenProperties(1, ['key'])).to.be.empty;
+    });
+  }));
addedjs-packages/tests/governance/council.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/governance/council.test.ts
@@ -0,0 +1,457 @@
+
+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 {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';
+
+describeGov('Governance: Council tests', () => {
+  let donor: IKeyringPair;
+  let counselors: ICounselors;
+  let sudoer: IKeyringPair;
+
+  const moreThanHalfCouncilThreshold = 3;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Council]);
+
+      donor = await privateKey({url: import.meta.url});
+      sudoer = await privateKey('//Alice');
+    });
+  });
+
+  beforeEach(async () => {
+    counselors = await initCouncil(donor, sudoer);
+  });
+
+  afterEach(async () => {
+    await clearCouncil(sudoer);
+    await clearTechComm(sudoer);
+  });
+
+  async function proposalFromMoreThanHalfCouncil(proposal: any) {
+    return await usingPlaygrounds(async (helper) => {
+      expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5);
+      const proposeResult = await helper.council.collective.propose(
+        counselors.filip,
+        proposal,
+        moreThanHalfCouncilThreshold,
+      );
+
+      const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+      const proposalIndex = councilProposedEvent.proposalIndex;
+      const proposalHash = councilProposedEvent.proposalHash;
+
+
+      await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
+      await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);
+      await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);
+
+      return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
+    });
+  }
+
+  async function proposalFromAllCouncil(proposal: any) {
+    return await usingPlaygrounds(async (helper) => {
+      expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5);
+      const proposeResult = await helper.council.collective.propose(
+        counselors.filip,
+        proposal,
+        moreThanHalfCouncilThreshold,
+      );
+
+      const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+      const proposalIndex = councilProposedEvent.proposalIndex;
+      const proposalHash = councilProposedEvent.proposalHash;
+
+
+      await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
+      await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);
+      await helper.council.collective.vote(counselors.ildar, proposalHash, proposalIndex, true);
+      await helper.council.collective.vote(counselors.irina, proposalHash, proposalIndex, true);
+      await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);
+
+      return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
+    });
+  }
+
+  itSub('>50% of Council can externally propose SuperMajorityAgainst', async ({helper}) => {
+    const forceSetBalanceReceiver = helper.arrange.createEmptyAccount();
+    const forceSetBalanceTestValue = 20n * 10n ** 25n;
+
+    const democracyProposal = await helper.constructApiCall('api.tx.balances.forceSetBalance', [
+      forceSetBalanceReceiver.address, forceSetBalanceTestValue,
+    ]);
+
+    const councilProposal = await helper.democracy.externalProposeDefaultCall(democracyProposal);
+
+    const proposeResult = await helper.council.collective.propose(
+      counselors.filip,
+      councilProposal,
+      moreThanHalfCouncilThreshold,
+    );
+
+    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+    const proposalIndex = councilProposedEvent.proposalIndex;
+    const proposalHash = councilProposedEvent.proposalHash;
+
+    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
+    await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);
+    await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);
+
+    await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
+
+    const democracyStartedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+    const democracyReferendumIndex = democracyStartedEvent.referendumIndex;
+    const democracyThreshold = democracyStartedEvent.threshold;
+
+    expect(democracyThreshold).to.be.equal('SuperMajorityAgainst');
+
+    await helper.democracy.vote(counselors.filip, democracyReferendumIndex, {
+      Standard: {
+        vote: {
+          aye: true,
+          conviction: 1,
+        },
+        balance: 10_000n,
+      },
+    });
+
+    await helper.democracy.vote(counselors.charu, democracyReferendumIndex, {
+      Standard: {
+        vote: {
+          aye: false,
+          conviction: 1,
+        },
+        balance: 50_000n,
+      },
+    });
+
+    const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed);
+    expect(passedReferendumEvent.referendumIndex).to.be.equal(democracyReferendumIndex);
+
+    await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched);
+    const receiverBalance = await helper.balance.getSubstrate(forceSetBalanceReceiver.address);
+    expect(receiverBalance).to.be.equal(forceSetBalanceTestValue);
+  });
+
+  itSub('Council prime member vote is the default', async ({helper}) => {
+    const newTechCommMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address);
+    const proposeResult = await helper.council.collective.propose(
+      counselors.filip,
+      addMemberProposal,
+      moreThanHalfCouncilThreshold,
+    );
+
+    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+    const proposalIndex = councilProposedEvent.proposalIndex;
+    const proposalHash = councilProposedEvent.proposalHash;
+
+    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
+
+    await helper.wait.newBlocks(councilMotionDuration);
+    const closeResult = await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
+    const closeEvent = Event.Council.Closed.expect(closeResult);
+    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as string[];
+    expect(closeEvent.yes).to.be.equal(members.length);
+  });
+
+  itSub('Superuser can add a member', async ({helper}) => {
+    const newMember = helper.arrange.createEmptyAccount();
+    await expect(helper.getSudo().council.membership.addMember(sudoer, newMember.address)).to.be.fulfilled;
+
+    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
+    expect(members).to.contains(newMember.address);
+  });
+
+  itSub('Superuser can remove a member', async ({helper}) => {
+    await expect(helper.getSudo().council.membership.removeMember(sudoer, counselors.alex.address)).to.be.fulfilled;
+
+    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
+    expect(members).to.not.contains(counselors.alex.address);
+  });
+
+  itSub('>50% Council can add TechComm member', async ({helper}) => {
+    const newTechCommMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address);
+
+    await proposalFromMoreThanHalfCouncil(addMemberProposal);
+
+    const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
+    expect(techCommMembers).to.contains(newTechCommMember.address);
+  });
+
+  itSub('Council can remove TechComm member', async ({helper}) => {
+    const techComm = await initTechComm(donor, sudoer);
+    const removeMemberPrpoposal = helper.technicalCommittee.membership.removeMemberCall(techComm.andy.address);
+    await proposalFromMoreThanHalfCouncil(removeMemberPrpoposal);
+
+    const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
+    expect(techCommMembers).to.not.contains(techComm.andy.address);
+  });
+
+  itSub.skip('Council member can add Fellowship member', async ({helper}) => {
+    const newFellowshipMember = helper.arrange.createEmptyAccount();
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.fellowship.collective.addMemberCall(newFellowshipMember.address),
+    )).to.be.fulfilled;
+    const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON();
+    expect(fellowshipMembers).to.contains(newFellowshipMember.address);
+  });
+
+  itSub('>50% Council can promote Fellowship member', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+    const memberWithZeroRank = fellowship[0][0];
+
+    const proposal = helper.fellowship.collective.promoteCall(memberWithZeroRank.address);
+    await proposalFromMoreThanHalfCouncil(proposal);
+    const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithZeroRank.address])).toJSON();
+    expect(record).to.be.deep.equal({rank: 1});
+
+    await clearFellowship(sudoer);
+  });
+
+  itSub('>50% Council can demote Fellowship member', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+    const memberWithRankOne = fellowship[1][0];
+
+    const proposal = helper.fellowship.collective.demoteCall(memberWithRankOne.address);
+    await proposalFromMoreThanHalfCouncil(proposal);
+
+    const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithRankOne.address])).toJSON();
+    expect(record).to.be.deep.equal({rank: 0});
+
+    await clearFellowship(sudoer);
+  });
+
+  itSub('>50% Council can add\remove Fellowship member', async ({helper}) => {
+    try {
+      const newMember = helper.arrange.createEmptyAccount();
+
+      const proposalAdd = helper.fellowship.collective.addMemberCall(newMember.address);
+      const proposalRemove = helper.fellowship.collective.removeMemberCall(newMember.address, fellowshipRankLimit);
+      await expect(proposalFromMoreThanHalfCouncil(proposalAdd)).to.be.fulfilled;
+      expect(await helper.fellowship.collective.getMembers()).to.be.deep.contain(newMember.address);
+      await expect(proposalFromMoreThanHalfCouncil(proposalRemove)).to.be.fulfilled;
+      expect(await helper.fellowship.collective.getMembers()).to.be.not.deep.contain(newMember.address);
+    }
+    finally {
+      await clearFellowship(sudoer);
+    }
+  });
+
+  itSub('Council can blacklist Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await expect(proposalFromAllCouncil(helper.democracy.blacklistCall(preimageHash, null))).to.be.fulfilled;
+  });
+
+  itSub('Sudo can blacklist Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await expect(helper.getSudo().democracy.blacklist(sudoer, preimageHash)).to.be.fulfilled;
+  });
+
+  itSub('[Negative] Council cannot add Council member', async ({helper}) => {
+    const newCouncilMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
+
+    await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejected;
+  });
+
+  itSub('[Negative] Council cannot remove Council member', async ({helper}) => {
+    const removeMemberProposal = helper.council.membership.removeMemberCall(counselors.alex.address);
+
+    await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejected;
+  });
+
+  itSub('[Negative] Council cannot submit regular democracy proposal', async ({helper}) => {
+    const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
+
+    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Council cannot externally propose SimpleMajority', async ({helper}) => {
+    const councilProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
+
+    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Council cannot externally propose SuperMajorityApprove', async ({helper}) => {
+    const councilProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
+
+    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Council member cannot add/remove a Council member', async ({helper}) => {
+    const newCouncilMember = helper.arrange.createEmptyAccount();
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.council.membership.addMemberCall(newCouncilMember.address),
+    )).to.be.rejectedWith('BadOrigin');
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.council.membership.removeMemberCall(counselors.charu.address),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council cannot set/clear Council prime member', async ({helper}) => {
+    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
+    const proposalForClear = await helper.council.membership.clearPrimeCall();
+
+    await expect(proposalFromAllCouncil(proposalForSet)).to.be.rejectedWith(/BadOrigin/);
+    await expect(proposalFromAllCouncil(proposalForClear)).to.be.rejectedWith(/BadOrigin/);
+
+  });
+
+  itSub('[Negative] Council member cannot set/clear Council prime member', async ({helper}) => {
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.council.membership.setPrimeCall(counselors.charu.address),
+    )).to.be.rejectedWith('BadOrigin');
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.council.membership.clearPrimeCall(),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council member cannot add/remove a TechComm member', async ({helper}) => {
+    const newTechCommMember = helper.arrange.createEmptyAccount();
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address),
+    )).to.be.rejectedWith('BadOrigin');
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.technicalCommittee.membership.removeMemberCall(newTechCommMember.address),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council member cannot promote/demote a Fellowship member', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+    const memberWithRankOne = fellowship[1][0];
+
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.fellowship.collective.promoteCall(memberWithRankOne.address),
+    )).to.be.rejectedWith('BadOrigin');
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.fellowship.collective.demoteCall(memberWithRankOne.address),
+    )).to.be.rejectedWith('BadOrigin');
+    await clearFellowship(sudoer);
+  });
+
+  itSub('[Negative] Council cannot fast-track Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await expect(proposalFromAllCouncil(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)))
+      .to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Council member cannot fast-track Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council cannot cancel Democracy proposals', async ({helper}) => {
+    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
+    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+
+    await expect(proposalFromAllCouncil(helper.democracy.cancelProposalCall(proposalIndex)))
+      .to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Council member cannot cancel Democracy proposals', async ({helper}) => {
+
+    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
+    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.democracy.cancelProposalCall(proposalIndex),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council cannot cancel ongoing Democracy referendums', async ({helper}) => {
+    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
+    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+    const referendumIndex = startedEvent.referendumIndex;
+
+    await expect(proposalFromAllCouncil(helper.democracy.emergencyCancelCall(referendumIndex)))
+      .to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Council member cannot cancel ongoing Democracy referendums', async ({helper}) => {
+    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
+    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+    const referendumIndex = startedEvent.referendumIndex;
+
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.democracy.emergencyCancelCall(referendumIndex),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council cannot cancel Fellowship referendums', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+    const fellowshipProposer = fellowship[5][0];
+    const proposal = dummyProposal(helper);
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      fellowshipProposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+
+    await expect(proposalFromAllCouncil(helper.fellowship.referenda.cancelCall(referendumIndex)))
+      .to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Council member cannot cancel Fellowship referendums', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+    const fellowshipProposer = fellowship[5][0];
+    const proposal = dummyProposal(helper);
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      fellowshipProposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+    await expect(helper.council.collective.execute(
+      counselors.alex,
+      helper.fellowship.referenda.cancelCall(referendumIndex),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] Council referendum cannot be closed until the voting threshold is met', async ({helper}) => {
+    const councilSize = (await helper.callRpc('api.query.councilMembership.members')).toJSON().length as any as number;
+    expect(councilSize).is.greaterThan(1);
+    const proposeResult = await helper.council.collective.propose(
+      counselors.filip,
+      dummyProposalCall(helper),
+      councilSize,
+    );
+
+    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
+    const proposalIndex = councilProposedEvent.proposalIndex;
+    const proposalHash = councilProposedEvent.proposalHash;
+
+
+    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
+    await expect(helper.council.collective.close(counselors.filip, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');
+  });
+
+});
addedjs-packages/tests/governance/democracy.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/governance/democracy.test.ts
@@ -0,0 +1,89 @@
+import type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js';
+import {clearFellowship, democracyLaunchPeriod, democracyTrackMinRank, dummyProposalCall, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, fellowshipPreparePeriod, fellowshipPropositionOrigin, initFellowship, voteUnanimouslyInFellowship} from './util.js';
+import {Event} from '@unique/playgrounds/unique.dev.js';
+
+describeGov('Governance: Democracy tests', () => {
+  let regularUser: IKeyringPair;
+  let donor: IKeyringPair;
+  let sudoer: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Democracy]);
+
+      donor = await privateKey({url: import.meta.url});
+      sudoer = await privateKey('//Alice');
+
+      [regularUser] = await helper.arrange.createAccounts([1000n], donor);
+    });
+  });
+
+  itSub('Regular user can vote', async ({helper}) => {
+    const fellows = await initFellowship(donor, sudoer);
+    const rank1Proposer = fellows[1][0];
+
+    const democracyProposalCall = dummyProposalCall(helper);
+    const fellowshipProposal = {
+      Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(),
+    };
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      rank1Proposer,
+      fellowshipPropositionOrigin,
+      fellowshipProposal,
+      {After: 0},
+    );
+
+    const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+    await voteUnanimouslyInFellowship(helper, fellows, democracyTrackMinRank, fellowshipReferendumIndex);
+    await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex);
+
+    await helper.wait.expectEvent(
+      fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod,
+      Event.Democracy.Proposed,
+    );
+
+    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+    const referendumIndex = startedEvent.referendumIndex;
+
+    const ayeBalance = 10_000n;
+
+    await helper.democracy.vote(regularUser, referendumIndex, {
+      Standard: {
+        vote: {
+          aye: true,
+          conviction: 1,
+        },
+        balance: ayeBalance,
+      },
+    });
+
+    const referendumInfo = await helper.democracy.referendumInfo(referendumIndex);
+    const tally = referendumInfo.ongoing.tally;
+
+    expect(BigInt(tally.ayes)).to.be.equal(ayeBalance);
+
+    await clearFellowship(sudoer);
+  });
+
+  itSub('[Negative] Regular user cannot submit a regular proposal', async ({helper}) => {
+    const submitResult = helper.democracy.propose(regularUser, dummyProposalCall(helper), 0n);
+    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Regular user cannot externally propose SuperMajorityAgainst', async ({helper}) => {
+    const submitResult = helper.democracy.externalProposeDefault(regularUser, dummyProposalCall(helper));
+    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Regular user cannot externally propose SimpleMajority', async ({helper}) => {
+    const submitResult = helper.democracy.externalProposeMajority(regularUser, dummyProposalCall(helper));
+    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Regular user cannot externally propose SuperMajorityApprove', async ({helper}) => {
+    const submitResult = helper.democracy.externalPropose(regularUser, dummyProposalCall(helper));
+    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
+  });
+});
addedjs-packages/tests/governance/fellowship.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/governance/fellowship.test.ts
@@ -0,0 +1,335 @@
+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 {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';
+
+describeGov('Governance: Fellowship tests', () => {
+  let members: IKeyringPair[][];
+
+  let rank1Proposer: IKeyringPair;
+
+  let sudoer: any;
+  let donor: any;
+  let counselors: ICounselors;
+  let techcomms: ITechComms;
+
+  const submissionDeposit = 1000n;
+
+  async function testBadFellowshipProposal(
+    helper: DevUniqueHelper,
+    proposalCall: any,
+  ) {
+    const badProposal = {
+      Inline: proposalCall.method.toHex(),
+    };
+    const submitResult = await helper.fellowship.referenda.submit(
+      rank1Proposer,
+      fellowshipPropositionOrigin,
+      badProposal,
+      defaultEnactmentMoment,
+    );
+
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+    await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, referendumIndex);
+    await helper.fellowship.referenda.placeDecisionDeposit(donor, referendumIndex);
+
+    const enactmentId = await helper.fellowship.referenda.enactmentEventId(referendumIndex);
+    const dispatchedEvent = await helper.wait.expectEvent(
+      fellowshipPreparePeriod + fellowshipConfirmPeriod + defaultEnactmentMoment.After,
+      Event.Scheduler.Dispatched,
+      (event: any) => event.id == enactmentId,
+    );
+
+    expect(dispatchedEvent.result.isErr, 'Bad Fellowship must fail')
+      .to.be.true;
+
+    expect(dispatchedEvent.result.asErr.isBadOrigin, 'Bad Fellowship must fail with BadOrigin')
+      .to.be.true;
+  }
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Fellowship, Pallets.TechnicalCommittee, Pallets.Council]);
+
+      sudoer = await privateKey('//Alice');
+      donor = await privateKey({url: import.meta.url});
+    });
+
+    counselors = await initCouncil(donor, sudoer);
+    techcomms = await initTechComm(donor, sudoer);
+    members = await initFellowship(donor, sudoer);
+
+    rank1Proposer = members[1][0];
+  });
+
+  after(async () => {
+    await clearFellowship(sudoer);
+    await clearTechComm(sudoer);
+    await clearCouncil(sudoer);
+    await hardResetFellowshipReferenda(sudoer);
+    await hardResetDemocracy(sudoer);
+    await hardResetGovScheduler(sudoer);
+  });
+
+  itSub('FellowshipProposition can submit regular Democracy proposals', async ({helper}) => {
+    const democracyProposalCall = dummyProposalCall(helper);
+    const fellowshipProposal = {
+      Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(),
+    };
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      rank1Proposer,
+      fellowshipPropositionOrigin,
+      fellowshipProposal,
+      defaultEnactmentMoment,
+    );
+
+    const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+    await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, fellowshipReferendumIndex);
+    await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex);
+
+    const democracyProposed = await helper.wait.expectEvent(
+      fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod,
+      Event.Democracy.Proposed,
+    );
+
+    const democracyEnqueuedProposal = await helper.democracy.expectPublicProposal(democracyProposed.proposalIndex);
+    expect(democracyEnqueuedProposal.inline, 'Fellowship proposal expected to be in the Democracy')
+      .to.be.equal(democracyProposalCall.method.toHex());
+
+    await helper.wait.newBlocks(democracyVotingPeriod);
+  });
+
+  itSub('Fellowship (rank-1 or greater) member can submit Fellowship proposals on the Democracy track', async ({helper}) => {
+    for(let rank = 1; rank < fellowshipRankLimit; rank++) {
+      const rankMembers = members[rank];
+
+      for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) {
+        const member = rankMembers[memberIdx];
+        const newDummyProposal = dummyProposal(helper);
+
+        const submitResult = await helper.fellowship.referenda.submit(
+          member,
+          fellowshipPropositionOrigin,
+          newDummyProposal,
+          defaultEnactmentMoment,
+        );
+
+        const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+        const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex);
+        expect(referendumInfo.ongoing.track, `${memberIdx}-th member of rank #${rank}: proposal #${referendumIndex} is on invalid track`)
+          .to.be.equal(democracyTrackId);
+      }
+    }
+  });
+
+  itSub(`Fellowship (rank-${democracyTrackMinRank} or greater) members can vote on the Democracy track`, async ({helper}) => {
+    const proposal = dummyProposal(helper);
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      rank1Proposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+
+    let expectedAyes = 0;
+    for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) {
+      const rankMembers = members[rank];
+
+      for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) {
+        const member = rankMembers[memberIdx];
+        await helper.fellowship.collective.vote(member, referendumIndex, true);
+        expectedAyes += 1;
+
+        const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex);
+        expect(referendumInfo.ongoing.tally.bareAyes, `Vote from ${memberIdx}-th member of rank #${rank} isn't accounted`)
+          .to.be.equal(expectedAyes);
+      }
+    }
+  });
+
+  itSub('Fellowship rank vote strength is correct', async ({helper}) => {
+    const excessRankWeightTable = [
+      1,
+      3,
+      6,
+      10,
+      15,
+      21,
+    ];
+
+    const proposal = dummyProposal(helper);
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      rank1Proposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+
+    for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) {
+      const rankMembers = members[rank];
+
+      for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) {
+        const member = rankMembers[memberIdx];
+
+        const referendumInfoBefore = await helper.fellowship.referenda.referendumInfo(referendumIndex);
+        const ayesBefore = referendumInfoBefore.ongoing.tally.ayes;
+
+        await helper.fellowship.collective.vote(member, referendumIndex, true);
+
+        const referendumInfoAfter = await helper.fellowship.referenda.referendumInfo(referendumIndex);
+        const ayesAfter = referendumInfoAfter.ongoing.tally.ayes;
+
+        const expectedVoteWeight = excessRankWeightTable[rank - democracyTrackMinRank];
+        const voteWeight = ayesAfter - ayesBefore;
+
+        expect(voteWeight, `Vote weight of ${memberIdx}-th member of rank #${rank} is invalid`)
+          .to.be.equal(expectedVoteWeight);
+      }
+    }
+  });
+
+  itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityAgainst', async ({helper}) => {
+    await testBadFellowshipProposal(helper, helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper)));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot externally propose SimpleMajority', async ({helper}) => {
+    await testBadFellowshipProposal(helper, helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityApprove', async ({helper}) => {
+    await testBadFellowshipProposal(helper, helper.democracy.externalProposeCall(dummyProposalCall(helper)));
+  });
+
+  itSub('[Negative] Fellowship (rank-0) member cannot submit Fellowship proposals on the Democracy track', async ({helper}) => {
+    const rank0Proposer = members[0][0];
+
+    const proposal = dummyProposal(helper);
+
+    const submitResult = helper.fellowship.referenda.submit(
+      rank0Proposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+
+    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
+  });
+
+  itSub('[Negative] Fellowship (rank-1 or greater) member cannot submit if no deposit can be provided', async ({helper}) => {
+    const poorMember = rank1Proposer;
+
+    const balanceBefore = await helper.balance.getSubstrate(poorMember.address);
+    await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, submissionDeposit - 1n);
+
+    const proposal = dummyProposal(helper);
+
+    const submitResult = helper.fellowship.referenda.submit(
+      poorMember,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+
+    await expect(submitResult).to.be.rejectedWith(/account balance too low/);
+
+    await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, balanceBefore);
+  });
+
+  itSub(`[Negative] Fellowship (rank-${democracyTrackMinRank - 1} or less) members cannot vote on the Democracy track`, async ({helper}) => {
+    const proposal = dummyProposal(helper);
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      rank1Proposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+
+    for(let rank = 0; rank < democracyTrackMinRank; rank++) {
+      for(const member of members[rank]) {
+        const voteResult = helper.fellowship.collective.vote(member, referendumIndex, true);
+        await expect(voteResult).to.be.rejectedWith(/RankTooLow/);
+      }
+    }
+  });
+
+  itSub('[Negative] FellowshipProposition cannot add/remove a Council member', async ({helper}) => {
+    const [councilNonMember] = await helper.arrange.createAccounts([10n], donor);
+
+    await testBadFellowshipProposal(helper, helper.council.membership.addMemberCall(councilNonMember.address));
+    await testBadFellowshipProposal(helper, helper.council.membership.removeMemberCall(counselors.ildar.address));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot set/clear Council prime member', async ({helper}) => {
+    await testBadFellowshipProposal(helper, helper.council.membership.setPrimeCall(counselors.ildar.address));
+    await testBadFellowshipProposal(helper, helper.council.membership.clearPrimeCall());
+  });
+
+  itSub('[Negative] FellowshipProposition cannot add/remove a TechComm member', async ({helper}) => {
+    const [techCommNonMember] = await helper.arrange.createAccounts([10n], donor);
+
+    await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.addMemberCall(techCommNonMember.address));
+    await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.removeMemberCall(techcomms.constantine.address));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot add/remove a Fellowship member', async ({helper}) => {
+    const [fellowshipNonMember] = await helper.arrange.createAccounts([10n], donor);
+
+    await testBadFellowshipProposal(helper, helper.fellowship.collective.addMemberCall(fellowshipNonMember.address));
+    await testBadFellowshipProposal(helper, helper.fellowship.collective.removeMemberCall(rank1Proposer.address, 1));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot promote/demote a Fellowship member', async ({helper}) => {
+    await testBadFellowshipProposal(helper, helper.fellowship.collective.promoteCall(rank1Proposer.address));
+    await testBadFellowshipProposal(helper, helper.fellowship.collective.demoteCall(rank1Proposer.address));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot fast-track Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await testBadFellowshipProposal(helper, helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot cancel Democracy proposals', async ({helper}) => {
+    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
+    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+
+    await testBadFellowshipProposal(helper, helper.democracy.cancelProposalCall(proposalIndex));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot cancel ongoing Democracy referendums', async ({helper}) => {
+    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
+    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+    const referendumIndex = startedEvent.referendumIndex;
+
+    await testBadFellowshipProposal(helper, helper.democracy.emergencyCancelCall(referendumIndex));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot blacklist Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await testBadFellowshipProposal(helper, helper.democracy.blacklistCall(preimageHash, null));
+  });
+
+  itSub('[Negative] FellowshipProposition cannot veto external proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await testBadFellowshipProposal(helper, helper.democracy.vetoExternalCall(preimageHash));
+  });
+});
addedjs-packages/tests/governance/init.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/governance/init.test.ts
@@ -0,0 +1,193 @@
+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 {democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, clearCouncil, clearTechComm, clearFellowship} from './util.js';
+import type {ICounselors, ITechComms} from './util.js';
+
+describeGov('Governance: Initialization', () => {
+  let donor: IKeyringPair;
+  let sudoer: IKeyringPair;
+  let counselors: ICounselors;
+  let techcomms: ITechComms;
+  let coreDevs: any;
+
+  const expectedAlexFellowRank = 7;
+  const expectedFellowRank = 6;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Council, Pallets.TechnicalCommittee]);
+
+      const councilMembers = await helper.council.membership.getMembers();
+      const techcommMembers = await helper.technicalCommittee.membership.getMembers();
+      expect(councilMembers.length == 0, 'The Council must be empty before the Gov Init');
+      expect(techcommMembers.length == 0, 'The Technical Commettee must be empty before the Gov Init');
+
+      donor = await privateKey({url: import.meta.url});
+      sudoer = await privateKey('//Alice');
+
+      const counselorsNum = 5;
+      const techCommsNum = 3;
+      const coreDevsNum = 2;
+      const [
+        alex,
+        ildar,
+        charu,
+        filip,
+        irina,
+
+        greg,
+        andy,
+        constantine,
+
+        yaroslav,
+        daniel,
+      ] = await helper.arrange.createAccounts(new Array(counselorsNum + techCommsNum + coreDevsNum).fill(10_000n), donor);
+
+      counselors = {
+        alex,
+        ildar,
+        charu,
+        filip,
+        irina,
+      };
+
+      techcomms = {
+        greg,
+        andy,
+        constantine,
+      };
+
+      coreDevs = {
+        yaroslav: yaroslav,
+        daniel: daniel,
+      };
+    });
+  });
+
+  itSub('Initialize Governance', async ({helper}) => {
+    const promoteFellow = (fellow: string, promotionsNum: number) => new Array(promotionsNum).fill(helper.fellowship.collective.promoteCall(fellow));
+
+    const expectFellowRank = async (fellow: string, expectedRank: number) => {
+      expect(await helper.fellowship.collective.getMemberRank(fellow)).to.be.equal(expectedRank);
+    };
+
+    console.log('\t- Setup the Prime of the Council via sudo');
+    await helper.getSudo().utility.batchAll(sudoer, [
+      helper.council.membership.addMemberCall(counselors.alex.address),
+      helper.council.membership.setPrimeCall(counselors.alex.address),
+
+      helper.fellowship.collective.addMemberCall(counselors.alex.address),
+      ...promoteFellow(counselors.alex.address, expectedAlexFellowRank),
+    ]);
+
+    let councilMembers = await helper.council.membership.getMembers();
+    const councilPrime = await helper.council.collective.getPrimeMember();
+    const alexFellowRank = await helper.fellowship.collective.getMemberRank(counselors.alex.address);
+    expect(councilMembers).to.be.deep.equal([counselors.alex.address]);
+    expect(councilPrime).to.be.equal(counselors.alex.address);
+    expect(alexFellowRank).to.be.equal(expectedAlexFellowRank);
+
+    console.log('\t- The Council Prime initializes the Technical Commettee');
+    const councilProposalThreshold = 1;
+
+    await helper.council.collective.propose(
+      counselors.alex,
+      helper.utility.batchAllCall([
+        helper.technicalCommittee.membership.addMemberCall(techcomms.greg.address),
+        helper.technicalCommittee.membership.addMemberCall(techcomms.andy.address),
+        helper.technicalCommittee.membership.addMemberCall(techcomms.constantine.address),
+
+        helper.technicalCommittee.membership.setPrimeCall(techcomms.greg.address),
+      ]),
+      councilProposalThreshold,
+    );
+
+    const techCommMembers = await helper.technicalCommittee.membership.getMembers();
+    const techCommPrime = await helper.technicalCommittee.membership.getPrimeMember();
+    const expectedTechComms = [techcomms.greg.address, techcomms.andy.address, techcomms.constantine.address];
+    expect(techCommMembers.length).to.be.equal(expectedTechComms.length);
+    expect(techCommMembers).to.containSubset(expectedTechComms);
+    expect(techCommPrime).to.be.equal(techcomms.greg.address);
+
+    console.log('\t- The Council Prime initiates a referendum to add counselors');
+    const returnPreimageHash = true;
+    const preimageHash = await helper.preimage.notePreimageFromCall(counselors.alex, helper.utility.batchAllCall([
+      helper.council.membership.addMemberCall(counselors.ildar.address),
+      helper.council.membership.addMemberCall(counselors.charu.address),
+      helper.council.membership.addMemberCall(counselors.filip.address),
+      helper.council.membership.addMemberCall(counselors.irina.address),
+
+      helper.fellowship.collective.addMemberCall(counselors.charu.address),
+      helper.fellowship.collective.addMemberCall(counselors.ildar.address),
+      helper.fellowship.collective.addMemberCall(counselors.irina.address),
+      helper.fellowship.collective.addMemberCall(counselors.filip.address),
+      helper.fellowship.collective.addMemberCall(techcomms.greg.address),
+      helper.fellowship.collective.addMemberCall(techcomms.andy.address),
+      helper.fellowship.collective.addMemberCall(techcomms.constantine.address),
+      helper.fellowship.collective.addMemberCall(coreDevs.yaroslav.address),
+      helper.fellowship.collective.addMemberCall(coreDevs.daniel.address),
+
+      ...promoteFellow(counselors.charu.address, expectedFellowRank),
+      ...promoteFellow(counselors.ildar.address, expectedFellowRank),
+      ...promoteFellow(counselors.irina.address, expectedFellowRank),
+      ...promoteFellow(counselors.filip.address, expectedFellowRank),
+      ...promoteFellow(techcomms.greg.address, expectedFellowRank),
+      ...promoteFellow(techcomms.andy.address, expectedFellowRank),
+      ...promoteFellow(techcomms.constantine.address, expectedFellowRank),
+      ...promoteFellow(coreDevs.yaroslav.address, expectedFellowRank),
+      ...promoteFellow(coreDevs.daniel.address, expectedFellowRank),
+    ]), returnPreimageHash);
+
+    await helper.council.collective.propose(
+      counselors.alex,
+      helper.democracy.externalProposeDefaultWithPreimageCall(preimageHash),
+      councilProposalThreshold,
+    );
+
+    console.log('\t- The referendum is being decided');
+    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+
+    await helper.democracy.vote(counselors.filip, startedEvent.referendumIndex, {
+      Standard: {
+        vote: {
+          aye: true,
+          conviction: 1,
+        },
+        balance: 10_000n,
+      },
+    });
+
+    const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed);
+    expect(passedReferendumEvent.referendumIndex).to.be.equal(startedEvent.referendumIndex);
+
+    await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched);
+
+    councilMembers = await helper.council.membership.getMembers();
+    const expectedCounselors = [
+      counselors.alex.address,
+      counselors.ildar.address,
+      counselors.charu.address,
+      counselors.filip.address,
+      counselors.irina.address,
+    ];
+    expect(councilMembers.length).to.be.equal(expectedCounselors.length);
+    expect(councilMembers).to.containSubset(expectedCounselors);
+
+    await expectFellowRank(counselors.ildar.address, expectedFellowRank);
+    await expectFellowRank(counselors.charu.address, expectedFellowRank);
+    await expectFellowRank(counselors.filip.address, expectedFellowRank);
+    await expectFellowRank(counselors.irina.address, expectedFellowRank);
+    await expectFellowRank(techcomms.greg.address, expectedFellowRank);
+    await expectFellowRank(techcomms.andy.address, expectedFellowRank);
+    await expectFellowRank(techcomms.constantine.address, expectedFellowRank);
+    await expectFellowRank(coreDevs.yaroslav.address, expectedFellowRank);
+    await expectFellowRank(coreDevs.daniel.address, expectedFellowRank);
+  });
+
+  after(async function() {
+    await clearFellowship(sudoer);
+    await clearTechComm(sudoer);
+    await clearCouncil(sudoer);
+  });
+});
addedjs-packages/tests/governance/technicalCommittee.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/governance/technicalCommittee.test.ts
@@ -0,0 +1,383 @@
+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 {initCouncil, democracyLaunchPeriod, democracyFastTrackVotingPeriod, clearCouncil, clearTechComm, clearFellowship, defaultEnactmentMoment, dummyProposal, dummyProposalCall, fellowshipPropositionOrigin, initFellowship, initTechComm, hardResetFellowshipReferenda, hardResetDemocracy, hardResetGovScheduler} from './util.js';
+import type {ITechComms} from './util.js';
+
+describeGov('Governance: Technical Committee tests', () => {
+  let sudoer: IKeyringPair;
+  let techcomms: ITechComms;
+  let donor: IKeyringPair;
+  let preImageHash: string;
+
+
+  const allTechCommitteeThreshold = 3;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.TechnicalCommittee]);
+      sudoer = await privateKey('//Alice');
+      donor = await privateKey({url: import.meta.url});
+
+      techcomms = await initTechComm(donor, sudoer);
+
+      const proposalCall = await helper.constructApiCall('api.tx.balances.forceSetBalance', [donor.address, 20n * 10n ** 25n]);
+      preImageHash = await helper.preimage.notePreimageFromCall(sudoer, proposalCall, true);
+    });
+  });
+
+  after(async () => {
+    await usingPlaygrounds(async (helper) => {
+      await clearTechComm(sudoer);
+
+      await helper.preimage.unnotePreimage(sudoer, preImageHash);
+      await hardResetFellowshipReferenda(sudoer);
+      await hardResetDemocracy(sudoer);
+      await hardResetGovScheduler(sudoer);
+    });
+  });
+
+  function proposalFromAllCommittee(proposal: any) {
+    return usingPlaygrounds(async (helper) => {
+      expect((await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length).to.be.equal(allTechCommitteeThreshold);
+      const proposeResult = await helper.technicalCommittee.collective.propose(
+        techcomms.andy,
+        proposal,
+        allTechCommitteeThreshold,
+      );
+
+      const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);
+      const proposalIndex = commiteeProposedEvent.proposalIndex;
+      const proposalHash = commiteeProposedEvent.proposalHash;
+
+
+      await helper.technicalCommittee.collective.vote(techcomms.andy, proposalHash, proposalIndex, true);
+      await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);
+      await helper.technicalCommittee.collective.vote(techcomms.greg, proposalHash, proposalIndex, true);
+
+      const closeResult = await helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex);
+      Event.TechnicalCommittee.Closed.expect(closeResult);
+      Event.TechnicalCommittee.Approved.expect(closeResult);
+      const {result} = Event.TechnicalCommittee.Executed.expect(closeResult);
+      expect(result).to.eq('Ok');
+
+      return closeResult;
+    });
+  }
+
+  itSub('TechComm can fast-track Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await helper.wait.parachainBlockMultiplesOf(35n);
+
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    const fastTrackProposal = await proposalFromAllCommittee(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0));
+    Event.Democracy.Started.expect(fastTrackProposal);
+  });
+
+  itSub('TechComm can cancel Democracy proposals', async ({helper}) => {
+    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
+    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+
+    const cancelProposal = await proposalFromAllCommittee(helper.democracy.cancelProposalCall(proposalIndex));
+    Event.Democracy.ProposalCanceled.expect(cancelProposal);
+  });
+
+  itSub('TechComm can cancel ongoing Democracy referendums', async ({helper}) => {
+    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
+    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+    const referendumIndex = startedEvent.referendumIndex;
+
+    const emergencyCancelProposal = await proposalFromAllCommittee(helper.democracy.emergencyCancelCall(referendumIndex));
+    Event.Democracy.Cancelled.expect(emergencyCancelProposal);
+  });
+
+  itSub('TechComm member can veto Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    const vetoExternalCall = await helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.democracy.vetoExternalCall(preimageHash),
+    );
+    Event.Democracy.Vetoed.expect(vetoExternalCall);
+  });
+
+  itSub('TechComm can cancel Fellowship referendums', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+    const fellowshipProposer = fellowship[5][0];
+    const proposal = dummyProposal(helper);
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      fellowshipProposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+    const cancelProposal = await proposalFromAllCommittee(helper.fellowship.referenda.cancelCall(referendumIndex));
+    Event.FellowshipReferenda.Cancelled.expect(cancelProposal);
+  });
+
+  itSub.skip('TechComm member can add a Fellowship member', async ({helper}) => {
+    const newFellowshipMember = helper.arrange.createEmptyAccount();
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.fellowship.collective.addMemberCall(newFellowshipMember.address),
+    )).to.be.fulfilled;
+    const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON();
+    expect(fellowshipMembers).to.contains(newFellowshipMember.address);
+    await clearFellowship(sudoer);
+  });
+
+  itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => {
+    const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
+
+    await expect(proposalFromAllCommittee(councilProposal)).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm cannot externally propose SuperMajorityAgainst', async ({helper}) => {
+    const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));
+
+    await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm cannot externally propose SimpleMajority', async ({helper}) => {
+    const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
+
+    await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm cannot externally propose SuperMajorityApprove', async ({helper}) => {
+    const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
+
+    await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot submit regular democracy proposal', async ({helper}) => {
+    const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      memberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot externally propose SuperMajorityAgainst', async ({helper}) => {
+    const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      memberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot externally propose SimpleMajority', async ({helper}) => {
+    const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      memberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot externally propose SuperMajorityApprove', async ({helper}) => {
+    const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      memberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+
+  itSub.skip('[Negative] TechComm cannot promote/demote Fellowship member', async () => {
+
+  });
+
+  itSub.skip('[Negative] TechComm member cannot promote/demote Fellowship member', async () => {
+
+  });
+
+  itSub('[Negative] TechComm cannot add/remove a Council member', async ({helper}) => {
+    const newCouncilMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
+    const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);
+
+    await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');
+    await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot add/remove a Council member', async ({helper}) => {
+    const newCouncilMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
+    const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      addMemberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      removeMemberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm cannot set/clear Council prime member', async ({helper}) => {
+    const counselors = await initCouncil(donor, sudoer);
+    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
+    const proposalForClear = await helper.council.membership.clearPrimeCall();
+
+    await expect(proposalFromAllCommittee(proposalForSet)).to.be.rejectedWith('BadOrigin');
+    await expect(proposalFromAllCommittee(proposalForClear)).to.be.rejectedWith('BadOrigin');
+    await clearCouncil(sudoer);
+  });
+
+  itSub('[Negative] TechComm member cannot set/clear Council prime member', async ({helper}) => {
+    const counselors = await initCouncil(donor, sudoer);
+    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
+    const proposalForClear = await helper.council.membership.clearPrimeCall();
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      proposalForSet,
+    )).to.be.rejectedWith('BadOrigin');
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      proposalForClear,
+    )).to.be.rejectedWith('BadOrigin');
+    await clearCouncil(sudoer);
+  });
+
+  itSub('[Negative] TechComm cannot add/remove a TechComm member', async ({helper}) => {
+    const newCommMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);
+    const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);
+
+    await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');
+    await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot add/remove a TechComm member', async ({helper}) => {
+    const newCommMember = helper.arrange.createEmptyAccount();
+    const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);
+    const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      addMemberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      removeMemberProposal,
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm cannot remove a Fellowship member', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+
+    await expect(proposalFromAllCommittee(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin');
+    await clearFellowship(sudoer);
+  });
+
+  itSub('[Negative] TechComm member cannot remove a Fellowship member', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5),
+    )).to.be.rejectedWith('BadOrigin');
+    await clearFellowship(sudoer);
+  });
+
+  itSub('[Negative] TechComm member cannot fast-track Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot cancel Democracy proposals', async ({helper}) => {
+    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
+    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.democracy.cancelProposalCall(proposalIndex),
+    ))
+      .to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot cancel ongoing Democracy referendums', async ({helper}) => {
+    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
+    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
+    const referendumIndex = startedEvent.referendumIndex;
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.democracy.emergencyCancelCall(referendumIndex),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm cannot blacklist Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await expect(proposalFromAllCommittee(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm member cannot blacklist Democracy proposals', async ({helper}) => {
+    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
+    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.democracy.blacklistCall(preimageHash),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub.skip('[Negative] TechComm member cannot veto external Democracy proposals until the cool-off period pass', async () => {
+
+  });
+
+  itSub('[Negative] TechComm member cannot cancel Fellowship referendums', async ({helper}) => {
+    const fellowship = await initFellowship(donor, sudoer);
+    const fellowshipProposer = fellowship[5][0];
+    const proposal = dummyProposal(helper);
+
+    const submitResult = await helper.fellowship.referenda.submit(
+      fellowshipProposer,
+      fellowshipPropositionOrigin,
+      proposal,
+      defaultEnactmentMoment,
+    );
+
+    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
+
+    await expect(helper.technicalCommittee.collective.execute(
+      techcomms.andy,
+      helper.fellowship.referenda.cancelCall(referendumIndex),
+    )).to.be.rejectedWith('BadOrigin');
+  });
+
+  itSub('[Negative] TechComm referendum cannot be closed until the voting threshold is met', async ({helper}) => {
+    const committeeSize = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length as any as number;
+    expect(committeeSize).is.greaterThan(1);
+    const proposeResult = await helper.technicalCommittee.collective.propose(
+      techcomms.andy,
+      dummyProposalCall(helper),
+      committeeSize,
+    );
+
+    const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);
+    const proposalIndex = committeeProposedEvent.proposalIndex;
+    const proposalHash = committeeProposedEvent.proposalHash;
+
+    await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);
+
+    await expect(helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');
+  });
+});
addedjs-packages/tests/governance/util.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/governance/util.ts
@@ -0,0 +1,224 @@
+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';
+
+export const democracyLaunchPeriod = 35;
+export const democracyVotingPeriod = 35;
+export const councilMotionDuration = 35;
+export const democracyEnactmentPeriod = 40;
+export const democracyFastTrackVotingPeriod = 5;
+
+export const fellowshipRankLimit = 7;
+export const fellowshipPropositionOrigin = 'FellowshipProposition';
+export const fellowshipPreparePeriod = 3;
+export const fellowshipConfirmPeriod = 3;
+export const fellowshipMinEnactPeriod = 1;
+
+export const defaultEnactmentMoment = {After: 0};
+
+export const democracyTrackId = 10;
+export const democracyTrackMinRank = 3;
+const twox128 = (data: any) => xxhashAsHex(data, 128);
+export interface ICounselors {
+  alex: IKeyringPair;
+  ildar: IKeyringPair;
+  charu: IKeyringPair;
+  filip: IKeyringPair;
+  irina: IKeyringPair;
+}
+export interface ITechComms {
+    greg: IKeyringPair;
+    andy: IKeyringPair;
+    constantine: IKeyringPair;
+}
+
+export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) {
+  let counselors: IKeyringPair[] = [];
+
+  await usingPlaygrounds(async (helper) => {
+    const [alex, ildar, charu, filip, irina] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n, 10_000n, 10_000n], donor);
+    const sudo = helper.getSudo();
+    {
+      const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as [];
+      if(members.length != 0) {
+        await clearCouncil(superuser);
+      }
+    }
+    const expectedMembers = [alex, ildar, charu, filip, irina];
+    for(const member of expectedMembers) {
+      await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.addMember', [member.address]);
+    }
+    await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.setPrime', [alex.address]);
+    {
+      const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
+      expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address));
+      expect(members.length).to.be.equal(expectedMembers.length);
+    }
+
+    counselors = [alex, ildar, charu, filip, irina];
+  });
+  return {
+    alex: counselors[0],
+    ildar: counselors[1],
+    charu: counselors[2],
+    filip: counselors[3],
+    irina: counselors[4],
+  };
+}
+
+export async function clearCouncil(superuser: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    let members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
+    if(members.length) {
+      const sudo = helper.getSudo();
+      for(const address of members) {
+        await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.removeMember', [address]);
+      }
+      members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
+    }
+    expect(members).to.be.deep.equal([]);
+  });
+}
+
+
+export async function initTechComm(donor: IKeyringPair, superuser: IKeyringPair) {
+  let techcomms: IKeyringPair[] = [];
+
+  await usingPlaygrounds(async (helper) => {
+    const [greg, andy, constantine] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor);
+    const sudo = helper.getSudo();
+    {
+      const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON() as [];
+      if(members.length != 0) {
+        await clearTechComm(superuser);
+      }
+    }
+    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [greg.address]);
+    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [andy.address]);
+    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [constantine.address]);
+    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.setPrime', [greg.address]);
+    {
+      const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
+      expect(members).to.containSubset([greg.address, andy.address, constantine.address]);
+      expect(members.length).to.be.equal(3);
+    }
+
+    techcomms = [greg, andy, constantine];
+  });
+
+  return {
+    greg: techcomms[0],
+    andy: techcomms[1],
+    constantine: techcomms[2],
+  };
+}
+
+export async function clearTechComm(superuser: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    let members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
+    if(members.length) {
+      const sudo = helper.getSudo();
+      for(const address of members) {
+        await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.removeMember', [address]);
+      }
+      members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
+    }
+    expect(members).to.be.deep.equal([]);
+  });
+}
+
+export async function initFellowship(donor: IKeyringPair, sudoer: IKeyringPair) {
+  const numMembersInRank = 3;
+  const memberBalance = 5000n;
+  const members: IKeyringPair[][] = [];
+
+  await usingPlaygrounds(async (helper) => {
+    const currentFellows = await helper.getApi().query.fellowshipCollective.members.keys();
+
+    if(currentFellows.length != 0) {
+      await clearFellowship(sudoer);
+    }
+    for(let i = 0; i < fellowshipRankLimit; i++) {
+      const rankMembers = await helper.arrange.createAccounts(
+        Array(numMembersInRank).fill(memberBalance),
+        donor,
+      );
+
+      for(const member of rankMembers) {
+        await helper.getSudo().fellowship.collective.addMember(sudoer, member.address);
+
+        for(let rank = 0; rank < i; rank++) {
+          await helper.getSudo().fellowship.collective.promote(sudoer, member.address);
+        }
+      }
+
+      members.push(rankMembers);
+    }
+  });
+
+  return members;
+}
+
+export async function clearFellowship(sudoer: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    const fellowship = (await helper.getApi().query.fellowshipCollective.members.keys())
+      .map((key) => key.args[0].toString());
+    for(const  member of fellowship) {
+      await helper.getSudo().fellowship.collective.removeMember(sudoer, member, fellowshipRankLimit);
+    }
+  });
+}
+
+export async function clearFellowshipReferenda(sudoer: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    const proposalsCount = (await helper.getApi().query.fellowshipReferenda.referendumCount()) as u32;
+    for(let i = 0; i < proposalsCount.toNumber(); i++) {
+      await helper.getSudo().fellowship.referenda.cancel(sudoer, i);
+    }
+  });
+}
+
+export async function hardResetFellowshipReferenda(sudoer: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    const api = helper.getApi();
+    const prefix = twox128('FellowshipReferenda');
+    await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));
+  });
+}
+
+export async function hardResetDemocracy(sudoer: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    const api = helper.getApi();
+    const prefix = twox128('Democracy');
+    await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));
+  });
+}
+
+export async function hardResetGovScheduler(sudoer: IKeyringPair) {
+  await usingPlaygrounds(async (helper) => {
+    const api = helper.getApi();
+    const prefix = twox128('GovScheduler');
+    await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 500)));
+  });
+}
+
+export async function voteUnanimouslyInFellowship(helper: DevUniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {
+  for(let rank = minRank; rank < fellowshipRankLimit; rank++) {
+    for(const member of fellows[rank]) {
+      await helper.fellowship.collective.vote(member, referendumIndex, true);
+    }
+  }
+}
+
+export function dummyProposalCall(helper: UniqueHelper) {
+  return helper.constructApiCall('api.tx.system.remark', ['dummy proposal' + (new Date()).getTime()]);
+}
+
+export function dummyProposal(helper: UniqueHelper) {
+  return {
+    Inline: dummyProposalCall(helper).method.toHex(),
+  };
+}
addedjs-packages/tests/inflation.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/inflation.seqtest.ts
@@ -0,0 +1,58 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from './util/index.js';
+
+// todo:playgrounds requires sudo, look into on the later stage
+describe('integration test: Inflation', () => {
+  let superuser: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (_, privateKey) => {
+      superuser = await privateKey('//Alice');
+    });
+  });
+
+  itSub('First year inflation is 10%', async ({helper}) => {
+    // Make sure non-sudo can't start inflation
+    const [bob] = await helper.arrange.createAccounts([10n], superuser);
+
+    await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
+
+    // Make sure superuser can't start inflation without explicit sudo
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
+
+    // Start inflation on relay block 1 (Alice is sudo)
+    const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]);
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected;
+
+    const blockInterval = (helper.getApi().consts.inflation.inflationBlockInterval as any).toBigInt();
+    const totalIssuanceStart = ((await helper.callRpc('api.query.inflation.startingYearTotalIssuance', [])) as any).toBigInt();
+    const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt();
+
+    const YEAR = 5259600n;  // 6-second block. Blocks in one year
+    // const YEAR = 2629800n; // 12-second block. Blocks in one year
+
+    const totalExpectedInflation = totalIssuanceStart / 10n;
+    const totalActualInflation = blockInflation * YEAR / blockInterval;
+
+    const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation
+    const expectedInflation = totalExpectedInflation / totalActualInflation - 1n;
+
+    expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance);
+  });
+});
addedjs-packages/tests/limits.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/limits.test.ts
@@ -0,0 +1,474 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js';
+
+describe('Number of tokens per address (NFT)', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});
+
+    for(let i = 0; i < 10; i++){
+      await expect(collection.mintToken(alice)).to.be.not.rejected;
+    }
+    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
+    for(let i = 1; i < 11; i++) {
+      await expect(collection.burnToken(alice, i)).to.be.not.rejected;
+    }
+    await collection.burn(alice);
+  });
+
+  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});
+
+    await collection.mintToken(alice);
+    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
+
+    await collection.burnToken(alice, 1);
+    await expect(collection.burn(alice)).to.be.not.rejected;
+  });
+});
+
+describe('Number of tokens per address (ReFungible)', () => {
+  let alice: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});
+
+    for(let i = 0; i < 10; i++){
+      await expect(collection.mintToken(alice, 10n)).to.be.not.rejected;
+    }
+    await expect(collection.mintToken(alice, 10n)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
+    for(let i = 1; i < 11; i++) {
+      await expect(collection.burnToken(alice, i, 10n)).to.be.not.rejected;
+    }
+    await collection.burn(alice);
+  });
+
+  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});
+
+    await collection.mintToken(alice);
+    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
+
+    await collection.burnToken(alice, 1);
+    await expect(collection.burn(alice)).to.be.not.rejected;
+  });
+});
+
+// todo:playgrounds skipped ~ postponed
+describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => {
+  /*let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async (api, privateKeyWrapper) => {
+      alice = privateKeyWrapper('//Alice');
+      bob = privateKeyWrapper('//Bob');
+      charlie = privateKeyWrapper('//Charlie');
+    });
+  });
+
+  itSub.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+    await transferExpectSuccess(collectionId, tokenId, alice, bob);
+    const aliceBalanceBefore = await getFreeBalance(alice);
+
+    // check setting SponsorTimeout = 5, fail
+    await waitNewBlocks(5);
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie);
+    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+    // check setting SponsorTimeout = 7, success
+    await waitNewBlocks(2); // 5 + 2
+    await transferExpectSuccess(collectionId, tokenId, charlie, bob);
+    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
+    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+
+  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {
+
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+    await transferExpectSuccess(collectionId, tokenId, alice, bob);
+    const aliceBalanceBefore = await getFreeBalance(alice);
+
+    // check setting SponsorTimeout = 1, fail
+    await waitNewBlocks(1);
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie);
+    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+    // check setting SponsorTimeout = 5, success
+    await waitNewBlocks(4);
+    await transferExpectSuccess(collectionId, tokenId, charlie, bob);
+    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
+    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+});
+
+describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async (api, privateKeyWrapper) => {
+      alice = privateKeyWrapper('//Alice');
+      bob = privateKeyWrapper('//Bob');
+      charlie = privateKeyWrapper('//Charlie');
+    });
+  });
+
+  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');
+    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
+    const aliceBalanceBefore = await getFreeBalance(alice);
+
+    // check setting SponsorTimeout = 5, fail
+    await waitNewBlocks(5);
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
+    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+    // check setting SponsorTimeout = 7, success
+    await waitNewBlocks(2); // 5 + 2
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
+    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
+    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+
+  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {
+
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');
+    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
+    const aliceBalanceBefore = await getFreeBalance(alice);
+
+    // check setting SponsorTimeout = 1, fail
+    await waitNewBlocks(1);
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
+    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+    // check setting SponsorTimeout = 5, success
+    await waitNewBlocks(4);
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
+    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
+    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+});
+
+describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingApi(async (api, privateKeyWrapper) => {
+      alice = privateKeyWrapper('//Alice');
+      bob = privateKeyWrapper('//Bob');
+      charlie = privateKeyWrapper('//Charlie');
+    });
+  });
+
+  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible');
+    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+    await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible');
+    const aliceBalanceBefore = await getFreeBalance(alice);
+
+    // check setting SponsorTimeout = 5, fail
+    await waitNewBlocks(5);
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');
+    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+    // check setting SponsorTimeout = 7, success
+    await waitNewBlocks(2); // 5 + 2
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');
+    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
+    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+    await destroyCollectionExpectSuccess(collectionId);
+  });
+
+  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {
+
+    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});
+    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
+    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
+    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
+    await transferExpectSuccess(collectionId, tokenId, alice, bob);
+    const aliceBalanceBefore = await getFreeBalance(alice);
+
+    // check setting SponsorTimeout = 1, fail
+    await waitNewBlocks(1);
+    await transferExpectSuccess(collectionId, tokenId, bob, charlie);
+    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
+
+    // check setting SponsorTimeout = 5, success
+    await waitNewBlocks(4);
+    await transferExpectSuccess(collectionId, tokenId, charlie, bob);
+    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
+    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
+    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
+    await destroyCollectionExpectSuccess(collectionId);
+  });*/
+});
+
+describe('Collection zero limits (NFT)', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    });
+  });
+
+  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});
+
+    for(let i = 0; i < 10; i++){
+      await collection.mintToken(alice);
+    }
+    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
+  });
+
+  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    await collection.setLimits(alice, {sponsorTransferTimeout: 0});
+    const token = await collection.mintToken(alice);
+
+    await collection.setSponsor(alice, alice.address);
+    await collection.confirmSponsorship(alice);
+
+    await token.transfer(alice, {Substrate: bob.address});
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+
+    // check setting SponsorTimeout = 0, success with next block
+    await helper.wait.newBlocks(1);
+    await token.transfer(bob, {Substrate: charlie.address});
+    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);
+    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;
+  });
+});
+
+describe('Collection zero limits (Fungible)', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {});
+    await collection.setLimits(alice, {sponsorTransferTimeout: 0});
+    await collection.mint(alice, 3n);
+
+    await collection.setSponsor(alice, alice.address);
+    await collection.confirmSponsorship(alice);
+
+    await collection.transfer(alice, {Substrate: bob.address}, 2n);
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+
+    // check setting SponsorTimeout = 0, success with next block
+    await helper.wait.newBlocks(1);
+    await collection.transfer(bob, {Substrate: charlie.address});
+    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);
+    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;
+  });
+});
+
+describe('Collection zero limits (ReFungible)', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+    });
+  });
+
+  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});
+    for(let i = 0; i < 10; i++){
+      await collection.mintToken(alice);
+    }
+    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
+  });
+
+  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    await collection.setLimits(alice, {sponsorTransferTimeout: 0});
+    const token = await collection.mintToken(alice, 3n);
+
+    await collection.setSponsor(alice, alice.address);
+    await collection.confirmSponsorship(alice);
+
+    await token.transfer(alice, {Substrate: bob.address}, 2n);
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+
+    // check setting SponsorTimeout = 0, success with next block
+    await helper.wait.newBlocks(1);
+    await token.transfer(bob, {Substrate: charlie.address});
+    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);
+    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;
+  });
+});
+
+describe('Effective collection limits (NFT)', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itSub('Effective collection limits', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {});
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+
+    {
+      // Check that limits are undefined
+      const collectionInfo = await collection.getData();
+      const limits = collectionInfo?.raw.limits;
+      expect(limits).to.be.any;
+
+      expect(limits.accountTokenOwnershipLimit).to.be.null;
+      expect(limits.sponsoredDataSize).to.be.null;
+      expect(limits.sponsoredDataRateLimit).to.be.null;
+      expect(limits.tokenLimit).to.be.null;
+      expect(limits.sponsorTransferTimeout).to.be.null;
+      expect(limits.sponsorApproveTimeout).to.be.null;
+      expect(limits.ownerCanTransfer).to.be.true;
+      expect(limits.ownerCanDestroy).to.be.null;
+      expect(limits.transfersEnabled).to.be.null;
+    }
+
+    { // Check that limits is undefined for non-existent collection
+      const limits = await helper.collection.getEffectiveLimits(999999);
+      expect(limits).to.be.null;
+    }
+
+    { // Check that default values defined for collection limits
+      const limits = await collection.getEffectiveLimits();
+
+      expect(limits.accountTokenOwnershipLimit).to.be.eq(100000);
+      expect(limits.sponsoredDataSize).to.be.eq(2048);
+      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});
+      expect(limits.tokenLimit).to.be.eq(4294967295);
+      expect(limits.sponsorTransferTimeout).to.be.eq(5);
+      expect(limits.sponsorApproveTimeout).to.be.eq(5);
+      expect(limits.ownerCanTransfer).to.be.true;
+      expect(limits.ownerCanDestroy).to.be.true;
+      expect(limits.transfersEnabled).to.be.true;
+    }
+
+    {
+      // Check the values for collection limits
+      await collection.setLimits(alice, {
+        accountTokenOwnershipLimit: 99_999,
+        sponsoredDataSize: 1024,
+        tokenLimit: 123,
+        transfersEnabled: false,
+      });
+
+      const limits = await collection.getEffectiveLimits();
+
+      expect(limits.accountTokenOwnershipLimit).to.be.eq(99999);
+      expect(limits.sponsoredDataSize).to.be.eq(1024);
+      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});
+      expect(limits.tokenLimit).to.be.eq(123);
+      expect(limits.sponsorTransferTimeout).to.be.eq(5);
+      expect(limits.sponsorApproveTimeout).to.be.eq(5);
+      expect(limits.ownerCanTransfer).to.be.true;
+      expect(limits.ownerCanDestroy).to.be.true;
+      expect(limits.transfersEnabled).to.be.false;
+    }
+  });
+});
addedjs-packages/tests/maintenance.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/maintenance.seqtest.ts
@@ -0,0 +1,348 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {ApiPromise} from '@polkadot/api';
+import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js';
+import {itEth} from './eth/util/index.js';
+import {main as correctState} from './migrations/correctStateAfterMaintenance.js';
+import type {PalletBalancesIdAmount} from '@polkadot/types/lookup';
+import type {Vec} from '@polkadot/types-codec';
+
+async function maintenanceEnabled(api: ApiPromise): Promise<boolean> {
+  return (await api.query.maintenance.enabled()).toJSON() as boolean;
+}
+
+describe('Integration Test: Maintenance Functionality', () => {
+  let superuser: IKeyringPair;
+  let donor: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.Maintenance]);
+      superuser = await privateKey('//Alice');
+      donor = await privateKey({url: import.meta.url});
+      [bob] = await helper.arrange.createAccounts([10000n], donor);
+
+    });
+  });
+
+  describe('Maintenance Mode', () => {
+    before(async function() {
+      await usingPlaygrounds(async (helper) => {
+        if(await maintenanceEnabled(helper.getApi())) {
+          console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.');
+          await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled;
+        }
+      });
+    });
+
+    itSub('Allows superuser to enable and disable maintenance mode - and disallows anyone else', async ({helper}) => {
+      // Make sure non-sudo can't enable maintenance mode
+      await expect(helper.executeExtrinsic(superuser, 'api.tx.maintenance.enable', []), 'on commoner enabling MM')
+        .to.be.rejectedWith(/BadOrigin/);
+
+      // Set maintenance mode
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
+
+      // Make sure non-sudo can't disable maintenance mode
+      await expect(helper.executeExtrinsic(bob, 'api.tx.maintenance.disable', []), 'on commoner disabling MM')
+        .to.be.rejectedWith(/BadOrigin/);
+
+      // Disable maintenance mode
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
+    });
+
+    itSub('MM blocks unique pallet calls', async ({helper}) => {
+      // Can create an NFT collection before enabling the MM
+      const nftCollection = await helper.nft.mintCollection(bob, {
+        tokenPropertyPermissions: [{
+          key: 'test', permission: {
+            collectionAdmin: true,
+            tokenOwner: true,
+            mutable: true,
+          },
+        }],
+      });
+
+      // Can mint an NFT before enabling the MM
+      const nft = await nftCollection.mintToken(bob);
+
+      // Can create an FT collection before enabling the MM
+      const ftCollection = await helper.ft.mintCollection(superuser);
+
+      // Can mint an FT before enabling the MM
+      await expect(ftCollection.mint(superuser)).to.be.fulfilled;
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
+
+      // Unable to create a collection when the MM is enabled
+      await expect(helper.nft.mintCollection(superuser), 'cudo forbidden stuff')
+        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
+
+      // Unable to set token properties when the MM is enabled
+      await expect(nft.setProperties(
+        bob,
+        [{key: 'test', value: 'test-val'}],
+      )).to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
+
+      // Unable to mint an NFT when the MM is enabled
+      await expect(nftCollection.mintToken(superuser))
+        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
+
+      // Unable to mint an FT when the MM is enabled
+      await expect(ftCollection.mint(superuser))
+        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
+
+      // Can create a collection after disabling the MM
+      await expect(helper.nft.mintCollection(bob), 'MM is disabled, the collection should be created').to.be.fulfilled;
+
+      // Can set token properties after disabling the MM
+      await nft.setProperties(bob, [{key: 'test', value: 'test-val'}]);
+
+      // Can mint an NFT after disabling the MM
+      await nftCollection.mintToken(bob);
+
+      // Can mint an FT after disabling the MM
+      await ftCollection.mint(superuser);
+    });
+
+    itSub.ifWithPallets('MM blocks unique pallet calls (Re-Fungible)', [Pallets.ReFungible], async ({helper}) => {
+      // Can create an RFT collection before enabling the MM
+      const rftCollection = await helper.rft.mintCollection(superuser);
+
+      // Can mint an RFT before enabling the MM
+      await expect(rftCollection.mintToken(superuser)).to.be.fulfilled;
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
+
+      // Unable to mint an RFT when the MM is enabled
+      await expect(rftCollection.mintToken(superuser))
+        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
+
+      // Can mint an RFT after disabling the MM
+      await rftCollection.mintToken(superuser);
+    });
+
+    itSub('MM allows native token transfers and RPC calls', async ({helper}) => {
+      // We can use RPC before the MM is enabled
+      const totalCount = await helper.collection.getTotalCount();
+
+      // We can transfer funds before the MM is enabled
+      await expect(helper.balance.transferToSubstrate(superuser, bob.address, 2n)).to.be.fulfilled;
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
+
+      // RPCs work while in maintenance
+      expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount);
+
+      // We still able to transfer funds
+      await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled;
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
+
+      // RPCs work after maintenance
+      expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount);
+
+      // Transfers work after maintenance
+      await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled;
+    });
+
+    itSched.ifWithPallets('MM blocks scheduled calls and the scheduler itself', [Pallets.UniqueScheduler], async (scheduleKind, {helper}) => {
+      const collection = await helper.nft.mintCollection(bob);
+
+      const nftBeforeMM = await collection.mintToken(bob);
+      const nftDuringMM = await collection.mintToken(bob);
+      const nftAfterMM = await collection.mintToken(bob);
+
+      const [
+        scheduledIdBeforeMM,
+        scheduledIdDuringMM,
+        scheduledIdBunkerThroughMM,
+        scheduledIdAttemptDuringMM,
+        scheduledIdAfterMM,
+      ] = scheduleKind == 'named'
+        ? helper.arrange.makeScheduledIds(5)
+        : new Array(5);
+
+      const blocksToWait = 6;
+
+      // Scheduling works before the maintenance
+      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdBeforeMM})
+        .nft.transferToken(bob, collection.collectionId, nftBeforeMM.tokenId, {Substrate: superuser.address});
+
+
+      await helper.wait.newBlocks(blocksToWait + 1);
+      expect(await nftBeforeMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
+
+      // Schedule a transaction that should occur *during* the maintenance
+      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdDuringMM})
+        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
+
+
+      // Schedule a transaction that should occur *after* the maintenance
+      await helper.scheduler.scheduleAfter(blocksToWait * 2, {scheduledId: scheduledIdBunkerThroughMM})
+        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
+
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
+
+      await helper.wait.newBlocks(blocksToWait + 1);
+      // The owner should NOT change since the scheduled transaction should be rejected
+      expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: bob.address});
+
+      // Any attempts to schedule a tx during the MM should be rejected
+      await expect(helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAttemptDuringMM})
+        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address}))
+        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
+
+      // Scheduling works after the maintenance
+      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM})
+        .nft.transferToken(bob, collection.collectionId, nftAfterMM.tokenId, {Substrate: superuser.address});
+
+      await helper.wait.newBlocks(blocksToWait + 1);
+
+      expect(await nftAfterMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
+      // The owner of the token scheduled for transaction *before* maintenance should now change *after* maintenance
+      expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
+    });
+
+    itEth('Disallows Ethereum transactions to execute while in maintenance', async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const receiver = helper.eth.createAccount();
+
+      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'B', 'C', '');
+
+      // Set maintenance mode
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
+
+      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      const tokenId = await contract.methods.nextTokenId().call();
+      expect(tokenId).to.be.equal('1');
+
+      await expect(contract.methods.mintWithTokenURI(receiver, 'Test URI').send())
+        .to.be.rejectedWith(/Returned error: unknown error/);
+
+      await expect(contract.methods.ownerOf(tokenId).call()).rejectedWith(/token not found/);
+
+      // Disable maintenance mode
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
+    });
+
+    itSub('Allows to enable and disable MM repeatedly', async ({helper}) => {
+      // Set maintenance mode
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
+
+      // Disable maintenance mode
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
+    });
+
+    afterEach(async () => {
+      await usingPlaygrounds(async helper => {
+        if(helper.fetchMissingPalletNames([Pallets.Maintenance]).length != 0) return;
+        if(await maintenanceEnabled(helper.getApi())) {
+          console.warn('\tMaintenance mode was left enabled AFTER a test has finished! Be careful. Disabling it now.');
+          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+        }
+        expect(await maintenanceEnabled(helper.getApi()), 'Disastrous! Exited the test suite with maintenance mode on.').to.be.false;
+      });
+    });
+  });
+
+  describe('Integration Test: Maintenance mode & App Promo', () => {
+    let superuser: IKeyringPair;
+
+    before(async function() {
+      await usingPlaygrounds(async (helper, privateKey) => {
+        requirePalletsOrSkip(this, helper, [Pallets.Maintenance]);
+        superuser = await privateKey('//Alice');
+      });
+    });
+
+    describe('Test AppPromo script for check state after Maintenance mode', () => {
+      before(async function () {
+        await usingPlaygrounds(async (helper) => {
+          if(await maintenanceEnabled(helper.getApi())) {
+            console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.');
+            await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled;
+          }
+        });
+      });
+      itSub('Can find and fix inconsistent state', async ({helper}) => {
+        const api = helper.getApi();
+
+        await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.system.setStorage([
+          // pendingUnstake(1 -> [superuser.address, 100UNQ])
+          ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f5153cb1f00942ff401000000',
+            '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'],
+          // pendingUnstake(2 -> [superuser.address, 100UNQ])
+          ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f9eb2dcce60f37a2702000000',
+            '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'],
+          // Balances.freezes(superuser.address -> freeze with app promo id and 200 UNQ )
+          ['0xc2261276cc9d1f8598ea4b6a74b15c2fb1c0eb12e038e5c7f91e120ed4b7ebf1de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d',
+            '0x046170707374616b656170707374616b65000020c65abc8ed70a00000000000000'],
+        ])]);
+
+        expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]);
+        expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]);
+        expect((await api.query.balances.freezes(superuser.address) as Vec<PalletBalancesIdAmount>)
+          .map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()})))
+          .to.be.deep.equal([{id: 'appstakeappstake', amount: 200000000000000000000n}]);
+        await correctState();
+
+        expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([]);
+        expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([]);
+        expect((await api.query.balances.freezes(superuser.address)).toJSON()).to.be.deep.equal([]);
+
+      });
+
+      itSub('(!negative test!) Only works when Maintenance mode is disabled', async({helper}) => {
+        await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', [])).to.be.fulfilled;
+        await expect(correctState()).to.be.rejectedWith('The network is still in maintenance mode');
+        await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled;
+      });
+    });
+  });
+
+  after(async () => {
+    await usingPlaygrounds(async(helper) => {
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
+    });
+  });
+});
addedjs-packages/tests/migrations/942057-appPromotion/README.mddiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/942057-appPromotion/README.md
@@ -0,0 +1,42 @@
+# Update Procedure
+
+- Enable maintenance mode
+- [Collect migration data using ChainQL](#stakers-data-loading)
+- ❗️❗️❗️ Initiate the runtime upgrade only at this point ❗️❗️❗️
+- Wait for the upgrade to complete
+- [Execute offchain migration](#execute-offchain-migration)
+- Disable maintenance mode
+
+## Stakers Data Loading
+
+Set the environment variable (WS_RPC). For example, ws://localhost:9944. Execute the following command:
+
+```sh
+chainql --tla-str=chainUrl=<WS_RPC> stakersParser.jsonnet > output.json
+```
+
+where `<WS_RPC>` - is the network address.
+
+Example for Opal:
+
+```sh
+chainql --tla-str=chainUrl=wss://eu-ws-opal.unique.network:443 stakersParser.jsonnet > output.json
+```
+
+To install chainql, execute the following command:
+
+```sh
+cargo install chainql
+```
+
+## Execute offchain migration
+
+To run, you need to set an environment variables:
+- `SUPERUSER_SEED` – the sudo key seed.
+- `WS_RPC` – the network address
+
+Run the migration by executing the following command:
+
+```sh
+npx ts-node --esm executeMigration.ts
+```
addedjs-packages/tests/migrations/942057-appPromotion/collectData.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/942057-appPromotion/collectData.ts
@@ -0,0 +1,12 @@
+import {exec} from 'child_process';
+import path from 'path';
+import {dirname} from 'path';
+import {fileURLToPath} from 'url';
+
+export const collectData = () => {
+  const dirName = dirname(fileURLToPath(import.meta.url));
+
+  const pathToScript = path.resolve(dirName, './stakersParser.jsonnet');
+  const outputPath = path.resolve(dirName, './output.json');
+  exec(`chainql --tla-str=chainUrl=ws://127.0.0.1:9944 ${pathToScript} > ${outputPath}`);
+};
addedjs-packages/tests/migrations/942057-appPromotion/executeMigration.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/942057-appPromotion/executeMigration.ts
@@ -0,0 +1,11 @@
+import {migrateLockedToFreeze} from './lockedToFreeze.js';
+
+
+const WS_RPC = process.env.WS_RPC || 'wss://ws-opal.unique.network:443';
+const SUPERUSER_SEED = process.env.SUPERUSER_SEED || '';
+
+migrateLockedToFreeze({
+  wsEndpoint: WS_RPC,
+  donorSeed: SUPERUSER_SEED,
+})
+  .catch(console.error);
\ No newline at end of file
addedjs-packages/tests/migrations/942057-appPromotion/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/942057-appPromotion/index.ts
@@ -0,0 +1,12 @@
+import type {Migration} from '../../util/frankensteinMigrate.js';
+import {collectData} from './collectData.js';
+import {migrateLockedToFreeze} from './lockedToFreeze.js';
+
+export const migration: Migration = {
+  async before() {
+    await collectData();
+  },
+  async after() {
+    await migrateLockedToFreeze();
+  },
+};
\ No newline at end of file
addedjs-packages/tests/migrations/942057-appPromotion/lockedToFreeze.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/942057-appPromotion/lockedToFreeze.ts
@@ -0,0 +1,259 @@
+// import { usingApi, privateKey, onlySign } from "./../../load/lib";
+import * as fs from 'fs';
+import {usingPlaygrounds} from '../../util/index.js';
+import path, {dirname} from 'path';
+import {isInteger, parse} from 'lossless-json';
+import {fileURLToPath} from 'url';
+import config from '../../config.js';
+
+
+const WS_ENDPOINT = config.substrateUrl;
+const DONOR_SEED = '//Alice';
+const UPDATE_IF_VERSION = 942057;
+
+export function customNumberParser(value: any) {
+  return isInteger(value) ? BigInt(value) : parseFloat(value);
+}
+
+export const migrateLockedToFreeze = async(options: { wsEndpoint: string; donorSeed: string } = {
+  wsEndpoint: WS_ENDPOINT,
+  donorSeed: DONOR_SEED,
+}) => {
+  await usingPlaygrounds(async (helper, privateKey) => {
+    const api = helper.getApi();
+    // 1. Check version equal 942057 or skip
+    console.log((api.consts.system.version as any).specVersion.toNumber());
+    if((api.consts.system.version as any).specVersion.toNumber() != UPDATE_IF_VERSION) {
+      console.log("Version isn't 942057.");
+      return;
+    }
+
+    // 2. Get sudo signer
+    const signer = await privateKey(options.donorSeed);
+    console.log('2. Getting sudo:', signer.address);
+
+    // 3. Parse data to migrate
+    console.log('3. Parsing chainql results...');
+    const dirName = dirname(fileURLToPath(import.meta.url));
+    const parsingResult = parse(fs.readFileSync(path.resolve(dirName, 'output.json'), 'utf-8'), undefined, customNumberParser);
+
+    const chainqlImportData = parsingResult as {
+      address: string;
+      balance: string;
+      account: {
+        fee_frozen: string,
+        free: string,
+        misc_frozen: string,
+        reserved: string,
+      },
+      locks: {
+        amount: string,
+        id: string,
+      }[],
+      stakes: object,
+      unstakes: object,
+    }[];
+    testChainqlData(chainqlImportData);
+
+    const stakers = chainqlImportData.map((i) => i.address);
+
+    // 3.1 Split into chunks by 100
+    console.log('3.1 Splitting into chunks...');
+    const stakersChunks = chunk(stakers, 100);
+    console.log('3.1 Done, total chunks:', stakersChunks.length);
+
+    // 4. Get signer/sudo nonce
+    console.log('4. Getting sudo nonce...');
+    const signerAccount = (
+        await api.query.system.account(signer.address)
+      ).toJSON() as any;
+
+    let nonce: number = signerAccount.nonce;
+    console.log('4. Sudo nonce is:', nonce);
+
+    // 5. Only sign upgradeAccounts-transactions for each chunk
+    console.log('5. Signing transactions...');
+    const signedTxs = [];
+    for(const chunk of stakersChunks) {
+      const tx = api.tx.sudo.sudo(api.tx.appPromotion.upgradeAccounts(chunk));
+      const signed = tx.sign(signer, {
+        blockHash: api.genesisHash,
+        genesisHash: api.genesisHash,
+        runtimeVersion: api.runtimeVersion,
+        nonce: nonce++,
+      });
+      signedTxs.push(signed);
+    }
+
+    // 6. Send all signed transactions
+    console.log('6. Sending transactions...');
+    const promises = signedTxs.map((tx) => api.rpc.author.submitAndWatchExtrinsic(tx));
+    // 6.1 Wait all transactions settled
+    console.log('6.1 Waiting all transactions settled...');
+    const res = await Promise.allSettled(promises);
+
+    console.log('Wait 5 blocks for transactions to be included in a block...');
+    await helper.wait.newBlocks(5);
+    // 6.2 Filter failed transactions
+    console.log('6.2 Getting failed transactions...');
+    const failedTx = res.filter((r) => r.status == 'rejected') as PromiseRejectedResult[];
+    console.log('6.2. total failedTxs:', failedTx.length);
+
+    // 6.3 Log the reasons of failed tx
+    for(const tx of failedTx) {
+      console.log(tx.reason);
+    }
+
+    // 7. Check balances for 10 blocks:
+    console.log('7. Check balances...');
+    let blocksLeft = 10;
+    let notMigrated = stakers;
+    const suspiciousAccounts = [];
+    do {
+      console.log('blocks left:', blocksLeft);
+      const _notMigrated: string[] = [];
+      console.log('accounts to migrate...', notMigrated.length);
+      for(const accountToMigrate of notMigrated) {
+        let accountMigrated = true;
+        // 7.0 get data from chainql:
+        const oldAccount = chainqlImportData.find(acc => acc.address === accountToMigrate);
+        if(!oldAccount) {
+          console.log('Cannot find old account data for', accountMigrated);
+          accountMigrated = false;
+          _notMigrated.push(accountToMigrate);
+          continue;
+        }
+
+        // 7.1 system.account
+        const balance = await api.query.system.account(accountToMigrate) as any;
+        // new balances
+        const free = balance.data.free;
+        const reserved = balance.data.reserved;
+        const frozen = balance.data.frozen;
+        // old balances
+        const oldFree = oldAccount.account.free;
+        const oldReserved = oldAccount.account.reserved;
+        const oldFrozen = oldAccount.account.fee_frozen;
+        // asserts new = old
+        if(oldFree.toString() !== free.toString()) {
+          console.log('Old free !== New free, which is probably not a problem', oldFree.toString(), free.toString());
+          suspiciousAccounts.push(accountToMigrate);
+        }
+        if(oldFrozen.toString() !== frozen.toString()) {
+          console.log('Old frozen !== New frozen, which is probably not a problem', oldFrozen.toString(), frozen.toString());
+          suspiciousAccounts.push(accountToMigrate);
+        }
+        if(oldReserved.toString() !== reserved.toString()) {
+          console.log('Old reserved !== New reserved, which is probably not a problem', oldReserved.toString(), reserved.toString());
+          suspiciousAccounts.push(accountToMigrate);
+        }
+
+        // 7.2 balances.locks: no id appstake
+        const locks = await helper.balance.getLocked(accountToMigrate);
+        const appPromoLocks = locks.filter(lock => lock.id === 'appstake');
+        if(appPromoLocks.length !== 0) {
+          console.log('Account still has app-promo lock');
+          accountMigrated = false;
+        }
+
+        // 7.3 balances.freezes set...
+        let freezes = await api.query.balances.freezes(accountToMigrate) as any;
+        freezes = freezes.map((freez: any) => ({id: freez.id.toString(), amount: freez.amount.toString()}));
+        if(!freezes) {
+          console.log('Account does not have freezes');
+          accountMigrated = false;
+        } else {
+          const oldAppPromoLocks = oldAccount.locks.filter(l => l.id === '0x6170707374616b65'); // get app promo locks
+          // should be only one freez for each account
+          if(freezes.length !== 1) {
+            console.log('freezes.length !== 1 and old appPromoLocks.length', freezes.length, oldAppPromoLocks.length);
+            accountMigrated = false;
+          } else {
+            const appPromoFreez = freezes[0];
+            // freez-amount should be equal to migrated lock amount
+            if(appPromoFreez.amount.toString() !== oldAppPromoLocks[0].amount.toString()) {
+              console.log('freezes amount !== old appPromoLocks amount', appPromoFreez.amount.toString(), oldAppPromoLocks[0].amount.toString());
+              accountMigrated = false;
+            }
+            // freez id should be correct
+            if(appPromoFreez.id !== '0x6170707374616b656170707374616b65') {
+              console.log('Got freez with incorrect id:', appPromoFreez.id);
+              accountMigrated = false;
+            }
+          }
+        }
+
+        // 7.4 Stakes number the same
+        const stakesNumber = await helper.staking.getStakesNumber({Substrate: accountToMigrate});
+        const oldStakesNumber = oldAccount.stakes ? Object.keys(oldAccount.stakes).length : 0;
+        if(stakesNumber.toString() !== oldStakesNumber.toString()) {
+          console.log('Old stakes number !== New stakes number', oldStakesNumber, stakesNumber);
+          accountMigrated = false;
+        }
+
+        // 7.5 Total pendingUnstake + total staked = old locked
+        const pendingUnstakes = await helper.staking.getPendingUnstake({Substrate: accountToMigrate});
+        const totalStaked = await helper.staking.getTotalStaked({Substrate: accountToMigrate});
+        const totalBalanceInAppPromo = pendingUnstakes + totalStaked;
+        if(totalBalanceInAppPromo.toString() !== oldAccount.balance.toString()) {
+          console.log('totalBalanceInAppPromo !== old locked in app promo', totalBalanceInAppPromo.toString(), oldAccount.balance.toString());
+          accountMigrated = false;
+        }
+
+        // 8 Add to not-migrated
+        if(!accountMigrated) {
+          console.log('Add to not migrated:', accountToMigrate);
+          _notMigrated.push(accountToMigrate);
+        }
+      }
+
+      blocksLeft--;
+      notMigrated = _notMigrated;
+      await helper.wait.newBlocks(1);
+    } while(blocksLeft > 0 && notMigrated.length !== 0);
+
+    console.log('Not migrated accounts...', notMigrated.length);
+    if(suspiciousAccounts.length > 0) {
+      console.log('Saving suspicious accounts to suspicious.json:');
+      fs.writeFileSync('./suspicious.json', JSON.stringify(suspiciousAccounts));
+    }
+    if(notMigrated.length > 0) {
+      console.log('Saving not migrated list to failed.json:');
+      notMigrated.forEach(console.log);
+      fs.writeFileSync('./failed.json', JSON.stringify(notMigrated));
+      process.exit(1);
+    } else {
+      console.log('Migration success');
+    }
+  }, options.wsEndpoint);
+};
+
+const chunk = <T>(arr: T[], size: number) =>
+  Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) =>
+    arr.slice(i * size, i * size + size));
+
+const testChainqlData = (data: any) => {
+  const wrongData = [];
+  for(const account of data) {
+    try {
+      if(account.address == null) throw Error('no address in data');
+      if(account.balance == null) throw Error('no balance in data');
+      if(account.account == null) throw Error('no account in data');
+      if(account.account.fee_frozen == null) throw Error('no account.fee_frozen in data');
+      if(account.account.misc_frozen == null) throw Error('no account.misc_frozen in data');
+      if(account.account.free == null) throw Error('no account.free in data');
+      if(account.account.reserved == null) throw Error('no account.reserved in data');
+      if(account.locks == null) throw Error('no locks in data');
+      if(account.locks[0].amount == null) throw Error('no locks.amount in data');
+      if(account.locks[0].id == null) throw Error('no locks.id in data');
+    } catch (error) {
+      wrongData.push(account.address);
+      console.log((error as Error).message, account.address);
+    }
+    if(wrongData.length > 0) {
+      console.log(data);
+      throw Error('Chainql data not correct');
+    }
+  }
+  console.log('Chainql data correct');
+};
addedjs-packages/tests/migrations/942057-appPromotion/stakersParser.jsonnetdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/942057-appPromotion/stakersParser.jsonnet
@@ -0,0 +1,29 @@
+function(chainUrl)
+
+  local
+    state = cql.chain(chainUrl).latest,
+    locked_balances = state.Balances.Locks._preloadKeys,
+    accountsRaw = state.System.Account._preloadKeys,
+    stakers = state.AppPromotion.Staked._preloadKeys,
+    unstakes = state.AppPromotion.PendingUnstake._preloadKeys,
+    locks = [
+      { [k]: std.filter(function(l) l.id == '0x6170707374616b65', locked_balances[k]) }
+      for k in std.objectFields(locked_balances)
+    ],
+    unstakersData = [
+      { [pair[0]]: { block: block, value: pair[1] } }
+
+      for block in std.objectFields(unstakes)
+      for pair in unstakes[block]
+    ],
+    non_empty_locks = std.prune(locks)
+  ;
+
+  std.map(function(a) {
+    address: std.objectFields(a)[0],
+    balance: a[std.objectFields(a)[0]][0].amount,
+    account: accountsRaw[std.objectFields(a)[0]].data,
+    locks: locked_balances[std.objectFields(a)[0]],
+    stakes: if std.objectHas(stakers, std.objectFields(a)[0]) then stakers[std.objectFields(a)[0]] else null,
+    unstakes: std.filterMap(function(b) std.objectHas(b, std.objectFields(a)[0]), function(c) std.objectValues(c)[0], unstakersData),
+  }, non_empty_locks)
addedjs-packages/tests/migrations/correctStateAfterMaintenance.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/correctStateAfterMaintenance.ts
@@ -0,0 +1,69 @@
+import config from '../config.js';
+import {usingPlaygrounds} from '../util/index.js';
+import type {u32} from '@polkadot/types-codec';
+
+const WS_ENDPOINT = config.substrateUrl;
+const DONOR_SEED = '//Alice';
+
+export const main = async(options: { wsEndpoint: string; donorSeed: string } = {
+  wsEndpoint: WS_ENDPOINT,
+  donorSeed: DONOR_SEED,
+}) => {
+  await usingPlaygrounds(async (helper, privateKey) => {
+    const api = helper.getApi();
+
+    if((await api.query.maintenance.enabled()).valueOf()) {
+      throw Error('The network is still in maintenance mode');
+    }
+
+    const pendingBlocks = (
+      await api.query.appPromotion.pendingUnstake.entries()
+    ).map(([k, _v]) =>
+      (k.args[0] as u32).toNumber());
+
+    const currentBlock = (await api.query.system.number() as u32).toNumber();
+
+    const filteredBlocks = pendingBlocks.filter(b => currentBlock > b);
+
+    if(filteredBlocks.length != 0) {
+      console.log(`During maintenance mode, ${filteredBlocks.length} block(s) were not processed. Number(s): ${filteredBlocks}`);
+    } else {
+      console.log('Nothing to change');
+      return;
+    }
+
+    const skippedBlocks = chunk(filteredBlocks, 10);
+
+    const signer = await privateKey(options.donorSeed);
+
+    const txs = skippedBlocks.map((b) =>
+      api.tx.sudo.sudo(api.tx.appPromotion.forceUnstake(b)));
+
+
+    const promises = txs.map((tx) => () => helper.signTransaction(signer, tx));
+
+    await Promise.allSettled(promises.map((p) => p()));
+
+    const failedBlocks: number[] = [];
+    let isSuccess = true;
+
+    for(const b of filteredBlocks) {
+      if(((await api.query.appPromotion.pendingUnstake(b)).toJSON() as any[]).length != 0) {
+        failedBlocks.push(b);
+        isSuccess = false;
+      }
+    }
+
+    if(isSuccess) {
+      console.log('Done. %d block(s) were processed.', filteredBlocks.length);
+    } else {
+      throw new Error(`Something went wrong. Block(s) have not been processed: ${failedBlocks}`);
+    }
+
+
+  }, options.wsEndpoint);
+};
+
+const chunk = <T>(arr: T[], size: number) =>
+  Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) =>
+    arr.slice(i * size, i * size + size));
addedjs-packages/tests/migrations/runCheckState.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/migrations/runCheckState.ts
@@ -0,0 +1,10 @@
+import {main} from './correctStateAfterMaintenance.js';
+
+main({
+  wsEndpoint: process.env.WS_RPC!,
+  donorSeed: process.env.SUPERUSER_SEED!,
+}).then(() => process.exit(0))
+  .catch((e) => {
+    console.error(e);
+    process.exit(1);
+  });
addedjs-packages/tests/nativeFungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nativeFungible.test.ts
@@ -0,0 +1,221 @@
+// Copyright 2019-2023 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from './util/index.js';
+
+describe('Native fungible', () => {
+  let root: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      root = await privateKey('//Alice');
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
+    });
+  });
+
+  itSub('destroy_collection()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.burn(alice)).to.be.rejectedWith('common.UnsupportedOperation');
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.destroyCollection', [0])).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('add_to_allow_list()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.addToAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('remove_from_allow_list()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.removeFromAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('change_collection_owner()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.changeOwner(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('add_collection_admin()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('remove_collection_admin()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.removeAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('set_collection_sponsor()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.setSponsor(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('confirm_sponsorship()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.confirmSponsorship(alice)).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('remove_collection_sponsor()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.removeSponsor(alice)).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('create_item()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.mint(alice, 100n, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('set_collection_properties()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.setProperties(alice, [{key: 'value'}])).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('delete_collection_properties()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.deleteProperties(alice, ['key'])).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('set_token_properties()', async ({helper}) => {
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.setTokenProperties',
+      [0, 0, [{key: 'value'}]],
+      true,
+    )).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('delete_token_properties()', async ({helper}) => {
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.deleteTokenProperties',
+      [0, 0, ['key']],
+      true,
+    )).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('set_transfers_enabled_flag()', async ({helper}) => {
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.setTransfersEnabledFlag',
+      [0, true],
+      true,
+    )).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('burn_item()', async ({helper}) => {
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.burnItem',
+      [0, 0, 100n],
+      true,
+    )).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('burn_from()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.burnTokens(alice, 100n)).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('transfer()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    const balanceAliceBefore = await helper.balance.getSubstrate(alice.address);
+    const balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+    await collection.transfer(alice, {Substrate: bob.address}, 100n);
+    const balanceAliceAfter = await helper.balance.getSubstrate(alice.address);
+    const balanceBobAfter = await helper.balance.getSubstrate(bob.address);
+    expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true;
+    expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true;
+  });
+
+  itSub('transfer_from()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    const balanceAliceBefore = await helper.balance.getSubstrate(alice.address);
+    const balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+
+    await collection.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address}, 100n);
+
+    const balanceAliceAfter = await helper.balance.getSubstrate(alice.address);
+    const balanceBobAfter = await helper.balance.getSubstrate(bob.address);
+    expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true;
+    expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true;
+
+    await expect(collection.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address}, 100n)).to.be.rejectedWith('common.ApprovedValueTooLow');
+  });
+
+  itSub('approve()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.approveTokens(alice, {Substrate: bob.address}, 100n)).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('approve_from()', async ({helper}) => {
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.approveFrom',
+      [{Substrate: alice.address}, {Substrate: bob.address}, 0, 0, 100n],
+      true,
+    )).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('set_collection_limits()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.setLimits(alice, {accountTokenOwnershipLimit: 1})).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('set_collection_permissions()', async ({helper}) => {
+    const collection = helper.ft.getCollectionObject(0);
+    await expect(collection.setPermissions(alice, {access: 'AllowList'})).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('repartition()', async ({helper}) => {
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.repartition',
+      [0, 0, 100n],
+      true,
+    )).to.be.rejectedWith('unique.RepartitionCalledOnNonRefungibleCollection');
+  });
+
+  itSub('force_repair_collection()', async ({helper}) => {
+    await expect(helper.executeExtrinsic(
+      alice,
+      'api.tx.unique.forceRepairCollection',
+      [0],
+      true,
+    )).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('force_repair_item()', async ({helper}) => {
+    await expect(helper.getSudo().executeExtrinsic(
+      root,
+      'api.tx.unique.forceRepairItem',
+      [0, 0],
+      true,
+    )).to.be.rejectedWith('common.UnsupportedOperation');
+  });
+
+  itSub('Nest into NFT token()', async ({helper}) => {
+    const nftCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await nftCollection.mintToken(alice);
+
+    const collection = helper.ft.getCollectionObject(0);
+    await collection.transfer(alice, targetToken.nestingAccount(), 100n);
+
+    await collection.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 100n);
+  });
+});
\ No newline at end of file
addedjs-packages/tests/nesting/collectionProperties.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nesting/collectionProperties.seqtest.ts
@@ -0,0 +1,61 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js';
+
+describe('Integration Test: Collection Properties with sudo', () => {
+  let superuser: IKeyringPair;
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      superuser = await privateKey('//Alice');
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'ft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
+    before(async function() {
+      // eslint-disable-next-line require-await
+      await usingPlaygrounds(async helper => {
+        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
+      });
+    });
+
+    itSub('Repairing an unbroken collection\'s properties preserves the consumed space', async({helper}) => {
+      const properties = [
+        {key: 'sea-creatures', value: 'mermaids'},
+        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
+      ];
+      const collection = await helper[testSuite.mode].mintCollection(alice, {properties});
+
+      const newProperty = {key: 'space', value: ' '.repeat(4096)};
+      await collection.setProperties(alice, [newProperty]);
+      const originalSpace = await collection.getPropertiesConsumedSpace();
+      expect(originalSpace).to.be.equal(sizeOfProperty(properties[0]) + sizeOfProperty(properties[1]) + sizeOfProperty(newProperty));
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true);
+      const recomputedSpace = await collection.getPropertiesConsumedSpace();
+      expect(recomputedSpace).to.be.equal(originalSpace);
+    });
+  }));
+});
addedjs-packages/tests/nesting/collectionProperties.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nesting/collectionProperties.test.ts
@@ -0,0 +1,326 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js';
+
+describe('Integration Test: Collection Properties', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([200n, 10n], donor);
+    });
+  });
+
+  itSub('Properties are initially empty', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    expect(await collection.getProperties()).to.be.empty;
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'ft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
+    before(async function() {
+      // eslint-disable-next-line require-await
+      await usingPlaygrounds(async helper => {
+        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
+      });
+    });
+
+    itSub('Sets properties for a collection', async ({helper}) =>  {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      // As owner
+      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled;
+
+      await collection.addAdmin(alice, {Substrate: bob.address});
+
+      // As administrator
+      await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled;
+
+      const properties = await collection.getProperties();
+      expect(properties).to.include.deep.members([
+        {key: 'electron', value: 'come bond'},
+        {key: 'black_hole', value: ''},
+      ]);
+    });
+
+    itSub('Check valid names for collection properties keys', async ({helper}) =>  {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      // alpha symbols
+      await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled;
+
+      // numeric symbols
+      await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled;
+
+      // underscore symbol
+      await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled;
+
+      // dash symbol
+      await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled;
+
+      // dot symbol
+      await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled;
+
+      const properties = await collection.getProperties();
+      expect(properties).to.include.deep.members([
+        {key: 'answer', value: ''},
+        {key: '451', value: ''},
+        {key: 'black_hole', value: ''},
+        {key: '-', value: ''},
+        {key: 'once.in.a.long.long.while...', value: 'you get a little lost'},
+      ]);
+    });
+
+    itSub('Changes properties of a collection', async ({helper}) =>  {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled;
+
+      // Mutate the properties
+      await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;
+
+      const properties = await collection.getProperties();
+      expect(properties).to.include.deep.members([
+        {key: 'electron', value: 'come bond'},
+        {key: 'black_hole', value: 'LIGO'},
+      ]);
+    });
+
+    itSub('Deletes properties of a collection', async ({helper}) =>  {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;
+
+      await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled;
+
+      const properties = await collection.getProperties(['black_hole', 'electron']);
+      expect(properties).to.be.deep.equal([
+        {key: 'black_hole', value: 'LIGO'},
+      ]);
+    });
+
+    itSub('Allows modifying a collection property multiple times with the same size', async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      const maxCollectionPropertiesSize = 40960;
+
+      const propDataSize = 4096;
+
+      let propDataChar = 'a';
+      const makeNewPropData = () => {
+        propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1);
+        return `${propDataChar}`.repeat(propDataSize);
+      };
+
+      const property = {key: propKey, value: makeNewPropData()};
+      await collection.setProperties(alice, [property]);
+      const originalSpace = await collection.getPropertiesConsumedSpace();
+      expect(originalSpace).to.be.equal(sizeOfProperty(property));
+
+      const sameSizePropertiesPossibleNum = maxCollectionPropertiesSize / propDataSize;
+
+      // It is possible to modify a property as many times as needed.
+      // It will not consume any additional space.
+      for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) {
+        await collection.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);
+        const consumedSpace = await collection.getPropertiesConsumedSpace();
+        expect(consumedSpace).to.be.equal(originalSpace);
+      }
+    });
+
+    itSub('Adding then removing a collection property doesn\'t change the consumed space', async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+      const originalSpace = await collection.getPropertiesConsumedSpace();
+
+      const propDataSize = 4096;
+      const propData = 'a'.repeat(propDataSize);
+
+      const property = {key: propKey, value: propData};
+      await collection.setProperties(alice, [property]);
+      let consumedSpace = await collection.getPropertiesConsumedSpace();
+      expect(consumedSpace).to.be.equal(sizeOfProperty(property));
+
+      await collection.deleteProperties(alice, [propKey]);
+      consumedSpace = await collection.getPropertiesConsumedSpace();
+      expect(consumedSpace).to.be.equal(originalSpace);
+    });
+
+    itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+      const originalSpace = await collection.getPropertiesConsumedSpace();
+
+      const initProp = {key: propKey, value: 'a'.repeat(4096)};
+      const biggerProp = {key: propKey, value: 'b'.repeat(5000)};
+      const smallerProp = {key: propKey, value: 'c'.repeat(4000)};
+
+      let consumedSpace;
+      let expectedConsumedSpaceDiff;
+
+      await collection.setProperties(alice, [initProp]);
+      consumedSpace = await collection.getPropertiesConsumedSpace();
+      expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace;
+      expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff);
+
+      await collection.setProperties(alice, [biggerProp]);
+      consumedSpace = await collection.getPropertiesConsumedSpace();
+      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp);
+      expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff);
+
+      await collection.setProperties(alice, [smallerProp]);
+      consumedSpace = await collection.getPropertiesConsumedSpace();
+      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp);
+      expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff);
+    });
+  }));
+});
+
+describe('Negative Integration Test: Collection Properties', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([1000n, 100n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'ft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
+    before(async function() {
+      // eslint-disable-next-line require-await
+      await usingPlaygrounds(async helper => {
+        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
+      });
+    });
+
+    itSub('Fails to set properties in a collection if not its onwer/administrator', async ({helper}) =>  {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]))
+        .to.be.rejectedWith(/common\.NoPermission/);
+
+      expect(await collection.getProperties()).to.be.empty;
+    });
+
+    itSub('Fails to set properties that exceed the limits', async ({helper}) =>  {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      const spaceLimit = (helper.getApi().consts.unique.maxCollectionPropertiesSize as any).toNumber();
+
+      // Mute the general tx parsing error, too many bytes to process
+      {
+        console.error = () => {};
+        await expect(collection.setProperties(alice, [
+          {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))},
+        ])).to.be.rejected;
+      }
+
+      expect(await collection.getProperties(['electron'])).to.be.empty;
+
+      await expect(collection.setProperties(alice, [
+        {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))},
+        {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))},
+      ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);
+
+      expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty;
+    });
+
+    itSub('Fails to set more properties than it is allowed', async ({helper}) =>  {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      const propertiesToBeSet = [];
+      for(let i = 0; i < 65; i++) {
+        propertiesToBeSet.push({
+          key: 'electron_' + i,
+          value: Math.random() > 0.5 ? 'high' : 'low',
+        });
+      }
+
+      await expect(collection.setProperties(alice, propertiesToBeSet)).
+        to.be.rejectedWith(/common\.PropertyLimitReached/);
+
+      expect(await collection.getProperties()).to.be.empty;
+    });
+
+    itSub('Fails to set properties with invalid names', async ({helper}) => {
+      const collection = await helper[testSuite.mode].mintCollection(alice);
+
+      const invalidProperties = [
+        [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],
+        [{key: 'Mr/Sandman', value: 'Bring me a gene'}],
+        [{key: 'déjà vu', value: 'hmm...'}],
+      ];
+
+      for(let i = 0; i < invalidProperties.length; i++) {
+        await expect(
+          collection.setProperties(alice, invalidProperties[i]),
+          `on rejecting the new badly-named property #${i}`,
+        ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
+      }
+
+      await expect(
+        collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]),
+        'on rejecting an unnamed property',
+      ).to.be.rejectedWith(/common\.EmptyPropertyKey/);
+
+      await expect(
+        collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]),
+        'on setting the correctly-but-still-badly-named property',
+      ).to.be.fulfilled;
+
+      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');
+
+      const properties = await collection.getProperties(keys);
+      expect(properties).to.be.deep.equal([
+        {key: 'CRISPR-Cas9', value: 'rewriting nature!'},
+      ]);
+
+      for(let i = 0; i < invalidProperties.length; i++) {
+        await expect(
+          collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)),
+          `on trying to delete the non-existent badly-named property #${i}`,
+        ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
+      }
+    });
+
+    itSub('Forbids to repair a collection if called with non-sudo', async({helper}) => {
+      const collection = await helper[testSuite.mode].mintCollection(alice, {properties: [
+        {key: 'sea-creatures', value: 'mermaids'},
+        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
+      ]});
+
+      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true))
+        .to.be.rejectedWith(/BadOrigin/);
+    });
+  }));
+});
addedjs-packages/tests/nesting/graphs.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nesting/graphs.test.ts
@@ -0,0 +1,86 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from '../util/index.js';
+import {UniqueHelper, UniqueNFTCollection, UniqueNFToken} from '@unique/playgrounds/unique.js';
+
+/**
+ * ```dot
+ * 4 -> 3 -> 2 -> 1
+ * 7 -> 6 -> 5 -> 2
+ * 8 -> 5
+ * ```
+ */
+async function buildComplexObjectGraph(helper: UniqueHelper, sender: IKeyringPair): Promise<[UniqueNFTCollection,UniqueNFToken[]]> {
+  const collection = await helper.nft.mintCollection(sender, {permissions: {nesting: {tokenOwner: true}}});
+  const tokens = await collection.mintMultipleTokens(sender, Array(8).fill({owner: {Substrate: sender.address}}));
+
+  await tokens[7].nest(sender, tokens[4]);
+  await tokens[6].nest(sender, tokens[5]);
+  await tokens[5].nest(sender, tokens[4]);
+  await tokens[4].nest(sender, tokens[1]);
+  await tokens[3].nest(sender, tokens[2]);
+  await tokens[2].nest(sender, tokens[1]);
+  await tokens[1].nest(sender, tokens[0]);
+
+  return [collection, tokens];
+}
+
+describe('Graphs', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([10n], donor);
+    });
+  });
+
+  itSub('Ouroboros can\'t be created in a complex graph', async ({helper}) => {
+    const [collection, tokens] = await buildComplexObjectGraph(helper, alice);
+
+    await collection.setPermissions(alice, {nesting: {collectionAdmin: false, tokenOwner: true}});
+
+    // [token owner] to self
+    await expect(
+      tokens[0].nest(alice, tokens[0]),
+      '[token owner] self-nesting is forbidden',
+    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+    // [token owner] to nested part of graph
+    await expect(
+      tokens[0].nest(alice, tokens[4]),
+      '[token owner] cannot nest the root node into an internal node',
+    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+    await expect(
+      tokens[1].transferFrom(alice, tokens[0].nestingAccount(), tokens[7].nestingAccount()),
+      '[token owner] cannot nest higher internal node into lower internal node',
+    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+
+    await collection.setPermissions(alice, {nesting: {collectionAdmin: true, tokenOwner: false}});
+
+    // [collection owner] to self
+    await expect(
+      tokens[0].nest(alice, tokens[0]),
+      '[collection owner] self-nesting is forbidden',
+    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+    // [collection owner] to nested part of graph
+    await expect(
+      tokens[0].nest(alice, tokens[4]),
+      '[collection owner] cannot nest the root node into an internal node',
+    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
+  });
+});
addedjs-packages/tests/nesting/propertyPermissions.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nesting/propertyPermissions.test.ts
@@ -0,0 +1,198 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, usingPlaygrounds, expect} from '../util/index.js';
+import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js';
+
+describe('Integration Test: Access Rights to Token Properties', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
+    });
+  });
+
+  itSub('Reads access rights to properties of a collection', async ({helper}) =>  {
+    const collection = await helper.nft.mintCollection(alice);
+    const propertyRights = (await helper.callRpc('api.query.common.collectionPropertyPermissions', [collection.collectionId])).toJSON();
+    expect(propertyRights).to.be.empty;
+  });
+
+  async function testSetsAccessRightsToProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {
+    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true}}]))
+      .to.be.fulfilled;
+
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    await expect(collection.setTokenPropertyPermissions(bob, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]))
+      .to.be.fulfilled;
+
+    const propertyRights = await collection.getPropertyPermissions(['skullduggery', 'mindgame']);
+    expect(propertyRights).to.include.deep.members([
+      {key: 'skullduggery', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}},
+      {key: 'mindgame', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},
+    ]);
+  }
+
+  itSub('Sets access rights to properties of a collection (NFT)', async ({helper}) =>  {
+    await testSetsAccessRightsToProperties(await helper.nft.mintCollection(alice));
+  });
+
+  itSub.ifWithPallets('Sets access rights to properties of a collection (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    await testSetsAccessRightsToProperties(await helper.rft.mintCollection(alice));
+  });
+
+  async function testChangesAccessRightsToProperty(collection: UniqueNFTCollection | UniqueRFTCollection) {
+    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]))
+      .to.be.fulfilled;
+
+    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))
+      .to.be.fulfilled;
+
+    const propertyRights = await collection.getPropertyPermissions();
+    expect(propertyRights).to.be.deep.equal([
+      {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
+    ]);
+  }
+
+  itSub('Changes access rights to properties of a NFT collection', async ({helper}) =>  {
+    await testChangesAccessRightsToProperty(await helper.nft.mintCollection(alice));
+  });
+
+  itSub.ifWithPallets('Changes access rights to properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
+    await testChangesAccessRightsToProperty(await helper.rft.mintCollection(alice));
+  });
+});
+
+describe('Negative Integration Test: Access Rights to Token Properties', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
+    });
+  });
+
+  async function testPreventsFromSettingAccessRightsNotAdminOrOwner(collection: UniqueNFTCollection | UniqueRFTCollection) {
+    await expect(collection.setTokenPropertyPermissions(bob, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]))
+      .to.be.rejectedWith(/common\.NoPermission/);
+
+    const propertyRights = await collection.getPropertyPermissions(['skullduggery']);
+    expect(propertyRights).to.be.empty;
+  }
+
+  itSub('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async ({helper}) =>  {
+    await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.nft.mintCollection(alice));
+  });
+
+  itSub.ifWithPallets('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', [Pallets.ReFungible], async ({helper}) => {
+    await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.rft.mintCollection(alice));
+  });
+
+  async function testPreventFromAddingTooManyPossibleProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {
+    const constitution = [];
+    for(let i = 0; i < 65; i++) {
+      constitution.push({
+        key: 'property_' + i,
+        permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},
+      });
+    }
+
+    await expect(collection.setTokenPropertyPermissions(alice, constitution))
+      .to.be.rejectedWith(/common\.PropertyLimitReached/);
+
+    const propertyRights = await collection.getPropertyPermissions();
+    expect(propertyRights).to.be.empty;
+  }
+
+  itSub('Prevents from adding too many possible properties (NFT)', async ({helper}) =>  {
+    await testPreventFromAddingTooManyPossibleProperties(await helper.nft.mintCollection(alice));
+  });
+
+  itSub.ifWithPallets('Prevents from adding too many possible properties (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    await testPreventFromAddingTooManyPossibleProperties(await helper.rft.mintCollection(alice));
+  });
+
+  async function testPreventAccessRightsModifiedIfConstant(collection: UniqueNFTCollection | UniqueRFTCollection) {
+    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))
+      .to.be.fulfilled;
+
+    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {collectionAdmin: true}}]))
+      .to.be.rejectedWith(/common\.NoPermission/);
+
+    const propertyRights = await collection.getPropertyPermissions(['skullduggery']);
+    expect(propertyRights).to.deep.equal([
+      {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
+    ]);
+  }
+
+  itSub('Prevents access rights to be modified if constant (NFT)', async ({helper}) =>  {
+    await testPreventAccessRightsModifiedIfConstant(await helper.nft.mintCollection(alice));
+  });
+
+  itSub.ifWithPallets('Prevents access rights to be modified if constant (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    await testPreventAccessRightsModifiedIfConstant(await helper.rft.mintCollection(alice));
+  });
+
+  async function testPreventsAddingPropertiesWithInvalidNames(collection: UniqueNFTCollection | UniqueRFTCollection) {
+    const invalidProperties = [
+      [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],
+      [{key: 'G#4', permission: {tokenOwner: true}}],
+      [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],
+    ];
+
+    for(let i = 0; i < invalidProperties.length; i++) {
+      await expect(
+        collection.setTokenPropertyPermissions(alice, invalidProperties[i]),
+        `on setting the new badly-named property #${i}`,
+      ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
+    }
+
+    await expect(
+      collection.setTokenPropertyPermissions(alice, [{key: '', permission: {}}]),
+      'on rejecting an unnamed property',
+    ).to.be.rejectedWith(/common\.EmptyPropertyKey/);
+
+    const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string
+    await expect(
+      collection.setTokenPropertyPermissions(alice, [
+        {key: correctKey, permission: {collectionAdmin: true}},
+      ]),
+      'on setting the correctly-but-still-badly-named property',
+    ).to.be.fulfilled;
+
+    const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');
+
+    const propertyRights = await collection.getPropertyPermissions(keys);
+    expect(propertyRights).to.be.deep.equal([
+      {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},
+    ]);
+  }
+
+  itSub('Prevents adding properties with invalid names (NFT)', async ({helper}) =>  {
+    await testPreventsAddingPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));
+  });
+
+  itSub.ifWithPallets('Prevents adding properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    await testPreventsAddingPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));
+  });
+});
addedjs-packages/tests/nesting/tokenProperties.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nesting/tokenProperties.seqtest.ts
@@ -0,0 +1,71 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js';
+
+describe('Integration Test: Token Properties with sudo', () => {
+  let superuser: IKeyringPair;
+  let alice: IKeyringPair; // collection owner
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      superuser = await privateKey('//Alice');
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, pieces: undefined, requiredPallets: []} as const,
+    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const,
+  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
+    before(async function() {
+      // eslint-disable-next-line require-await
+      await usingPlaygrounds(async helper => {
+        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
+      });
+    });
+
+    itSub('force_repair_item preserves valid consumed space', async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testSuite.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [
+          {
+            key: propKey,
+            permission: {mutable: true, tokenOwner: true},
+          },
+        ],
+      });
+      const token = await (
+        testSuite.pieces
+          ? collection.mintToken(alice, testSuite.pieces as any)
+          : collection.mintToken(alice)
+      );
+
+      const prop = {key: propKey, value: 'a'.repeat(4096)};
+
+      await token.setProperties(alice, [prop]);
+      const originalSpace = await token.getTokenPropertiesConsumedSpace();
+      expect(originalSpace).to.be.equal(sizeOfProperty(prop));
+
+      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true);
+      const recomputedSpace = await token.getTokenPropertiesConsumedSpace();
+      expect(recomputedSpace).to.be.equal(originalSpace);
+    });
+  }));
+});
addedjs-packages/tests/nesting/tokenProperties.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nesting/tokenProperties.test.ts
@@ -0,0 +1,816 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+describe('Integration Test: Token Properties', () => {
+  let alice: IKeyringPair; // collection owner
+  let bob: IKeyringPair; // collection admin
+  let charlie: IKeyringPair; // token owner
+
+  let permissions: {permission: any, signers: IKeyringPair[]}[];
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor);
+    });
+
+    permissions = [
+      {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},
+      {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},
+      {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},
+      {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},
+      {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
+      {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
+    ];
+  });
+
+  async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> {
+    const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, {
+      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
+    });
+    return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n];
+  }
+
+  async function testReadsYetEmptyProperties(token: UniqueNFToken | UniqueRFToken) {
+    const properties = await token.getProperties();
+    expect(properties).to.be.empty;
+
+    const tokenData = await token.getData();
+    expect(tokenData!.properties).to.be.empty;
+  }
+
+  itSub('Reads yet empty properties of a token (NFT)', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    const token = await collection.mintToken(alice);
+    await testReadsYetEmptyProperties(token);
+  });
+
+  itSub.ifWithPallets('Reads yet empty properties of a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice);
+    const token = await collection.mintToken(alice);
+    await testReadsYetEmptyProperties(token);
+  });
+
+  async function testAssignPropertiesAccordingToPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
+    await token.collection.addAdmin(alice, {Substrate: bob.address});
+    await token.transfer(alice, {Substrate: charlie.address}, pieces);
+
+    const propertyKeys: string[] = [];
+    let i = 0;
+    for(const permission of permissions) {
+      i++;
+      let j = 0;
+      for(const signer of permission.signers) {
+        j++;
+        const key = i + '_' + signer.address;
+        propertyKeys.push(key);
+
+        await expect(
+          token.setProperties(signer, [{key: key, value: 'Serotonin increase'}]),
+          `on adding property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+      }
+    }
+
+    const properties = await token.getProperties(propertyKeys);
+    const tokenData = await token.getData();
+    for(let i = 0; i < properties.length; i++) {
+      expect(properties[i].value).to.be.equal('Serotonin increase');
+      expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');
+    }
+  }
+
+  itSub('Assigns properties to a token according to permissions (NFT)', async ({helper}) =>  {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
+    await testAssignPropertiesAccordingToPermissions(token, amount);
+  });
+
+  itSub.ifWithPallets('Assigns properties to a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
+    await testAssignPropertiesAccordingToPermissions(token, amount);
+  });
+
+  async function testChangesPropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
+    await token.collection.addAdmin(alice, {Substrate: bob.address});
+    await token.transfer(alice, {Substrate: charlie.address}, pieces);
+
+    const propertyKeys: string[] = [];
+    let i = 0;
+    for(const permission of permissions) {
+      i++;
+      if(!permission.permission.mutable) continue;
+
+      let j = 0;
+      for(const signer of permission.signers) {
+        j++;
+        const key = i + '_' + signer.address;
+        propertyKeys.push(key);
+
+        await expect(
+          token.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+          `on adding property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+
+        await expect(
+          token.setProperties(signer, [{key, value: 'Serotonin stable'}]),
+          `on changing property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+      }
+    }
+
+    const properties = await token.getProperties(propertyKeys);
+    const tokenData = await token.getData();
+    for(let i = 0; i < properties.length; i++) {
+      expect(properties[i].value).to.be.equal('Serotonin stable');
+      expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');
+    }
+  }
+
+  itSub('Changes properties of a token according to permissions (NFT)', async ({helper}) =>  {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
+    await testChangesPropertiesAccordingPermission(token, amount);
+  });
+
+  itSub.ifWithPallets('Changes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
+    await testChangesPropertiesAccordingPermission(token, amount);
+  });
+
+  async function testDeletePropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
+    await token.collection.addAdmin(alice, {Substrate: bob.address});
+    await token.transfer(alice, {Substrate: charlie.address}, pieces);
+
+    const propertyKeys: string[] = [];
+    let i = 0;
+
+    for(const permission of permissions) {
+      i++;
+      if(!permission.permission.mutable) continue;
+
+      let j = 0;
+      for(const signer of permission.signers) {
+        j++;
+        const key = i + '_' + signer.address;
+        propertyKeys.push(key);
+
+        await expect(
+          token.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+          `on adding property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+
+        await expect(
+          token.deleteProperties(signer, [key]),
+          `on deleting property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+      }
+    }
+
+    expect(await token.getProperties(propertyKeys)).to.be.empty;
+    expect((await token.getData())!.properties).to.be.empty;
+  }
+
+  itSub('Deletes properties of a token according to permissions (NFT)', async ({helper}) =>  {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
+    await testDeletePropertiesAccordingPermission(token, amount);
+  });
+
+  itSub.ifWithPallets('Deletes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
+    await testDeletePropertiesAccordingPermission(token, amount);
+  });
+
+  itSub('Assigns properties to a nested token according to permissions', async ({helper}) =>  {
+    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const collectionB = await helper.nft.mintCollection(alice, {
+      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
+    });
+    const targetToken = await collectionA.mintToken(alice);
+    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
+
+    await collectionB.addAdmin(alice, {Substrate: bob.address});
+    await targetToken.transfer(alice, {Substrate: charlie.address});
+
+    const propertyKeys: string[] = [];
+    let i = 0;
+    for(const permission of permissions) {
+      i++;
+      let j = 0;
+      for(const signer of permission.signers) {
+        j++;
+        const key = i + '_' + signer.address;
+        propertyKeys.push(key);
+
+        await expect(
+          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+          `on adding property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+      }
+    }
+
+    const properties = await nestedToken.getProperties(propertyKeys);
+    const tokenData = await nestedToken.getData();
+    for(let i = 0; i < properties.length; i++) {
+      expect(properties[i].value).to.be.equal('Serotonin increase');
+      expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');
+    }
+    expect(await targetToken.getProperties()).to.be.empty;
+  });
+
+  itSub('Changes properties of a nested token according to permissions', async ({helper}) =>  {
+    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const collectionB = await helper.nft.mintCollection(alice, {
+      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
+    });
+    const targetToken = await collectionA.mintToken(alice);
+    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
+
+    await collectionB.addAdmin(alice, {Substrate: bob.address});
+    await targetToken.transfer(alice, {Substrate: charlie.address});
+
+    const propertyKeys: string[] = [];
+    let i = 0;
+    for(const permission of permissions) {
+      i++;
+      if(!permission.permission.mutable) continue;
+
+      let j = 0;
+      for(const signer of permission.signers) {
+        j++;
+        const key = i + '_' + signer.address;
+        propertyKeys.push(key);
+
+        await expect(
+          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+          `on adding property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+
+        await expect(
+          nestedToken.setProperties(signer, [{key, value: 'Serotonin stable'}]),
+          `on changing property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+      }
+    }
+
+    const properties = await nestedToken.getProperties(propertyKeys);
+    const tokenData = await nestedToken.getData();
+    for(let i = 0; i < properties.length; i++) {
+      expect(properties[i].value).to.be.equal('Serotonin stable');
+      expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');
+    }
+    expect(await targetToken.getProperties()).to.be.empty;
+  });
+
+  itSub('Deletes properties of a nested token according to permissions', async ({helper}) =>  {
+    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const collectionB = await helper.nft.mintCollection(alice, {
+      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
+        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
+    });
+    const targetToken = await collectionA.mintToken(alice);
+    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
+
+    await collectionB.addAdmin(alice, {Substrate: bob.address});
+    await targetToken.transfer(alice, {Substrate: charlie.address});
+
+    const propertyKeys: string[] = [];
+    let i = 0;
+    for(const permission of permissions) {
+      i++;
+      if(!permission.permission.mutable) continue;
+
+      let j = 0;
+      for(const signer of permission.signers) {
+        j++;
+        const key = i + '_' + signer.address;
+        propertyKeys.push(key);
+
+        await expect(
+          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
+          `on adding property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+
+        await expect(
+          nestedToken.deleteProperties(signer, [key]),
+          `on deleting property #${i} by signer #${j}`,
+        ).to.be.fulfilled;
+      }
+    }
+
+    expect(await nestedToken.getProperties(propertyKeys)).to.be.empty;
+    expect((await nestedToken.getData())!.properties).to.be.empty;
+    expect(await targetToken.getProperties()).to.be.empty;
+  });
+
+  [
+    {mode: 'nft' as const, storage: 'nonfungible' as const, pieces: undefined, requiredPallets: []} as const,
+    {mode: 'rft' as const, storage: 'refungible' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const,
+  ].map(testCase =>
+    itSub.ifWithPallets(`Allows modifying a token property multiple times with the same size (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [
+          {
+            key: propKey,
+            permission: {mutable: true, tokenOwner: true},
+          },
+        ],
+      });
+
+      const maxTokenPropertiesSize = 32768;
+
+      const propDataSize = 4096;
+
+      let propDataChar = 'a';
+      const makeNewPropData = () => {
+        propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1);
+        return `${propDataChar}`.repeat(propDataSize);
+      };
+
+      const token = await (
+        testCase.pieces
+          ? collection.mintToken(alice, testCase.pieces as any)
+          : collection.mintToken(alice)
+      );
+
+      const property = {key: propKey, value: makeNewPropData()};
+      await token.setProperties(alice, [property]);
+      const originalSpace = await token.getTokenPropertiesConsumedSpace();
+      expect(originalSpace).to.be.equal(sizeOfProperty(property));
+
+      const sameSizePropertiesPossibleNum = maxTokenPropertiesSize / propDataSize;
+
+      // It is possible to modify a property as many times as needed.
+      // It will not consume any additional space.
+      for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) {
+        await token.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);
+        const consumedSpace = await token.getTokenPropertiesConsumedSpace();
+        expect(consumedSpace).to.be.equal(originalSpace);
+      }
+    }));
+
+  [
+    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
+    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itSub.ifWithPallets(`Adding then removing a token property doesn't change the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [
+          {
+            key: propKey,
+            permission: {mutable: true, tokenOwner: true},
+          },
+        ],
+      });
+      const token = await (
+        testCase.pieces
+          ? collection.mintToken(alice, testCase.pieces as any)
+          : collection.mintToken(alice)
+      );
+      const originalSpace = await token.getTokenPropertiesConsumedSpace();
+
+      const propDataSize = 4096;
+      const propData = 'a'.repeat(propDataSize);
+
+      const property = {key: propKey, value: propData};
+      await token.setProperties(alice, [property]);
+      let consumedSpace = await token.getTokenPropertiesConsumedSpace();
+      expect(consumedSpace).to.be.equal(sizeOfProperty(property));
+
+      await token.deleteProperties(alice, [propKey]);
+      consumedSpace = await token.getTokenPropertiesConsumedSpace();
+      expect(consumedSpace).to.be.equal(originalSpace);
+    }));
+
+  [
+    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
+    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itSub.ifWithPallets(`Modifying a token property with different sizes correctly changes the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [
+          {
+            key: propKey,
+            permission: {mutable: true, tokenOwner: true},
+          },
+        ],
+      });
+      const token = await (
+        testCase.pieces
+          ? collection.mintToken(alice, testCase.pieces as any)
+          : collection.mintToken(alice)
+      );
+      const originalSpace = await token.getTokenPropertiesConsumedSpace();
+
+      const initProp = {key: propKey, value: 'a'.repeat(4096)};
+      const biggerProp = {key: propKey, value: 'b'.repeat(5000)};
+      const smallerProp = {key: propKey, value: 'c'.repeat(4000)};
+
+      let consumedSpace;
+      let expectedConsumedSpaceDiff;
+
+      await token.setProperties(alice, [initProp]);
+      consumedSpace = await token.getTokenPropertiesConsumedSpace();
+      expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace;
+      expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff);
+
+      await token.setProperties(alice, [biggerProp]);
+      consumedSpace = await token.getTokenPropertiesConsumedSpace();
+      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp);
+      expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff);
+
+      await token.setProperties(alice, [smallerProp]);
+      consumedSpace = await token.getTokenPropertiesConsumedSpace();
+      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp);
+      expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff);
+    }));
+
+  itSub('Set sponsored properties', async({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true}}]});
+
+    await collection.setSponsor(alice, alice.address);
+    await collection.confirmSponsorship(alice);
+    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
+    await collection.addToAllowList(alice, {Substrate: bob.address});
+    await collection.setLimits(alice, {sponsoredDataRateLimit: {blocks: 30}});
+
+    const token = await collection.mintToken(alice, {Substrate: bob.address});
+
+    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
+    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await token.setProperties(bob, [{key: 'k', value: 'val'}]);
+
+    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
+    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobBalanceAfter).to.be.equal(bobBalanceBefore);
+    expect(aliceBalanceBefore > aliceBalanceAfter).to.be.true;
+  });
+});
+
+describe('Negative Integration Test: Token Properties', () => {
+  let alice: IKeyringPair; // collection owner
+  let bob: IKeyringPair; // collection admin
+  let charlie: IKeyringPair; // token owner
+
+  let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      let dave: IKeyringPair;
+      [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
+
+      // todo:playgrounds probably separate these tests later
+      constitution = [
+        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},
+        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},
+        {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},
+        {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},
+        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},
+        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},
+      ];
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: [Pallets.NFT]},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itSub.ifWithPallets(`Forbids adding/deleting properties of a token if token doesn't exist (${testCase.mode.toLocaleUpperCase})`, testCase.requiredPallets, async({helper}) => {
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: constitution.slice(0, 1).map(({permission}) => ({key: '1', permission})),
+      });
+      const nonExistentToken = collection.getTokenObject(1);
+
+      await expect(
+        nonExistentToken.setProperties(alice, [{key: '1', value: 'Serotonin increase'}]),
+        'on expecting failure whilst adding a property by alice',
+      ).to.be.rejectedWith(/common\.TokenNotFound/);
+
+      await expect(
+        nonExistentToken.deleteProperties(alice, ['1']),
+        'on expecting failure whilst deleting a property by alice',
+      ).to.be.rejectedWith(/common\.TokenNotFound/);
+    }));
+
+  async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> {
+    const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, {
+      tokenPropertyPermissions: constitution.map(({permission}, i) => ({key: `${i+1}`, permission})),
+    });
+    return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n];
+  }
+
+  async function getConsumedSpace(api: any, collectionId: number, tokenId: number, mode: 'NFT' | 'RFT'): Promise<number> {
+    return (await (mode == 'NFT' ? api.query.nonfungible : api.query.refungible).tokenProperties(collectionId, tokenId)).toJSON().consumedSpace;
+  }
+
+  async function prepare(token: UniqueNFToken | UniqueRFToken, pieces: bigint): Promise<number> {
+    await token.collection.addAdmin(alice, {Substrate: bob.address});
+    await token.transfer(alice, {Substrate: charlie.address}, pieces);
+
+    let i = 0;
+    for(const passage of constitution) {
+      i++;
+      const signer = passage.signers[0];
+      await expect(
+        token.setProperties(signer, [{key: `${i}`, value: 'Serotonin increase'}]),
+        `on adding property ${i} by ${signer.address}`,
+      ).to.be.fulfilled;
+    }
+
+    const originalSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+    return originalSpace;
+  }
+
+  async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
+    const originalSpace = await prepare(token, pieces);
+
+    let i = 0;
+    for(const forbiddance of constitution) {
+      i++;
+      if(!forbiddance.permission.mutable) continue;
+
+      await expect(
+        token.setProperties(forbiddance.sinner, [{key: `${i}`, value: 'Serotonin down'}]),
+        `on failing to change property ${i} by the malefactor`,
+      ).to.be.rejectedWith(/common\.NoPermission/);
+
+      await expect(
+        token.deleteProperties(forbiddance.sinner, [`${i}`]),
+        `on failing to delete property ${i} by the malefactor`,
+      ).to.be.rejectedWith(/common\.NoPermission/);
+    }
+
+    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+    expect(consumedSpace).to.be.equal(originalSpace);
+  }
+
+  itSub('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async ({helper}) =>  {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
+    await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount);
+  });
+
+  itSub.ifWithPallets('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
+    await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount);
+  });
+
+  async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
+    const originalSpace = await prepare(token, pieces);
+
+    let i = 0;
+    for(const permission of constitution) {
+      i++;
+      if(permission.permission.mutable) continue;
+
+      await expect(
+        token.setProperties(permission.signers[0], [{key: `${i}`, value: 'Serotonin down'}]),
+        `on failing to change property ${i} by signer #0`,
+      ).to.be.rejectedWith(/common\.NoPermission/);
+
+      await expect(
+        token.deleteProperties(permission.signers[0], [i.toString()]),
+        `on failing to delete property ${i} by signer #0`,
+      ).to.be.rejectedWith(/common\.NoPermission/);
+    }
+
+    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+    expect(consumedSpace).to.be.equal(originalSpace);
+  }
+
+  itSub('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async ({helper}) =>  {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
+    await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount);
+  });
+
+  itSub.ifWithPallets('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
+    await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount);
+  });
+
+  async function testForbidsAddingPropertiesIfPropertyNotDeclared(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
+    const originalSpace = await prepare(token, pieces);
+
+    await expect(
+      token.setProperties(alice, [{key: 'non-existent', value: 'I exist!'}]),
+      'on failing to add a previously non-existent property',
+    ).to.be.rejectedWith(/common\.NoPermission/);
+
+    await expect(
+      token.collection.setTokenPropertyPermissions(alice, [{key: 'now-existent', permission: {}}]),
+      'on setting a new non-permitted property',
+    ).to.be.fulfilled;
+
+    await expect(
+      token.setProperties(alice, [{key: 'now-existent', value: 'I exist!'}]),
+      'on failing to add a property forbidden by the \'None\' permission',
+    ).to.be.rejectedWith(/common\.NoPermission/);
+
+    expect(await token.getProperties(['non-existent', 'now-existent'])).to.be.empty;
+
+    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+    expect(consumedSpace).to.be.equal(originalSpace);
+  }
+
+  itSub('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async ({helper}) =>  {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
+    await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount);
+  });
+
+  itSub.ifWithPallets('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
+    await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount);
+  });
+
+  async function testForbidsAddingTooLargeProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
+    const originalSpace = await prepare(token, pieces);
+
+    await expect(
+      token.collection.setTokenPropertyPermissions(alice, [
+        {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}},
+        {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},
+      ]),
+      'on setting new permissions for properties',
+    ).to.be.fulfilled;
+
+    // Mute the general tx parsing error
+    {
+      console.error = () => {};
+      await expect(token.setProperties(alice, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]))
+        .to.be.rejected;
+    }
+
+    await expect(token.setProperties(alice, [
+      {key: 'a_holy_book', value: 'word '.repeat(3277)},
+      {key: 'young_years', value: 'neverending'.repeat(1490)},
+    ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);
+
+    expect(await token.getProperties(['a_holy_book', 'young_years'])).to.be.empty;
+    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
+    expect(consumedSpace).to.be.equal(originalSpace);
+  }
+
+  itSub('Forbids adding too large properties to a token (NFT)', async ({helper}) =>  {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
+    await testForbidsAddingTooLargeProperties(token, amount);
+  });
+
+  itSub.ifWithPallets('Forbids adding too large properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
+    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
+    await testForbidsAddingTooLargeProperties(token, amount);
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itSub.ifWithPallets(`Forbids adding too many propeties to a token (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+      const collection = await helper[testCase.mode].mintCollection(alice);
+      const maxPropertiesPerItem = 64;
+
+      for(let i = 0; i < maxPropertiesPerItem; i++) {
+        await collection.setTokenPropertyPermissions(alice, [{
+          key: `${i+1}`,
+          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
+        }]);
+      }
+
+      await expect(collection.setTokenPropertyPermissions(alice, [{
+        key: `${maxPropertiesPerItem}-th`,
+        permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
+      }])).to.be.rejectedWith(/common\.PropertyLimitReached/);
+    }));
+
+  [
+    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
+    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itSub.ifWithPallets(`Forbids force_repair_item from non-sudo (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
+      const propKey = 'tok-prop';
+
+      const collection = await helper[testCase.mode].mintCollection(alice, {
+        tokenPropertyPermissions: [
+          {
+            key: propKey,
+            permission: {mutable: true, tokenOwner: true},
+          },
+        ],
+      });
+      const token = await (
+        testCase.pieces
+          ? collection.mintToken(alice, testCase.pieces as any)
+          : collection.mintToken(alice)
+      );
+
+      const propDataSize = 4096;
+      const propData = 'a'.repeat(propDataSize);
+      await token.setProperties(alice, [{key: propKey, value: propData}]);
+
+      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true))
+        .to.be.rejectedWith(/BadOrigin/);
+    }));
+});
+
+describe('ReFungible token properties permissions tests', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  async function prepare(helper: UniqueHelper): Promise<UniqueRFToken> {
+    const collection = await helper.rft.mintCollection(alice);
+    const token = await collection.mintToken(alice, 100n);
+
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable: true, tokenOwner: true}}]);
+
+    return token;
+  }
+
+  itSub('Forbids adding token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {
+    const token = await prepare(helper);
+
+    await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+    await expect(token.setProperties(alice, [
+      {key: 'fractals', value: 'multiverse'},
+    ])).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Forbids mutating token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {
+    const token = await prepare(helper);
+
+    await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, tokenOwner: true}}]))
+      .to.be.fulfilled;
+
+    await expect(token.setProperties(alice, [
+      {key: 'fractals', value: 'multiverse'},
+    ])).to.be.fulfilled;
+
+    await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+    await expect(token.setProperties(alice, [
+      {key: 'fractals', value: 'want to rule the world'},
+    ])).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Forbids deleting token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {
+    const token = await prepare(helper);
+
+    await expect(token.setProperties(alice, [
+      {key: 'fractals', value: 'one headline - why believe it'},
+    ])).to.be.fulfilled;
+
+    await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+    await expect(token.deleteProperties(alice, ['fractals'])).
+      to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Allows token property mutation with collectionOwner==true when admin doesn\'t have all pieces', async ({helper}) =>  {
+    const token = await prepare(helper);
+
+    await token.transfer(alice, {Substrate: charlie.address}, 33n);
+
+    await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, collectionAdmin: true}}]))
+      .to.be.fulfilled;
+
+    await expect(token.setProperties(alice, [
+      {key: 'fractals', value: 'multiverse'},
+    ])).to.be.fulfilled;
+  });
+});
addedjs-packages/tests/nesting/unnest.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nesting/unnest.test.ts
@@ -0,0 +1,325 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+describe('Integration Test: Unnesting', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 50n, 50n], donor);
+    });
+  });
+
+  itSub('NFT: allows the owner to successfully unnest a token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    // Create a nested token
+    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+
+    // Unnest
+    await expect(nestedToken.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}), 'while unnesting').to.be.fulfilled;
+    expect(await nestedToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
+
+    // Nest and burn
+    await nestedToken.nest(alice, targetToken);
+    await expect(nestedToken.burnFrom(alice, targetToken.nestingAccount()), 'while burning').to.be.fulfilled;
+    await expect(nestedToken.getOwner()).to.be.rejected;
+  });
+
+  itSub('NativeFungible: allows the owner to successfully unnest a token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    const collectionFT = helper.ft.getCollectionObject(0);
+
+    // Nest
+    await collectionFT.transfer(alice, targetToken.nestingAccount(), 10n);
+    // Unnest
+    await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
+
+    expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
+  });
+
+  itSub('Fungible: allows the owner to successfully unnest a token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    const collectionFT = await helper.ft.mintCollection(alice);
+
+    // Nest and unnest
+    await collectionFT.mint(alice, 10n, targetToken.nestingAccount());
+    await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
+    expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(9n);
+    expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
+
+    // Nest and burn
+    await collectionFT.transfer(alice, targetToken.nestingAccount(), 5n);
+    await expect(collectionFT.burnTokensFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled;
+    expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(4n);
+    expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
+    expect(await targetToken.getChildren()).to.be.length(0);
+  });
+
+  itSub.ifWithPallets('ReFungible: allows the owner to successfully unnest a token', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    const collectionRFT = await helper.rft.mintCollection(alice);
+
+    // Nest and unnest
+    const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount());
+    await expect(token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n);
+    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
+
+    // Nest and burn
+    await token.transfer(alice, targetToken.nestingAccount(), 5n);
+    await expect(token.burnFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(4n);
+    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
+    expect(await targetToken.getChildren()).to.be.length(0);
+  });
+
+  async function checkNestedAmountState({
+    expectedBalance,
+    childrenShouldPresent,
+    nested,
+    targetNft,
+  }: {
+    expectedBalance: bigint,
+    childrenShouldPresent: boolean,
+    nested: UniqueFTCollection | UniqueRFToken,
+    targetNft: UniqueNFToken,
+  }) {
+    const balance = await nested.getBalance(targetNft.nestingAccount());
+    expect(balance).to.be.equal(expectedBalance);
+
+    const children = await targetNft.getChildren();
+
+    if(childrenShouldPresent) {
+      expect(children[0]).to.be.deep.equal({
+        collectionId: nested.collectionId,
+        tokenId: (nested instanceof UniqueFTCollection) ? 0 : nested.tokenId,
+      });
+    } else {
+      expect(children.length).to.be.equal(0);
+    }
+  }
+
+  function ownerOrAdminUnnestCases(modes: ('ft' | 'nft' | 'rft')[]): {
+    mode: 'ft' | 'nft' | 'rft',
+    sender: string,
+    op: 'transfer' | 'burn',
+    requiredPallets: Pallets[],
+  }[] {
+    const senders = ['owner', 'admin'];
+    const ops = ['transfer', 'burn'];
+
+    const cases = [];
+    for(const mode of modes) {
+      const requiredPallets = (mode === 'rft')
+        ? [Pallets.ReFungible]
+        : [];
+
+      for(const sender of senders) {
+        for(const op of ops) {
+          cases.push({
+            mode: mode as 'ft' | 'nft' | 'rft',
+            sender,
+            op: op as 'transfer' | 'burn',
+            requiredPallets,
+          });
+        }
+      }
+    }
+
+    return cases;
+  }
+
+  ownerOrAdminUnnestCases(['ft', 'rft']).map(testCase =>
+    itSub.ifWithPallets(`[${testCase.mode}]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, testCase.requiredPallets, async({helper}) => {
+      const owner = alice;
+      const admin = bob;
+
+      const unnester = (testCase.sender === 'owner')
+        ? owner
+        : admin;
+
+      const collectionNFT = await helper.nft.mintCollection(owner);
+      await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}});
+
+      const collectionNested = await helper[testCase.mode as 'ft' | 'rft'].mintCollection(owner, {
+        limits: {
+          ownerCanTransfer: true,
+        },
+      });
+      await collectionNested.addAdmin(owner, {Substrate: admin.address});
+
+      const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address});
+
+      let nested: UniqueFTCollection | UniqueRFToken;
+      const totalAmount = 5n;
+      const firstUnnestAmount = 2n;
+      const restUnnestAmount = totalAmount - firstUnnestAmount;
+
+      if(collectionNested instanceof UniqueFTCollection) {
+        await collectionNested.mint(owner, totalAmount, {Substrate: charlie.address});
+        nested = collectionNested;
+      } else {
+        nested = await collectionNested.mintToken(owner, totalAmount, {Substrate: charlie.address});
+      }
+
+      // transfer/burn `amount` of nested assets by `unnester`.
+      const doOperationAndCheck = async ({
+        amount,
+        shouldBeNestedAfterOp,
+      }: {
+        amount: bigint,
+        shouldBeNestedAfterOp: boolean,
+      }) => {
+        const nestedBalanceBeforeOp = await nested.getBalance(targetNft.nestingAccount());
+
+        if(testCase.op === 'transfer') {
+          const bobBalanceBeforeOp = await nested.getBalance({Substrate: bob.address});
+
+          await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address}, amount);
+          expect(await nested.getBalance({Substrate: bob.address})).to.be.equal(bobBalanceBeforeOp + amount);
+        } else {
+          if(nested instanceof UniqueFTCollection) {
+            await nested.burnTokensFrom(unnester, targetNft.nestingAccount(), amount);
+          } else {
+            await nested.burnFrom(unnester, targetNft.nestingAccount(), amount);
+          }
+        }
+
+        await checkNestedAmountState({
+          expectedBalance: nestedBalanceBeforeOp - amount,
+          childrenShouldPresent: shouldBeNestedAfterOp,
+          nested,
+          targetNft,
+        });
+      };
+
+      // Initial setup: nest (fungibles/rft parts).
+      // Check NFT's balance of nested assets and NFT's children.
+      await nested.transfer(charlie, targetNft.nestingAccount(), totalAmount);
+      await checkNestedAmountState({
+        expectedBalance: totalAmount,
+        childrenShouldPresent: true,
+        nested,
+        targetNft,
+      });
+
+      // Transfer/burn only a part of nested assets.
+      // Check that NFT's balance of the nested assets correctly decreased and NFT's children are not changed.
+      await doOperationAndCheck({
+        amount: firstUnnestAmount,
+        shouldBeNestedAfterOp: true,
+      });
+
+      // Transfer/burn all remaining nested assets.
+      // Check that NFT's balance of the nested assets is 0 and NFT has no more children.
+      await doOperationAndCheck({
+        amount: restUnnestAmount,
+        shouldBeNestedAfterOp: false,
+      });
+    }));
+
+  ownerOrAdminUnnestCases(['nft']).map(testCase =>
+    itSub(`[nft]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, async ({helper}) => {
+      const owner = alice;
+      const admin = bob;
+
+      const unnester = (testCase.sender === 'owner')
+        ? owner
+        : admin;
+
+      const collectionNFT = await helper.nft.mintCollection(owner);
+      await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}});
+
+      const collectionNested = await helper.nft.mintCollection(owner, {
+        limits: {
+          ownerCanTransfer: true,
+        },
+      });
+      await collectionNested.addAdmin(owner, {Substrate: admin.address});
+
+      const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address});
+      const nested = await collectionNested.mintToken(owner, {Substrate: charlie.address});
+
+      await nested.transfer(charlie, targetNft.nestingAccount());
+      expect(await targetNft.getChildren()).to.be.deep.equal([{
+        collectionId: nested.collectionId,
+        tokenId: nested.tokenId,
+      }]);
+
+      if(testCase.op === 'transfer') {
+        await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address});
+      } else {
+        await nested.burnFrom(unnester, targetNft.nestingAccount());
+      }
+
+      expect((await targetNft.getChildren()).length).to.be.equal(0);
+    }));
+});
+
+describe('Negative Test: Unnesting', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
+    });
+  });
+
+  itSub('Disallows a non-owner to unnest/burn a token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    // Create a nested token
+    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+
+    // Try to unnest
+    await expect(nestedToken.unnest(bob, targetToken, {Substrate: alice.address})).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+
+    // Try to burn
+    await expect(nestedToken.burnFrom(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+  });
+
+  // todo another test for creating excessive depth matryoshka with Ethereum?
+
+  // Recursive nesting
+  itSub('Prevents Ouroboros creation', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    // Fail to create a nested token ouroboros
+    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+    await expect(targetToken.nest(alice, nestedToken)).to.be.rejectedWith(/^structure\.OuroborosDetected$/);
+  });
+});
addedjs-packages/tests/nextSponsoring.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/nextSponsoring.test.ts
@@ -0,0 +1,97 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js';
+
+const SPONSORING_TIMEOUT = 5;
+
+describe('Integration Test getNextSponsored(collection_id, owner, item_id):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
+    });
+  });
+
+  itSub('NFT', async ({helper}) => {
+    // Non-existing collection
+    expect(await helper.collection.getTokenNextSponsored(0, 0, {Substrate: alice.address})).to.be.null;
+
+    const collection = await helper.nft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice);
+
+    // Check with Disabled sponsoring state
+    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null;
+
+    // Check with Unconfirmed sponsoring state
+    await collection.setSponsor(alice, bob.address);
+    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null;
+
+    // Check with Confirmed sponsoring state
+    await collection.confirmSponsorship(bob);
+    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0);
+
+    // Check after transfer
+    await token.transfer(alice, {Substrate: bob.address});
+    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT);
+
+    // Non-existing token
+    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null;
+  });
+
+  itSub('Fungible', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {});
+    await collection.mint(alice, 10n);
+
+    // Check with Disabled sponsoring state
+    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null;
+
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+
+    // Check with Confirmed sponsoring state
+    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.equal(0);
+
+    // Check after transfer
+    await collection.transfer(alice, {Substrate: bob.address});
+    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT);
+  });
+
+  itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    const token = await collection.mintToken(alice, 10n);
+
+    // Check with Disabled sponsoring state
+    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null;
+
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+
+    // Check with Confirmed sponsoring state
+    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0);
+
+    // Check after transfer
+    await token.transfer(alice, {Substrate: bob.address});
+    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT);
+
+    // Non-existing token
+    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null;
+  });
+});
modifiedjs-packages/tests/package.jsondiffbeforeafterboth
--- a/js-packages/tests/package.json
+++ b/js-packages/tests/package.json
@@ -20,13 +20,12 @@
         "typescript": "^5.1.6"
     },
     "scripts": {
-        "setup": "ts-node --esm ./src/util/globalSetup.ts",
-        "setIdentities": "ts-node --esm ./src/util/identitySetter.ts",
-        "checkRelayIdentities": "ts-node --esm ./src/util/relayIdentitiesChecker.ts",
-        "frankenstein": "ts-node --esm ./src/util/frankenstein.ts",
+        "setup": "ts-node --esm ./util/globalSetup.ts",
+        "setIdentities": "ts-node --esm ./util/identitySetter.ts",
+        "checkRelayIdentities": "ts-node --esm ./util/relayIdentitiesChecker.ts",
         "_test": "yarn setup && mocha --timeout 9999999 --loader=ts-node/esm.mjs",
         "_testParallel": "yarn setup && mocha --timeout 9999999 --parallel --loader=ts-node/esm.mjs",
-        "test": "yarn _test './src/**/*.*test.ts'",
+        "test": "yarn _test './**/*.*test.ts'",
         "load": "yarn _test './**/*.load.ts'"
     }
 }
addedjs-packages/tests/pallet-presence.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/pallet-presence.test.ts
@@ -0,0 +1,130 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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';
+
+// Pallets that must always be present
+const requiredPallets = [
+  'balances',
+  'balancesadapter',
+  'common',
+  'timestamp',
+  'transactionpayment',
+  'treasury',
+  'statetriemigration',
+  'structure',
+  'system',
+  'utility',
+  'vesting',
+  'parachainsystem',
+  'parachaininfo',
+  'evm',
+  'evmcodersubstrate',
+  'evmcontracthelpers',
+  'evmmigration',
+  'evmtransactionpayment',
+  'ethereum',
+  'fungible',
+  'xcmpqueue',
+  'polkadotxcm',
+  'cumulusxcm',
+  'dmpqueue',
+  'inflation',
+  'unique',
+  'nonfungible',
+  'charging',
+  'configuration',
+  'tokens',
+  'xtokens',
+  'maintenance',
+];
+
+// Pallets that depend on consensus and governance configuration
+const consensusPallets = [
+  'sudo',
+  'aura',
+  'auraext',
+];
+
+describe('Pallet presence', () => {
+  before(async () => {
+    await usingPlaygrounds(async helper => {
+      const runtimeVersion = await helper.callRpc('api.rpc.state.getRuntimeVersion', []);
+      const chain = runtimeVersion.specName;
+
+      const refungible = 'refungible';
+      const foreignAssets = 'foreignassets';
+      const appPromotion = 'apppromotion';
+      const collatorSelection = ['authorship', 'session', 'collatorselection'];
+      const preimage = ['preimage'];
+      const governance = [
+        'council',
+        'councilmembership',
+        'democracy',
+        'fellowshipcollective',
+        'fellowshipreferenda',
+        'origins',
+        'scheduler',
+        'technicalcommittee',
+        'technicalcommitteemembership',
+        'identity',
+      ];
+      const testUtils = 'testutils';
+
+      if(chain.eq('opal')) {
+        requiredPallets.push(
+          refungible,
+          foreignAssets,
+          appPromotion,
+          testUtils,
+          ...collatorSelection,
+          ...preimage,
+          ...governance,
+        );
+      } else if(chain.eq('quartz') || chain.eq('sapphire')) {
+        requiredPallets.push(
+          refungible,
+          appPromotion,
+          foreignAssets,
+          ...collatorSelection,
+          ...preimage,
+          ...governance,
+        );
+      } else if(chain.eq('unique')) {
+        // Insert Unique additional pallets here
+        requiredPallets.push(
+          refungible,
+          foreignAssets,
+          appPromotion,
+          ...preimage,
+          ...governance,
+        );
+      }
+    });
+  });
+
+  itSub('Required pallets are present', ({helper}) => {
+    expect(helper.fetchAllPalletNames()).to.contain.members([...requiredPallets].sort());
+  });
+
+  itSub('Governance and consensus pallets are present', ({helper}) => {
+    expect(helper.fetchAllPalletNames()).to.contain.members([...consensusPallets].sort());
+  });
+
+  itSub('No extra pallets are included', ({helper}) => {
+    expect(helper.fetchAllPalletNames().sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort());
+  });
+});
addedjs-packages/tests/performance.seq.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/performance.seq.test.ts
@@ -0,0 +1,125 @@
+// Copyright 2019-2023 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {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';
+
+describe('Performace tests', () => {
+  let alice: IKeyringPair;
+  const MAX_TOKENS_TO_MINT = 120;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([100_000n], donor);
+    });
+  });
+
+  itSub('NFT tokens minting', async ({helper}) => {
+    const propertyKey = 'prop-a';
+    const collection = await helper.nft.mintCollection(alice, {
+      name: 'test properties',
+      description: 'test properties collection',
+      tokenPrefix: 'TPC',
+      tokenPropertyPermissions: [
+        {key: propertyKey, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
+      ],
+    });
+
+    const step = 1_000;
+    const sizeOfKey = sizeOfEncodedStr(propertyKey);
+    let currentSize = step;
+    let startCount = 0;
+    let minterFunc = tryMintUnsafeRPC;
+    try {
+      startCount = await tryMintUnsafeRPC(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address});
+    }
+    catch (e) {
+      startCount = await tryMintExplicit(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address});
+      minterFunc = tryMintExplicit;
+    }
+
+    expect(startCount).to.be.equal(MAX_TOKENS_TO_MINT);
+
+    while(currentSize <= 32_000) {
+      const property = {key: propertyKey, value: 'A'.repeat(currentSize - sizeOfKey - sizeOfInt(currentSize))};
+      const tokens = await minterFunc(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}, property);
+      expect(tokens).to.be.equal(MAX_TOKENS_TO_MINT);
+
+      currentSize += step;
+      await helper.wait.newBlocks(2);
+    }
+  });
+});
+
+
+const dryRun = async (api: ApiPromise, signer: IKeyringPair, tx: any) => {
+  const signed = await tx.signAsync(signer);
+  const dryRun = await api.rpc.system.dryRun(signed.toHex());
+  return dryRun.isOk && dryRun.asOk.isOk;
+};
+
+const getTokens = (tokensCount: number, owner: ICrossAccountId, property?: IProperty) => (new Array(tokensCount)).fill(0).map(() => {
+  const token = {owner} as {owner: ICrossAccountId, properties?: IProperty[]};
+  if(property) token.properties = [property];
+  return token;
+});
+
+const tryMintUnsafeRPC = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise<number> => {
+  if(tokensCount < 10) console.log('try mint', tokensCount, 'tokens');
+  const tokens = getTokens(tokensCount, owner, property);
+  const tx = helper.constructApiCall('api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]);
+  if(!(await dryRun(helper.getApi(), signer, tx))) {
+    if(tokensCount < 2) return 0;
+    return await tryMintUnsafeRPC(helper, signer, tokensCount - 1, collectionId, owner, property);
+  }
+  await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]);
+  return tokensCount;
+};
+
+const tryMintExplicit = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise<number> => {
+  const tokens = getTokens(tokensCount, owner, property);
+  try {
+    await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]);
+  }
+  catch (e) {
+    if(tokensCount < 2) return 0;
+    return await tryMintExplicit(helper, signer, tokensCount - 1, collectionId, owner, property);
+  }
+  return tokensCount;
+};
+
+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();
+function sizeOfEncodedStr(v: string) {
+  const encoded = UTF8_ENCODER.encode(v);
+  return sizeOfInt(encoded.length) + encoded.length;
+}
addedjs-packages/tests/refungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/refungible.test.ts
@@ -0,0 +1,132 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/index.js';
+
+const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n;
+
+describe('integration test: Refungible functionality:', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
+    });
+  });
+
+  itSub('Create refungible collection and token', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    const itemCountBefore = await collection.getLastTokenId();
+    const token = await collection.mintToken(alice, 100n);
+
+    const itemCountAfter = await collection.getLastTokenId();
+
+    // What to expect
+    expect(token?.tokenId).to.be.gte(itemCountBefore);
+    expect(itemCountAfter).to.be.equal(itemCountBefore + 1);
+    expect(itemCountAfter.toString()).to.be.equal(token?.tokenId.toString());
+  });
+
+  itSub('Checking RPC methods when interacting with maximum allowed values (MAX_REFUNGIBLE_PIECES)', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    const token = await collection.mintToken(alice, MAX_REFUNGIBLE_PIECES);
+
+    expect(await collection.getTokenBalance(token.tokenId, {Substrate: alice.address})).to.be.equal(MAX_REFUNGIBLE_PIECES);
+
+    await collection.transferToken(alice, token.tokenId, {Substrate: bob.address}, MAX_REFUNGIBLE_PIECES);
+    expect(await collection.getTokenBalance(token.tokenId, {Substrate: bob.address})).to.be.equal(MAX_REFUNGIBLE_PIECES);
+    expect(await token.getTotalPieces()).to.be.equal(MAX_REFUNGIBLE_PIECES);
+
+    await expect(collection.mintToken(alice, MAX_REFUNGIBLE_PIECES + 1n))
+      .to.eventually.be.rejectedWith(/refungible\.WrongRefungiblePieces/);
+  });
+
+  itSub('RPC method tokenOwners for refungible collection and token', async ({helper}) => {
+    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address}));
+
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+
+    const token = await collection.mintToken(alice, 10_000n);
+
+    await token.transfer(alice, {Substrate: bob.address}, 1000n);
+    await token.transfer(alice, ethAcc, 900n);
+
+    for(let i = 0; i < 7; i++) {
+      await token.transfer(alice, facelessCrowd[i], 50n * BigInt(i + 1));
+    }
+
+    const owners = await token.getTop10Owners();
+
+    // What to expect
+    expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]);
+    expect(owners.length).to.be.equal(10);
+
+    const [eleven] = await helper.arrange.createAccounts([0n], donor);
+    expect(await token.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true;
+    expect((await token.getTop10Owners()).length).to.be.equal(10);
+  });
+
+  itSub('Create multiple tokens', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    // TODO: fix mintMultipleTokens
+    // await collection.mintMultipleTokens(alice, [
+    //   {owner: {Substrate: alice.address}, pieces: 1n},
+    //   {owner: {Substrate: alice.address}, pieces: 2n},
+    //   {owner: {Substrate: alice.address}, pieces: 100n},
+    // ]);
+    await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, [
+      {pieces: 1n},
+      {pieces: 2n},
+      {pieces: 100n},
+    ]);
+    const lastTokenId = await collection.getLastTokenId();
+    expect(lastTokenId).to.be.equal(3);
+    expect(await collection.getTokenBalance(lastTokenId, {Substrate: alice.address})).to.be.equal(100n);
+  });
+
+  itSub('Set allowance for token', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
+
+    expect(await token.approve(alice, {Substrate: bob.address}, 60n)).to.be.true;
+    expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n);
+
+    expect(await token.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(80n);
+    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(20n);
+    expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n);
+  });
+
+  itSub('Create new collection with properties', async ({helper}) => {
+    const properties = [{key: 'key1', value: 'val1'}];
+    const tokenPropertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}];
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties, tokenPropertyPermissions});
+    const info = await collection.getData();
+    expect(info?.raw.properties).to.be.deep.equal(properties);
+    expect(info?.raw.tokenPropertyPermissions).to.be.deep.equal(tokenPropertyPermissions);
+  });
+});
addedjs-packages/tests/removeCollectionAdmin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/removeCollectionAdmin.test.ts
@@ -0,0 +1,113 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+
+describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
+    });
+  });
+
+  itSub('Remove collection admin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-1', tokenPrefix: 'RCA'});
+    const collectionInfo = await collection.getData();
+    expect(collectionInfo?.raw.owner.toString()).to.be.deep.eq(alice.address);
+    // first - add collection admin Bob
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    const adminListAfterAddAdmin = await collection.getAdmins();
+    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
+
+    // then remove bob from admins of collection
+    await collection.removeAdmin(alice, {Substrate: bob.address});
+
+    const adminListAfterRemoveAdmin = await collection.getAdmins();
+    expect(adminListAfterRemoveAdmin).not.to.be.deep.contains({Substrate: bob.address});
+  });
+
+  itSub('Remove admin from collection that has no admins', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-2', tokenPrefix: 'RCA'});
+
+    const adminListBeforeAddAdmin = await collection.getAdmins();
+    expect(adminListBeforeAddAdmin).to.have.lengthOf(0);
+
+    await expect(collection.removeAdmin(alice, {Substrate: alice.address})).to.be.rejectedWith('common.UserIsNotCollectionAdmin');
+  });
+});
+
+describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('Can\'t remove collection admin from not existing collection', async ({helper}) => {
+    const collectionId = NON_EXISTENT_COLLECTION_ID;
+
+    await expect(helper.collection.removeAdmin(alice, collectionId, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('Can\'t remove collection admin from deleted collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-2', tokenPrefix: 'RCA'});
+
+    expect(await collection.burn(alice)).to.be.true;
+
+    await expect(helper.collection.removeAdmin(alice, collection.collectionId, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('Regular user can\'t remove collection admin', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-3', tokenPrefix: 'RCA'});
+
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    await expect(collection.removeAdmin(charlie, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('Admin can\'t remove collection admin.', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-4', tokenPrefix: 'RCA'});
+
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await collection.addAdmin(alice, {Substrate: charlie.address});
+
+    const adminListAfterAddAdmin = await collection.getAdmins();
+    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
+    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: charlie.address});
+
+    await expect(collection.removeAdmin(charlie, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.NoPermission/);
+
+    const adminListAfterRemoveAdmin = await collection.getAdmins();
+    expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: bob.address});
+    expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: charlie.address});
+  });
+});
addedjs-packages/tests/removeCollectionSponsor.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/removeCollectionSponsor.test.ts
@@ -0,0 +1,126 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+
+describe('integration test: ext. removeCollectionSponsor():', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('Removing NFT collection sponsor stops sponsorship', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-1', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    await collection.removeSponsor(alice);
+
+    // Find unused address
+    const [zeroBalance] = await helper.arrange.createAccounts([0n], donor);
+
+    // Mint token for unused address
+    const token = await collection.mintToken(alice, {Substrate: zeroBalance.address});
+
+    // Transfer this tokens from unused address to Alice - should fail
+    const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    await expect(token.transfer(zeroBalance, {Substrate: alice.address}))
+      .to.be.rejectedWith('Inability to pay some fees');
+    const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
+  });
+
+  itSub('Remove a sponsor after it was already removed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-2', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
+    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
+  });
+
+  itSub('Remove sponsor in a collection that never had the sponsor set', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-3', tokenPrefix: 'RCS'});
+    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
+  });
+
+  itSub('Remove sponsor for a collection that had the sponsor set, but not confirmed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-4', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
+  });
+
+  itSub('Remove a sponsor from a collection with collection admin permissions', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-1', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.addAdmin(alice, {Substrate: charlie.address});
+    await expect(collection.removeSponsor(charlie)).not.to.be.rejected;
+  });
+});
+
+describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('(!negative test!) Remove sponsor for a collection that never existed', async ({helper}) => {
+    const collectionId = NON_EXISTENT_COLLECTION_ID;
+    await expect(helper.collection.removeSponsor(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('(!negative test!) Remove sponsor for a collection by regular user', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-2', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await expect(collection.removeSponsor(charlie)).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('(!negative test!) Remove sponsor in a destroyed collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-3', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.burn(alice);
+    await expect(collection.removeSponsor(alice)).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('Set - remove - confirm: fails', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-4', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.removeSponsor(alice);
+    await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
+  });
+
+  itSub('Set - confirm - remove - confirm: Sponsor cannot come back', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-5', tokenPrefix: 'RCS'});
+    await collection.setSponsor(alice, bob.address);
+    await collection.confirmSponsorship(bob);
+    await collection.removeSponsor(alice);
+    await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
+  });
+});
addedjs-packages/tests/rpc.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/rpc.test.ts
@@ -0,0 +1,68 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {usingPlaygrounds, itSub, expect} from './util/index.js';
+import {ICrossAccountId} from '@unique/playgrounds/types.js';
+
+describe('integration test: RPC methods', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
+    });
+  });
+
+  itSub('returns None for fungible collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'RPC-1', tokenPrefix: 'RPC'});
+    const owner = (await helper.callRpc('api.rpc.unique.tokenOwner', [collection.collectionId, 0])).toJSON() as any;
+    expect(owner).to.be.null;
+  });
+
+  itSub('RPC method tokenOwners for fungible collection and token', async ({helper}) => {
+    // Set-up a few token owners of all stripes
+    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
+    const facelessCrowd = (await helper.arrange.createAccounts([0n, 0n, 0n, 0n, 0n, 0n, 0n], donor))
+      .map(i => ({Substrate: i.address}));
+
+    const collection = await helper.ft.mintCollection(alice, {name: 'RPC-2', tokenPrefix: 'RPC'});
+    // mint some maximum (u128) amounts of tokens possible
+    await collection.mint(alice, (1n << 128n) - 1n);
+
+    await collection.transfer(alice, {Substrate: bob.address}, 1000n);
+    await collection.transfer(alice, ethAcc, 900n);
+
+    for(let i = 0; i < facelessCrowd.length; i++) {
+      await collection.transfer(alice, facelessCrowd[i], 1n);
+    }
+    // Set-up over
+
+    const owners = await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0]);
+    const ids = owners.toHuman() as ICrossAccountId[];
+
+    expect(ids).to.have.deep.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]);
+    expect(owners.length == 10).to.be.true;
+
+    // Make sure only 10 results are returned with this RPC
+    const [eleven] = await helper.arrange.createAccounts([0n], donor);
+    expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true;
+    expect((await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0])).length).to.be.equal(10);
+  });
+});
addedjs-packages/tests/scheduler.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/scheduler.seqtest.ts
@@ -0,0 +1,796 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+
+describe('Scheduling token and balance transfers', () => {
+  let superuser: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]);
+
+      superuser = await privateKey('//Alice');
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+
+      await helper.testUtils.enable(Pallets.TestUtils);
+    });
+  });
+
+  beforeEach(async () => {
+    await usingPlaygrounds(async (helper) => {
+      await helper.wait.noScheduledTasks();
+    });
+  });
+
+  itSched('Can delay a transfer of an owned token', async (scheduleKind, {helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+    const blocksBeforeExecution = 4;
+    await helper.scheduler.scheduleAfter(blocksBeforeExecution, {scheduledId})
+      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
+    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSched('Can transfer funds periodically', async (scheduleKind, {helper}) => {
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+    const waitForBlocks = 1;
+
+    const amount = 1n * helper.balance.getOneTokenNominal();
+    const periodic = {
+      period: 2,
+      repetitions: 2,
+    };
+
+    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic})
+      .balance.transferToSubstrate(alice, bob.address, amount);
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
+    expect(bobsBalanceAfterFirst)
+      .to.be.equal(
+        bobsBalanceBefore + 1n * amount,
+        '#1 Balance of the recipient should be increased by 1 * amount',
+      );
+
+    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
+
+    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
+    expect(bobsBalanceAfterSecond)
+      .to.be.equal(
+        bobsBalanceBefore + 2n * amount,
+        '#2 Balance of the recipient should be increased by 2 * amount',
+      );
+  });
+
+  itSub('Can cancel a scheduled operation which has not yet taken effect', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+
+    const scheduledId = helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.scheduler.cancelScheduled(alice, scheduledId);
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub('Can cancel a periodic operation (transfer of funds)', async ({helper}) => {
+    const waitForBlocks = 1;
+    const periodic = {
+      period: 3,
+      repetitions: 2,
+    };
+
+    const scheduledId = helper.arrange.makeScheduledId();
+
+    const amount = 1n * helper.balance.getOneTokenNominal();
+
+    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic})
+      .balance.transferToSubstrate(alice, bob.address, amount);
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
+
+    expect(bobsBalanceAfterFirst)
+      .to.be.equal(
+        bobsBalanceBefore + 1n * amount,
+        '#1 Balance of the recipient should be increased by 1 * amount',
+      );
+
+    await helper.scheduler.cancelScheduled(alice, scheduledId);
+    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
+
+    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
+    expect(bobsBalanceAfterSecond)
+      .to.be.equal(
+        bobsBalanceAfterFirst,
+        '#2 Balance of the recipient should not be changed',
+      );
+  });
+
+  itSched('scheduler will not insert more tasks than allowed', async (scheduleKind, {helper}) => {
+    const maxScheduledPerBlock = 50;
+    let fillScheduledIds = new Array(maxScheduledPerBlock);
+    let extraScheduledId = undefined;
+
+    if(scheduleKind == 'named') {
+      const scheduledIds = helper.arrange.makeScheduledIds(maxScheduledPerBlock + 1);
+      fillScheduledIds = scheduledIds.slice(0, maxScheduledPerBlock);
+      extraScheduledId = scheduledIds[maxScheduledPerBlock];
+    }
+
+    // Since the dev node has Instant Seal,
+    // we need a larger gap between the current block and the target one.
+    //
+    // We will schedule `maxScheduledPerBlock` transaction into the target block,
+    // so we need at least `maxScheduledPerBlock`-wide gap.
+    // We add some additional blocks to this gap to mitigate possible PolkadotJS delays.
+    const waitForBlocks = await helper.arrange.isDevNode() ? maxScheduledPerBlock + 5 : 5;
+
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks;
+
+    const amount = 1n * helper.balance.getOneTokenNominal();
+
+    const balanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    // Fill the target block
+    for(let i = 0; i < maxScheduledPerBlock; i++) {
+      await helper.scheduler.scheduleAt(executionBlock, {scheduledId: fillScheduledIds[i]})
+        .balance.transferToSubstrate(superuser, bob.address, amount);
+    }
+
+    // Try to schedule a task into a full block
+    await expect(helper.scheduler.scheduleAt(executionBlock, {scheduledId: extraScheduledId})
+      .balance.transferToSubstrate(superuser, bob.address, amount))
+      .to.be.rejectedWith(/scheduler\.AgendaIsExhausted/);
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const balanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(balanceAfter > balanceBefore).to.be.true;
+
+    const diff = balanceAfter - balanceBefore;
+    expect(diff).to.be.equal(amount * BigInt(maxScheduledPerBlock));
+  });
+
+  itSched.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async (scheduleKind, {helper}) => {
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+    const waitForBlocks = 4;
+
+    const initTestVal = 42;
+    const changedTestVal = 111;
+
+    await helper.testUtils.setTestValue(alice, initTestVal);
+
+    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId})
+      .testUtils.setTestValueAndRollback(alice, changedTestVal);
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const testVal = await helper.testUtils.testValue();
+    expect(testVal, 'The test value should NOT be commited')
+      .to.be.equal(initTestVal);
+  });
+
+  itSched.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function(scheduleKind, {helper}) {
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+    const waitForBlocks = 4;
+    const periodic = {
+      period: 2,
+      repetitions: 2,
+    };
+
+    const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []);
+    const scheduledLen = dummyTx.callIndex.length;
+
+    const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen))
+      .partialFee.toBigInt();
+
+    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId, periodic})
+      .testUtils.justTakeFee(alice);
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    const aliceInitBalance = await helper.balance.getSubstrate(alice.address);
+    let diff;
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address);
+    expect(
+      aliceBalanceAfterFirst < aliceInitBalance,
+      '[after execution #1] Scheduled task should take a fee',
+    ).to.be.true;
+
+    diff = aliceInitBalance - aliceBalanceAfterFirst;
+    expect(diff).to.be.equal(
+      expectedScheduledFee,
+      'Scheduled task should take the right amount of fees',
+    );
+
+    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
+
+    const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address);
+    expect(
+      aliceBalanceAfterSecond < aliceBalanceAfterFirst,
+      '[after execution #2] Scheduled task should take a fee',
+    ).to.be.true;
+
+    diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond;
+    expect(diff).to.be.equal(
+      expectedScheduledFee,
+      'Scheduled task should take the right amount of fees',
+    );
+  });
+
+  // Check if we can cancel a scheduled periodic operation
+  // in the same block in which it is running
+  itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.TestUtils], async ({helper}) => {
+    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
+    const blocksBeforeExecution = 10;
+    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
+
+    const [
+      scheduledId,
+      scheduledCancelId,
+    ] = helper.arrange.makeScheduledIds(2);
+
+    const periodic = {
+      period: 5,
+      repetitions: 5,
+    };
+
+    const initTestVal = 0;
+    const incTestVal = initTestVal + 1;
+    const finalTestVal = initTestVal + 2;
+
+    await helper.testUtils.setTestValue(alice, initTestVal);
+
+    await helper.scheduler.scheduleAt<DevUniqueHelper>(firstExecutionBlockNumber, {scheduledId, periodic})
+      .testUtils.incTestValue(alice);
+
+    // Cancel the inc tx after 2 executions
+    // *in the same block* in which the second execution is scheduled
+    await helper.scheduler.scheduleAt(
+      firstExecutionBlockNumber + periodic.period,
+      {scheduledId: scheduledCancelId},
+    ).scheduler.cancelScheduled(alice, scheduledId);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
+
+    // execution #0
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(incTestVal);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);
+
+    // execution #1
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(finalTestVal);
+
+    for(let i = 1; i < periodic.repetitions; i++) {
+      await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1));
+      expect(await helper.testUtils.testValue())
+        .to.be.equal(finalTestVal);
+    }
+  });
+
+  itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.TestUtils], async ({helper}) => {
+    const scheduledId = helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+    const periodic = {
+      period: 2,
+      repetitions: 5,
+    };
+
+    const initTestVal = 0;
+    const maxTestVal = 2;
+
+    await helper.testUtils.setTestValue(alice, initTestVal);
+
+    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId, periodic})
+      .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal);
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    // execution #0
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(initTestVal + 1);
+
+    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
+
+    // execution #1
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(initTestVal + 2);
+
+    await helper.wait.forParachainBlockNumber(executionBlock + 2 * periodic.period);
+
+    // <canceled>
+    expect(await helper.testUtils.testValue())
+      .to.be.equal(initTestVal + 2);
+  });
+
+  itSub('Root can cancel any scheduled operation', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(bob);
+
+    const scheduledId = helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+      .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.getSudo().scheduler.cancelScheduled(superuser, scheduledId);
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSched('Root can set prioritized scheduled operation', async (scheduleKind, {helper}) => {
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+    const waitForBlocks = 4;
+
+    const amount = 42n * helper.balance.getOneTokenNominal();
+
+    const balanceBefore = await helper.balance.getSubstrate(charlie.address);
+
+    await helper.getSudo()
+      .scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42})
+      .balance.forceTransferToSubstrate(superuser, bob.address, charlie.address, amount);
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const balanceAfter = await helper.balance.getSubstrate(charlie.address);
+
+    expect(balanceAfter > balanceBefore).to.be.true;
+
+    const diff = balanceAfter - balanceBefore;
+    expect(diff).to.be.equal(amount);
+  });
+
+  itSub("Root can change scheduled operation's priority", async ({helper}) => {
+    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(bob);
+
+    const scheduledId = helper.arrange.makeScheduledId();
+    const waitForBlocks = 6;
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+      .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    const priority = 112;
+    await helper.getSudo().scheduler.changePriority(superuser, scheduledId, priority);
+
+    const priorityChanged = await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged);
+
+    const [blockNumber, index] = priorityChanged.task();
+    expect(blockNumber).to.be.equal(executionBlock);
+    expect(index).to.be.equal(0);
+
+    expect(priorityChanged.priority().toString()).to.be.equal(priority.toString());
+  });
+
+  itSub('Prioritized operations execute in valid order', async ({helper}) => {
+    const [
+      scheduledFirstId,
+      scheduledSecondId,
+    ] = helper.arrange.makeScheduledIds(2);
+
+    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
+    const blocksBeforeExecution = 6;
+    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
+
+    const prioHigh = 0;
+    const prioLow = 255;
+
+    const periodic = {
+      period: 6,
+      repetitions: 2,
+    };
+
+    const amount = 1n * helper.balance.getOneTokenNominal();
+
+    // Scheduler a task with a lower priority first, then with a higher priority
+    await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, {
+      scheduledId: scheduledFirstId,
+      priority: prioLow,
+      periodic,
+    }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);
+
+    await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, {
+      scheduledId: scheduledSecondId,
+      priority: prioHigh,
+      periodic,
+    }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);
+
+    const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched');
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
+
+    // Flip priorities
+    await helper.getSudo().scheduler.changePriority(superuser, scheduledFirstId, prioHigh);
+    await helper.getSudo().scheduler.changePriority(superuser, scheduledSecondId, prioLow);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);
+
+    const dispatchEvents = capture.extractCapturedEvents();
+    expect(dispatchEvents.length).to.be.equal(4);
+
+    const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString());
+
+    const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]];
+    const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]];
+
+    expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId);
+    expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId);
+
+    expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId);
+    expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId);
+  });
+
+  itSched('Periodic operations always can be rescheduled', async (scheduleKind, {helper}) => {
+    const maxScheduledPerBlock = 50;
+    const numFilledBlocks = 3;
+    const ids = helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1);
+    const periodicId = scheduleKind == 'named' ? ids[0] : undefined;
+    const fillIds = ids.slice(1);
+
+    const fillScheduleFn = scheduleKind == 'named' ? 'scheduleNamed' : 'schedule';
+
+    const initTestVal = 0;
+    const firstExecTestVal = 1;
+    const secondExecTestVal = 2;
+    await helper.testUtils.setTestValue(alice, initTestVal);
+
+    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
+    const blocksBeforeExecution = 8;
+    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
+
+    const period = 5;
+
+    const periodic = {
+      period,
+      repetitions: 2,
+    };
+
+    // Fill `numFilledBlocks` blocks beginning from the block in which the second execution should occur
+    const txs = [];
+    for(let offset = 0; offset < numFilledBlocks; offset ++) {
+      for(let i = 0; i < maxScheduledPerBlock; i++) {
+
+        const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]);
+
+        const when = firstExecutionBlockNumber + period + offset;
+        const mandatoryArgs = [when, null, null, scheduledTx];
+        const scheduleArgs = scheduleKind == 'named'
+          ? [fillIds[i + offset * maxScheduledPerBlock], ...mandatoryArgs]
+          : mandatoryArgs;
+
+        const tx = helper.constructApiCall(`api.tx.scheduler.${fillScheduleFn}`, scheduleArgs);
+
+        txs.push(tx);
+      }
+    }
+    await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true);
+
+    await helper.scheduler.scheduleAt<DevUniqueHelper>(firstExecutionBlockNumber, {
+      scheduledId: periodicId,
+      periodic,
+    }).testUtils.incTestValue(alice);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
+    expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal);
+
+    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + period + numFilledBlocks);
+
+    // The periodic operation should be postponed by `numFilledBlocks`
+    for(let i = 0; i < numFilledBlocks; i++) {
+      expect(await helper.testUtils.testValue(firstExecutionBlockNumber + period + i)).to.be.equal(firstExecTestVal);
+    }
+
+    // After the `numFilledBlocks` the periodic operation will eventually be executed
+    expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal);
+  });
+
+  itSched('scheduled operations does not change nonce', async (scheduleKind, {helper}) => {
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+    const blocksBeforeExecution = 4;
+
+    await helper.scheduler
+      .scheduleAfter<DevUniqueHelper>(blocksBeforeExecution, {scheduledId})
+      .balance.transferToSubstrate(alice, bob.address, 1n);
+    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;
+
+    const initNonce = await helper.chain.getNonce(alice.address);
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const finalNonce = await helper.chain.getNonce(alice.address);
+
+    expect(initNonce).to.be.equal(finalNonce);
+  });
+});
+
+describe('Negative Test: Scheduling', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]);
+
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+
+      await helper.testUtils.enable(Pallets.TestUtils);
+    });
+  });
+
+  itSub("Can't overwrite a scheduled ID", async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+
+    const scheduledId = helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId});
+    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal()))
+      .to.be.rejectedWith(/scheduler\.FailedToSchedule/);
+
+    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+    expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter);
+  });
+
+  itSub("Can't cancel an operation which is not scheduled", async ({helper}) => {
+    const scheduledId = helper.arrange.makeScheduledId();
+    await expect(helper.scheduler.cancelScheduled(alice, scheduledId))
+      .to.be.rejectedWith(/scheduler\.NotFound/);
+  });
+
+  itSub("Can't cancel a non-owned scheduled operation", async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(alice);
+
+    const scheduledId = helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await expect(helper.scheduler.cancelScheduled(bob, scheduledId))
+      .to.be.rejectedWith(/BadOrigin/);
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSched("Regular user can't set prioritized scheduled operation", async (scheduleKind, {helper}) => {
+    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
+    const waitForBlocks = 4;
+
+    const amount = 42n * helper.balance.getOneTokenNominal();
+
+    const balanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42});
+
+    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount))
+      .to.be.rejectedWith(/BadOrigin/);
+
+    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
+
+    await helper.wait.forParachainBlockNumber(executionBlock);
+
+    const balanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(balanceAfter).to.be.equal(balanceBefore);
+  });
+
+  itSub("Regular user can't change scheduled operation's priority", async ({helper}) => {
+    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
+    const token = await collection.mintToken(bob);
+
+    const scheduledId = helper.arrange.makeScheduledId();
+    const waitForBlocks = 4;
+
+    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
+      .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
+
+    const priority = 112;
+    await expect(helper.scheduler.changePriority(alice, scheduledId, priority))
+      .to.be.rejectedWith(/BadOrigin/);
+
+    await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged);
+  });
+});
+
+// Implementation of the functionality tested here was postponed/shelved
+describe.skip('Sponsoring scheduling', () => {
+  // let alice: IKeyringPair;
+  // let bob: IKeyringPair;
+
+  // before(async() => {
+  //   await usingApi(async (_, privateKey) => {
+  //     alice = privateKey('//Alice');
+  //     bob = privateKey('//Bob');
+  //   });
+  // });
+
+  it('Can sponsor scheduling a transaction', async () => {
+    // const collectionId = await createCollectionExpectSuccess();
+    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    // await usingApi(async api => {
+    //   const scheduledId = await makeScheduledId();
+    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+    //   const bobBalanceBefore = await getFreeBalance(bob);
+    //   const waitForBlocks = 4;
+    //   // no need to wait to check, fees must be deducted on scheduling, immediately
+    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId);
+    //   const bobBalanceAfter = await getFreeBalance(bob);
+    //   // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
+    //   expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
+    //   // wait for sequentiality matters
+    //   await waitNewBlocks(waitForBlocks - 1);
+    // });
+  });
+
+  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {
+    // await usingApi(async (api, privateKey) => {
+    //   // Find an empty, unused account
+    //   const zeroBalance = await findUnusedAddress(api, privateKey);
+
+    //   const collectionId = await createCollectionExpectSuccess();
+
+    //   // Add zeroBalance address to allow list
+    //   await enablePublicMintingExpectSuccess(alice, collectionId);
+    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+    //   // Grace zeroBalance with money, enough to cover future transactions
+    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
+    //   await submitTransactionAsync(alice, balanceTx);
+
+    //   // Mint a fresh NFT
+    //   const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');
+    //   const scheduledId = await makeScheduledId();
+
+    //   // Schedule transfer of the NFT a few blocks ahead
+    //   const waitForBlocks = 5;
+    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId);
+
+    //   // Get rid of the account's funds before the scheduled transaction takes place
+    //   const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
+    //   const events = await submitTransactionAsync(zeroBalance, balanceTx2);
+    //   expect(getGenericResult(events).success).to.be.true;
+    //   /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?
+    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);
+    //   const events = await submitTransactionAsync(alice, sudoTx);
+    //   expect(getGenericResult(events).success).to.be.true;*/
+
+    //   // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions
+    //   await waitNewBlocks(waitForBlocks - 3);
+
+    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
+    // });
+  });
+
+  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {
+    // const collectionId = await createCollectionExpectSuccess();
+
+    // await usingApi(async (api, privateKey) => {
+    //   const zeroBalance = await findUnusedAddress(api, privateKey);
+    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
+    //   await submitTransactionAsync(alice, balanceTx);
+
+    //   await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);
+    //   await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);
+
+    //   const scheduledId = await makeScheduledId();
+    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
+
+    //   const waitForBlocks = 5;
+    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId);
+
+    //   const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
+    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
+    //   const events = await submitTransactionAsync(alice, sudoTx);
+    //   expect(getGenericResult(events).success).to.be.true;
+
+    //   // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions
+    //   await waitNewBlocks(waitForBlocks - 3);
+
+    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));
+    // });
+  });
+
+  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
+    // const collectionId = await createCollectionExpectSuccess();
+    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
+    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
+
+    // await usingApi(async (api, privateKey) => {
+    //   const zeroBalance = await findUnusedAddress(api, privateKey);
+
+    //   await enablePublicMintingExpectSuccess(alice, collectionId);
+    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
+
+    //   const bobBalanceBefore = await getFreeBalance(bob);
+
+    //   const createData = {nft: {const_data: [], variable_data: []}};
+    //   const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);
+    //   const scheduledId = await makeScheduledId();
+
+    //   /*const badTransaction = async function () {
+    //     await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
+    //   };
+    //   await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
+
+    //   await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);
+
+    //   expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
+    // });
+  });
+});
addedjs-packages/tests/setCollectionLimits.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/setCollectionLimits.test.ts
@@ -0,0 +1,201 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+// 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';
+
+const accountTokenOwnershipLimit = 0;
+const sponsoredDataSize = 0;
+const sponsorTransferTimeout = 1;
+const tokenLimit = 10;
+
+describe('setCollectionLimits positive', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
+    });
+  });
+
+  itSub('execute setCollectionLimits with predefined params', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-1', tokenPrefix: 'SCL'});
+
+    await collection.setLimits(
+      alice,
+      {
+        accountTokenOwnershipLimit,
+        sponsoredDataSize,
+        tokenLimit,
+        sponsorTransferTimeout,
+        ownerCanTransfer: true,
+        ownerCanDestroy: true,
+      },
+    );
+
+    // get collection limits defined previously
+    const collectionInfo = await collection.getEffectiveLimits();
+
+    expect(collectionInfo.accountTokenOwnershipLimit).to.be.equal(accountTokenOwnershipLimit);
+    expect(collectionInfo.sponsoredDataSize).to.be.equal(sponsoredDataSize);
+    expect(collectionInfo.tokenLimit).to.be.equal(tokenLimit);
+    expect(collectionInfo.sponsorTransferTimeout).to.be.equal(sponsorTransferTimeout);
+    expect(collectionInfo.ownerCanTransfer).to.be.true;
+    expect(collectionInfo.ownerCanDestroy).to.be.true;
+  });
+
+  itSub('Set the same token limit twice', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-2', tokenPrefix: 'SCL'});
+
+    const collectionLimits = {
+      accountTokenOwnershipLimit,
+      sponsoredDataSize,
+      tokenLimit,
+      sponsorTransferTimeout,
+      ownerCanTransfer: true,
+      ownerCanDestroy: true,
+    };
+
+    await collection.setLimits(alice, collectionLimits);
+
+    const collectionInfo1 = await collection.getEffectiveLimits();
+
+    expect(collectionInfo1.tokenLimit).to.be.equal(tokenLimit);
+
+    await collection.setLimits(alice, collectionLimits);
+    const collectionInfo2 = await collection.getEffectiveLimits();
+    expect(collectionInfo2.tokenLimit).to.be.equal(tokenLimit);
+  });
+
+  itSub('execute setCollectionLimits from admin collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-3', tokenPrefix: 'SCL'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    const collectionLimits = {
+      accountTokenOwnershipLimit,
+      sponsoredDataSize,
+      // sponsoredMintSize,
+      tokenLimit,
+    };
+
+    await expect(collection.setLimits(alice, collectionLimits)).to.not.be.rejected;
+  });
+});
+
+describe('setCollectionLimits negative', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
+    });
+  });
+
+  itSub('execute setCollectionLimits for not exists collection', async ({helper}) => {
+    const nonExistentCollectionId = NON_EXISTENT_COLLECTION_ID;
+    await expect(helper.collection.setLimits(
+      alice,
+      nonExistentCollectionId,
+      {
+        accountTokenOwnershipLimit,
+        sponsoredDataSize,
+        // sponsoredMintSize,
+        tokenLimit,
+      },
+    )).to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('execute setCollectionLimits from user who is not owner of this collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-1', tokenPrefix: 'SCL'});
+
+    await expect(collection.setLimits(bob, {
+      accountTokenOwnershipLimit,
+      sponsoredDataSize,
+      // sponsoredMintSize,
+      tokenLimit,
+    })).to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('fails when trying to enable OwnerCanTransfer after it was disabled', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-2', tokenPrefix: 'SCL'});
+
+    await collection.setLimits(alice, {
+      accountTokenOwnershipLimit,
+      sponsoredDataSize,
+      tokenLimit,
+      sponsorTransferTimeout,
+      ownerCanTransfer: false,
+      ownerCanDestroy: true,
+    });
+
+    await expect(collection.setLimits(alice, {
+      accountTokenOwnershipLimit,
+      sponsoredDataSize,
+      tokenLimit,
+      sponsorTransferTimeout,
+      ownerCanTransfer: true,
+      ownerCanDestroy: true,
+    })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/);
+  });
+
+  itSub('fails when trying to enable OwnerCanDestroy after it was disabled', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-3', tokenPrefix: 'SCL'});
+
+    await collection.setLimits(alice, {
+      accountTokenOwnershipLimit,
+      sponsoredDataSize,
+      tokenLimit,
+      sponsorTransferTimeout,
+      ownerCanTransfer: true,
+      ownerCanDestroy: false,
+    });
+
+    await expect(collection.setLimits(alice, {
+      accountTokenOwnershipLimit,
+      sponsoredDataSize,
+      tokenLimit,
+      sponsorTransferTimeout,
+      ownerCanTransfer: true,
+      ownerCanDestroy: true,
+    })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/);
+  });
+
+  itSub('Setting the higher token limit fails', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-4', tokenPrefix: 'SCL'});
+
+    const collectionLimits = {
+      accountTokenOwnershipLimit: accountTokenOwnershipLimit,
+      sponsoredMintSize: sponsoredDataSize,
+      tokenLimit: tokenLimit,
+      sponsorTransferTimeout,
+      ownerCanTransfer: true,
+      ownerCanDestroy: true,
+    };
+
+    // The first time
+    await collection.setLimits(alice, collectionLimits);
+
+    // The second time - higher token limit
+    collectionLimits.tokenLimit += 1;
+    await expect(collection.setLimits(alice, collectionLimits)).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
+  });
+});
addedjs-packages/tests/setCollectionSponsor.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/setCollectionSponsor.test.ts
@@ -0,0 +1,120 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+describe('integration test: ext. setCollectionSponsor():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('Set NFT collection sponsor', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-1-NFT', tokenPrefix: 'SCS'});
+    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
+
+    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
+      Unconfirmed: bob.address,
+    });
+  });
+
+  itSub('Set Fungible collection sponsor', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'SetCollectionSponsor-1-FT', tokenPrefix: 'SCS'});
+    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
+
+    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
+      Unconfirmed: bob.address,
+    });
+  });
+
+  itSub.ifWithPallets('Set ReFungible collection sponsor', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'SetCollectionSponsor-1-RFT', tokenPrefix: 'SCS'});
+    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
+
+    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
+      Unconfirmed: bob.address,
+    });
+  });
+
+  itSub('Set the same sponsor repeatedly', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-2', tokenPrefix: 'SCS'});
+    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
+    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
+
+    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
+      Unconfirmed: bob.address,
+    });
+  });
+
+  itSub('Replace collection sponsor', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-3', tokenPrefix: 'SCS'});
+    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
+    await expect(collection.setSponsor(alice, charlie.address)).to.be.not.rejected;
+
+    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
+      Unconfirmed: charlie.address,
+    });
+  });
+
+  itSub('Collection admin add sponsor', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-4', tokenPrefix: 'SCS'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await expect(collection.setSponsor(bob, charlie.address)).to.be.not.rejected;
+
+    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
+      Unconfirmed: charlie.address,
+    });
+  });
+});
+
+describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([10n, 5n], donor);
+    });
+  });
+
+  itSub('(!negative test!) Add sponsor with a non-owner', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-1', tokenPrefix: 'SCS'});
+    await expect(collection.setSponsor(bob, bob.address))
+      .to.be.rejectedWith(/common\.NoPermission/);
+  });
+
+  itSub('(!negative test!) Add sponsor to a collection that never existed', async ({helper}) => {
+    const collectionId = NON_EXISTENT_COLLECTION_ID;
+    await expect(helper.collection.setSponsor(alice, collectionId, bob.address))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('(!negative test!) Add sponsor to a collection that was destroyed', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-2', tokenPrefix: 'SCS'});
+    await collection.burn(alice);
+    await expect(collection.setSponsor(alice, bob.address))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+});
addedjs-packages/tests/setPermissions.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/setPermissions.test.ts
@@ -0,0 +1,108 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+
+describe('Integration Test: Set Permissions', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
+    });
+  });
+
+  itSub('can all be enabled twice', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-1', tokenPrefix: 'SP'});
+    expect((await collection.getData())?.raw.permissions.access).to.not.equal('AllowList');
+
+    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}});
+    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}});
+
+    const permissions = (await collection.getData())?.raw.permissions;
+    expect(permissions).to.be.deep.equal({
+      access: 'AllowList',
+      mintMode: true,
+      nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]},
+    });
+  });
+
+  itSub('can all be disabled twice', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'});
+    expect((await collection.getData())?.raw.permissions.access).to.equal('Normal');
+
+    await collection.setPermissions(alice, {access: 'AllowList', nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}});
+    expect((await collection.getData())?.raw.permissions).to.be.deep.equal({
+      access: 'AllowList',
+      mintMode: false,
+      nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]},
+    });
+
+    await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}});
+    await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}});
+    expect((await collection.getData())?.raw.permissions).to.be.deep.equal({
+      access: 'Normal',
+      mintMode: false,
+      nesting: {collectionAdmin: false, tokenOwner: false, restricted: null},
+    });
+  });
+
+  itSub('collection admin can set permissions', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    await collection.setPermissions(bob, {access: 'AllowList', mintMode: true});
+
+    expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList');
+    expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true);
+  });
+});
+
+describe('Negative Integration Test: Set Permissions', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
+    });
+  });
+
+  itSub('fails on not existing collection', async ({helper}) => {
+    const collectionId = NON_EXISTENT_COLLECTION_ID;
+    await expect(helper.collection.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('fails on removed collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-1', tokenPrefix: 'SP'});
+    await collection.burn(alice);
+
+    await expect(collection.setPermissions(alice, {access: 'AllowList', mintMode: true}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('fails when non-owner tries to set permissions', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-2', tokenPrefix: 'SP'});
+
+    await expect(collection.setPermissions(bob, {access: 'AllowList', mintMode: true}))
+      .to.be.rejectedWith(/common\.NoPermission/);
+  });
+});
deletedjs-packages/tests/src/addCollectionAdmin.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/addCollectionAdmin.test.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
-  let donor: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itSub('Add collection admin.', async ({helper}) => {
-    const [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
-    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
-
-    const collection = await helper.collection.getData(collectionId);
-    expect(collection!.normalizedOwner!).to.be.equal(helper.address.normalizeSubstrate(alice.address));
-
-    await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});
-
-    const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId);
-    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
-  });
-});
-
-describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
-  let donor: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itSub("Not owner can't add collection admin.", async ({helper}) => {
-    const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
-
-    const collection = await helper.collection.getData(collectionId);
-    expect(collection?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
-
-    await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/);
-    await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.NoPermission/);
-
-    const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId);
-    expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: charlie.address});
-    expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: bob.address});
-  });
-
-  itSub("Admin can't add collection admin.", async ({helper}) => {
-    const [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
-
-    await collection.addAdmin(alice, {Substrate: bob.address});
-
-    const adminListAfterAddAdmin = await collection.getAdmins();
-    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
-
-    await expect(collection.addAdmin(bob, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/);
-
-    const adminListAfterAddNewAdmin = await collection.getAdmins();
-    expect(adminListAfterAddNewAdmin).to.be.deep.contains({Substrate: bob.address});
-    expect(adminListAfterAddNewAdmin).to.be.not.deep.contains({Substrate: charlie.address});
-  });
-
-  itSub("Can't add collection admin of not existing collection.", async ({helper}) => {
-    const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    const collectionId = NON_EXISTENT_COLLECTION_ID;
-
-    await expect(helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/);
-
-    // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
-    await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
-  });
-
-  itSub("Can't add an admin to a destroyed collection.", async ({helper}) => {
-    const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
-
-    await collection.burn(alice);
-    await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/);
-
-    // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
-    await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
-  });
-
-  itSub('Add an admin to a collection that has reached the maximum number of admins limit', async ({helper}) => {
-    const [alice, ...accounts] = await helper.arrange.createAccounts([10n, 0n, 0n, 0n, 0n, 0n, 0n, 0n], donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
-
-    const chainAdminLimit = (helper.getApi().consts.common.collectionAdminsLimit as any).toNumber();
-    expect(chainAdminLimit).to.be.equal(5);
-
-    for(let i = 0; i < chainAdminLimit; i++) {
-      await collection.addAdmin(alice, {Substrate: accounts[i].address});
-      const adminListAfterAddAdmin = await collection.getAdmins();
-      expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: accounts[i].address});
-    }
-
-    await expect(collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address})).to.be.rejectedWith(/common\.CollectionAdminCountExceeded/);
-  });
-});
deletedjs-packages/tests/src/adminTransferAndBurn.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/adminTransferAndBurn.test.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
-
-describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('admin transfers other user\'s token', async ({helper}) => {
-    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
-    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
-    const limits = await helper.collection.getEffectiveLimits(collectionId);
-    expect(limits.ownerCanTransfer).to.be.true;
-
-    const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
-    await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})).to.be.rejected;
-
-    await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address});
-    const newTokenOwner = await helper.nft.getTokenOwner(collectionId, tokenId);
-    expect(newTokenOwner).to.be.deep.equal({Substrate: charlie.address});
-  });
-
-  itSub('admin burns other user\'s token', async ({helper}) => {
-    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
-
-    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
-    const limits = await helper.collection.getEffectiveLimits(collectionId);
-    expect(limits.ownerCanTransfer).to.be.true;
-
-    const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
-
-    await expect(helper.nft.burnToken(alice, collectionId, tokenId)).to.be.rejected;
-
-    await helper.nft.burnToken(bob, collectionId, tokenId);
-    const token = await helper.nft.getToken(collectionId, tokenId);
-    expect(token).to.be.null;
-  });
-});
deletedjs-packages/tests/src/allowLists.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/allowLists.test.ts
+++ /dev/null
@@ -1,361 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
-import type {ICollectionPermissions} from '@unique/playgrounds/src/types.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('Integration Test ext. Allow list tests', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor);
-    });
-  });
-
-  describe('Positive', () => {
-    itSub('Owner can add address to allow list', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      // allow list does not need to be enabled to add someone in advance
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
-      const allowList = await helper.nft.getAllowList(collectionId);
-      expect(allowList).to.deep.contain({Substrate: bob.address});
-    });
-
-    itSub('Admin can add address to allow list', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.addAdmin(alice, collectionId, {Substrate: bob.address});
-
-      // allow list does not need to be enabled to add someone in advance
-      await helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address});
-      const allowList = await helper.nft.getAllowList(collectionId);
-      expect(allowList).to.deep.contain({Substrate: charlie.address});
-    });
-
-    itSub('If address is already added to allow list, nothing happens', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
-      const allowList = await helper.nft.getAllowList(collectionId);
-      expect(allowList).to.deep.contain({Substrate: bob.address});
-    });
-  });
-
-  describe('Negative', () => {
-    itSub('Nobody can add address to allow list of non-existing collection', async ({helper}) => {
-      const collectionId = NON_EXISTENT_COLLECTION_ID;
-      await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.CollectionNotFound/);
-    });
-
-    itSub('Nobody can add address to allow list of destroyed collection', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.collection.burn(alice, collectionId);
-      await expect(helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address}))
-        .to.be.rejectedWith(/common\.CollectionNotFound/);
-    });
-
-    itSub('Non-privileged user cannot add address to allow list', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.NoPermission/);
-    });
-  });
-});
-
-describe('Integration Test ext. Remove from Allow List', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor);
-    });
-  });
-
-  describe('Positive', () => {
-    itSub('Owner can remove address from allow list', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
-
-      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});
-
-      const allowList = await helper.nft.getAllowList(collectionId);
-
-      expect(allowList).to.not.deep.contain({Substrate: bob.address});
-    });
-
-    itSub('Admin can remove address from allow list', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.addAdmin(alice, collectionId, {Substrate: charlie.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
-      await helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: bob.address});
-
-      const allowList = await helper.nft.getAllowList(collectionId);
-      expect(allowList).to.not.deep.contain({Substrate: bob.address});
-    });
-
-    itSub('If address is already removed from allow list, nothing happens', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
-      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});
-      const allowListBefore = await helper.nft.getAllowList(collectionId);
-      expect(allowListBefore).to.not.deep.contain({Substrate: bob.address});
-
-      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address});
-
-      const allowListAfter = await helper.nft.getAllowList(collectionId);
-      expect(allowListAfter).to.not.deep.contain({Substrate: bob.address});
-    });
-  });
-
-  describe('Negative', () => {
-    itSub('Non-privileged user cannot remove address from allow list', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-      await expect(helper.collection.removeFromAllowList(charlie, collectionId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.NoPermission/);
-
-      const allowList = await helper.nft.getAllowList(collectionId);
-      expect(allowList).to.deep.contain({Substrate: charlie.address});
-    });
-
-    itSub('Nobody can remove address from allow list of non-existing collection', async ({helper}) => {
-      const collectionId = NON_EXISTENT_COLLECTION_ID;
-      await expect(helper.collection.removeFromAllowList(bob, collectionId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.CollectionNotFound/);
-    });
-
-    itSub('Nobody can remove address from allow list of deleted collection', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
-      await helper.collection.burn(alice, collectionId);
-
-      await expect(helper.collection.removeFromAllowList(alice, collectionId, {Substrate: bob.address}))
-        .to.be.rejectedWith(/common\.CollectionNotFound/);
-    });
-  });
-});
-
-describe('Integration Test ext. Transfer if included in Allow List', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([30n, 10n, 10n], donor);
-    });
-  });
-
-  describe('Positive', () => {
-    itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});
-      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
-      expect(owner).to.be.deep.equal({Substrate: charlie.address});
-    });
-
-    itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transferFrom.', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
-
-      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});
-      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
-      expect(owner).to.be.deep.equal({Substrate: charlie.address});
-    });
-
-    itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transfer', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-
-      await helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});
-      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
-      expect(owner).to.be.deep.equal({Substrate: charlie.address});
-    });
-
-    itSub('If Public Access mode is set to AllowList, tokens can be transferred from a allowlisted address with transferFrom', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
-
-      await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: alice.address}, {Substrate: charlie.address});
-      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
-      expect(owner).to.be.deep.equal({Substrate: charlie.address});
-    });
-  });
-
-  describe('Negative', () => {
-    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-
-      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
-    });
-
-    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
-      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address});
-
-      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
-    });
-
-    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
-
-      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
-    });
-
-    itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred to a non-allowlisted address with transfer or transferFrom. Test2', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: alice.address});
-      await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
-
-      await helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
-      await helper.collection.removeFromAllowList(alice, collectionId, {Substrate: alice.address});
-
-      await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address}))
-        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
-    });
-
-    itSub('If Public Access mode is set to AllowList, tokens can\'t be destroyed by a non-allowlisted address (even if it owned them before enabling AllowList mode)', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await expect(helper.nft.burnToken(bob, collectionId, tokenId))
-        .to.be.rejectedWith(/common\.NoPermission/);
-    });
-
-    itSub('If Public Access mode is set to AllowList, token transfers can\'t be Approved by a non-allowlisted address (see Approve method)', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
-      await helper.nft.setPermissions(alice, collectionId, {access: 'AllowList'});
-      await expect(helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}))
-        .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
-    });
-  });
-});
-
-describe('Integration Test ext. Mint if included in Allow List', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  const permissionSet: ICollectionPermissions[] = [
-    {access: 'Normal', mintMode: false},
-    {access: 'Normal', mintMode: true},
-    {access: 'AllowList', mintMode: false},
-    {access: 'AllowList', mintMode: true},
-  ];
-
-  const testPermissionSuite = (permissions: ICollectionPermissions) => {
-    const allowlistedMintingShouldFail = !permissions.mintMode!;
-
-    const appropriateRejectionMessage = permissions.mintMode! ? /common\.AddressNotInAllowlist/ : /common\.PublicMintingNotAllowed/;
-
-    const allowlistedMintingTest = () => itSub(
-      `With the condtions above, tokens can${allowlistedMintingShouldFail ? '\'t' : ''} be created by allow-listed addresses`,
-      async ({helper}) => {
-        const collection = await helper.nft.mintCollection(alice, {});
-        await collection.setPermissions(alice, permissions);
-        await collection.addToAllowList(alice, {Substrate: bob.address});
-
-        if(allowlistedMintingShouldFail)
-          await expect(collection.mintToken(bob, {Substrate: bob.address})).to.be.rejectedWith(appropriateRejectionMessage);
-        else
-          await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected;
-      },
-    );
-
-
-    describe(`Public Access Mode = ${permissions.access}, Mint Mode = ${permissions.mintMode}`, () => {
-      describe('Positive', () => {
-        itSub('With the condtions above, tokens can be created by owner', async ({helper}) => {
-          const collection = await helper.nft.mintCollection(alice, {});
-          await collection.setPermissions(alice, permissions);
-          await expect(collection.mintToken(alice, {Substrate: alice.address})).to.not.be.rejected;
-        });
-
-        itSub('With the condtions above, tokens can be created by admin', async ({helper}) => {
-          const collection = await helper.nft.mintCollection(alice, {});
-          await collection.setPermissions(alice, permissions);
-          await collection.addAdmin(alice, {Substrate: bob.address});
-          await expect(collection.mintToken(bob, {Substrate: bob.address})).to.not.be.rejected;
-        });
-
-        if(!allowlistedMintingShouldFail) allowlistedMintingTest();
-      });
-
-      describe('Negative', () => {
-        itSub('With the condtions above, tokens can\'t be created by non-priviliged non-allow-listed address', async ({helper}) => {
-          const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-          await collection.setPermissions(alice, permissions);
-          await expect(collection.mintToken(bob, {Substrate: bob.address}))
-            .to.be.rejectedWith(appropriateRejectionMessage);
-        });
-
-        if(allowlistedMintingShouldFail) allowlistedMintingTest();
-      });
-    });
-  };
-
-  for(const permissions of permissionSet) {
-    testPermissionSuite(permissions);
-  }
-});
deletedjs-packages/tests/src/apiConsts.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/apiConsts.test.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {ApiPromise} from '@polkadot/api';
-import {usingPlaygrounds, itSub, expect, COLLECTION_HELPER, CONTRACT_HELPER} from './util/index.js';
-
-
-const MAX_COLLECTION_DESCRIPTION_LENGTH = 256n;
-const MAX_COLLECTION_NAME_LENGTH = 64n;
-const COLLECTION_ADMINS_LIMIT = 5n;
-const MAX_COLLECTION_PROPERTIES_SIZE = 40960n;
-const MAX_TOKEN_PREFIX_LENGTH = 16n;
-const MAX_PROPERTY_KEY_LENGTH = 256n;
-const MAX_PROPERTY_VALUE_LENGTH = 32768n;
-const MAX_PROPERTIES_PER_ITEM = 64n;
-const MAX_TOKEN_PROPERTIES_SIZE = 32768n;
-const NESTING_BUDGET = 5n;
-
-const DEFAULT_COLLETCTION_LIMIT = {
-  accountTokenOwnershipLimit: '1,000,000',
-  sponsoredDataSize: '2,048',
-  sponsoredDataRateLimit: 'SponsoringDisabled',
-  tokenLimit: '4,294,967,295',
-  sponsorTransferTimeout: '5',
-  sponsorApproveTimeout: '5',
-  ownerCanTransfer: false,
-  ownerCanDestroy: true,
-  transfersEnabled: true,
-};
-
-describe('integration test: API UNIQUE consts', () => {
-  let api: ApiPromise;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper) => {
-      api = await helper.getApi();
-    });
-  });
-
-  itSub('DEFAULT_NFT_COLLECTION_LIMITS', () => {
-    expect(api.consts.unique.nftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
-  });
-
-  itSub('DEFAULT_RFT_COLLECTION_LIMITS', () => {
-    expect(api.consts.unique.rftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
-  });
-
-  itSub('DEFAULT_FT_COLLECTION_LIMITS', () => {
-    expect(api.consts.unique.ftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
-  });
-
-  itSub('MAX_COLLECTION_NAME_LENGTH', () => {
-    checkConst(api.consts.unique.maxCollectionNameLength, MAX_COLLECTION_NAME_LENGTH);
-  });
-
-  itSub('MAX_COLLECTION_DESCRIPTION_LENGTH', () => {
-    checkConst(api.consts.unique.maxCollectionDescriptionLength, MAX_COLLECTION_DESCRIPTION_LENGTH);
-  });
-
-  itSub('MAX_COLLECTION_PROPERTIES_SIZE', () => {
-    checkConst(api.consts.unique.maxCollectionPropertiesSize, MAX_COLLECTION_PROPERTIES_SIZE);
-  });
-
-  itSub('MAX_TOKEN_PREFIX_LENGTH', () => {
-    checkConst(api.consts.unique.maxTokenPrefixLength, MAX_TOKEN_PREFIX_LENGTH);
-  });
-
-  itSub('MAX_PROPERTY_KEY_LENGTH', () => {
-    checkConst(api.consts.unique.maxPropertyKeyLength, MAX_PROPERTY_KEY_LENGTH);
-  });
-
-  itSub('MAX_PROPERTY_VALUE_LENGTH', () => {
-    checkConst(api.consts.unique.maxPropertyValueLength, MAX_PROPERTY_VALUE_LENGTH);
-  });
-
-  itSub('MAX_PROPERTIES_PER_ITEM', () => {
-    checkConst(api.consts.unique.maxPropertiesPerItem, MAX_PROPERTIES_PER_ITEM);
-  });
-
-  itSub('NESTING_BUDGET', () => {
-    checkConst(api.consts.unique.nestingBudget, NESTING_BUDGET);
-  });
-
-  itSub('MAX_TOKEN_PROPERTIES_SIZE', () => {
-    checkConst(api.consts.unique.maxTokenPropertiesSize, MAX_TOKEN_PROPERTIES_SIZE);
-  });
-
-  itSub('COLLECTION_ADMINS_LIMIT', () => {
-    checkConst(api.consts.unique.collectionAdminsLimit, COLLECTION_ADMINS_LIMIT);
-  });
-
-  itSub('HELPERS_CONTRACT_ADDRESS', () => {
-    expect(api.consts.evmContractHelpers.contractAddress.toString().toLowerCase()).to.be.equal(CONTRACT_HELPER.toLowerCase());
-  });
-
-  itSub('EVM_COLLECTION_HELPERS_ADDRESS', () => {
-    expect(api.consts.common.contractAddress.toString().toLowerCase()).to.be.equal(COLLECTION_HELPER.toLowerCase());
-  });
-});
-
-function checkConst<T>(constValue: any, expectedValue: T) {
-  expect(constValue.toBigInt()).equal(expectedValue);
-}
\ No newline at end of file
deletedjs-packages/tests/src/approve.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/approve.test.ts
+++ /dev/null
@@ -1,658 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/src/unique.js';
-
-
-
-[
-  {method: 'approveToken', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toICrossAccountId()},
-  {method: 'approveTokenFromEth', account: (account: IKeyringPair) => CrossAccountId.fromKeyring(account).toEthereum().toICrossAccountId()},
-].map(testCase => {
-  describe(`Integration Test ${testCase.method}(spender, collection_id, item_id, amount):`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('[nft] Execute the extrinsic and check approvedList', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
-      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true;
-    });
-
-    itSub('[fungible] Execute the extrinsic and check approvedList', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
-      expect(amount).to.be.equal(1n);
-    });
-
-    itSub.ifWithPallets('[refungible] Execute the extrinsic and check approvedList', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
-      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
-      expect(amount).to.be.equal(1n);
-    });
-
-    itSub('[nft] Remove approval by using 0 amount', async ({helper}) => {
-      const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const collectionId = collection.collectionId;
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
-      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true;
-      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
-      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false;
-    });
-
-    itSub('[fungible] Remove approval by using 0 amount', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
-      expect(amountBefore).to.be.equal(1n);
-
-      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
-      const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
-      expect(amountAfter).to.be.equal(0n);
-    });
-
-    itSub.ifWithPallets('[refungible] Remove approval by using 0 amount', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
-      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
-      expect(amountBefore).to.be.equal(1n);
-
-      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
-      const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, testCase.account(alice));
-      expect(amountAfter).to.be.equal(0n);
-    });
-
-    itSub('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
-      const result = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address});
-      await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-  });
-
-  describe(`[${testCase.method}] Normal user can approve other users to transfer:`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('NFT', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
-      await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.true;
-    });
-
-    itSub('Fungible up to an approved amount', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      const amount = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob));
-      expect(amount).to.be.equal(1n);
-    });
-
-    itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n});
-      await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n);
-      const amount = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: charlie.address}, testCase.account(bob));
-      expect(amount).to.be.equal(100n);
-    });
-  });
-
-  describe(`[${testCase.method}] Approved users can transferFrom up to approved amount:`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('NFT', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
-      await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address});
-      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
-      expect(owner).to.be.deep.equal({Substrate: alice.address});
-    });
-
-    itSub('Fungible up to an approved amount', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
-      await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
-      const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
-      expect(after - before).to.be.equal(1n);
-    });
-
-    itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n});
-      await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
-      await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
-      const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
-      expect(after - before).to.be.equal(1n);
-    });
-  });
-
-  describe(`[${testCase.method}] Approved users cannot use transferFrom to repeat transfers if approved amount was already transferred:`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('NFT', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
-      await (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address});
-      const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
-      expect(owner).to.be.deep.equal({Substrate: alice.address});
-      const transferTokenFromTx = () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address});
-      await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow');
-    });
-
-    itSub('Fungible up to an approved amount', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(bob));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      const before = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
-      await helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
-      const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
-      expect(after - before).to.be.equal(1n);
-
-      const transferTokenFromTx = () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 1n);
-      await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow');
-    });
-
-    itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob), pieces: 100n});
-      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 100n);
-      const before = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
-      await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n);
-      const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
-      expect(after - before).to.be.equal(100n);
-      const transferTokenFromTx = () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, testCase.account(bob), {Substrate: alice.address}, 100n);
-      await expect(transferTokenFromTx()).to.be.rejectedWith('common.ApprovedValueTooLow');
-    });
-  });
-
-  describe(`[${testCase.method}] Approved amount decreases by the transferred amount:`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-    let dave: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('If a user B is approved to transfer 10 Fungible tokens from user A, they can transfer 2 tokens to user C, which will result in decreasing approval from 10 to 8. Then user B can transfer 8 tokens to user D.', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 10n);
-
-      const charlieBefore = await helper.ft.getBalance(collectionId, {Substrate: charlie.address});
-      await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: charlie.address}, 2n);
-      const charlieAfter = await helper.ft.getBalance(collectionId, {Substrate: charlie.address});
-      expect(charlieAfter - charlieBefore).to.be.equal(2n);
-
-      const daveBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
-      await helper.ft.transferTokenFrom(bob, collectionId, tokenId, testCase.account(alice), {Substrate: dave.address}, 8n);
-      const daveAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
-      expect(daveAfter - daveBefore).to.be.equal(8n);
-    });
-  });
-
-  describe(`[${testCase.method}] User may clear the approvals to approving for 0 amount:`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('NFT', async ({helper}) => {
-      const owner = testCase.account(alice);
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: owner});
-
-      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true;
-
-      await (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
-      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false;
-
-      const transferTokenFromTx = helper.nft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: bob.address});
-      await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow');
-    });
-
-    itSub('Fungible', async ({helper}) => {
-      const owner = testCase.account(alice);
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, owner);
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      const amountBefore = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
-      expect(amountBefore).to.be.equal(1n);
-
-      await (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
-      const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
-      expect(amountAfter).to.be.equal(0n);
-
-      const transferTokenFromTx = helper.ft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n);
-      await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow');
-    });
-
-    itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => {
-      const owner = testCase.account(alice);
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner, pieces: 100n});
-      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address});
-      const amountBefore = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
-      expect(amountBefore).to.be.equal(1n);
-
-      await (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 0n);
-      const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, owner);
-      expect(amountAfter).to.be.equal(0n);
-
-      const transferTokenFromTx = helper.rft.transferTokenFrom(bob, collectionId, tokenId, owner, {Substrate: charlie.address}, 1n);
-      await expect(transferTokenFromTx).to.be.rejectedWith('common.ApprovedValueTooLow');
-    });
-  });
-
-  describe(`[${testCase.method}] User cannot approve for the amount greater than they own:`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('1 for NFT', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
-      const result = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address}, 2n);
-      await expect(result).to.be.rejectedWith('nonfungible.NonfungibleItemsHaveNoAmount');
-      expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false;
-    });
-
-    itSub('Fungible', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      const result = (helper.ft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 11n);
-      await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-
-    itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
-      const result = (helper.rft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: bob.address}, 101n);
-      await expect(result).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-  });
-
-  describe(`[${testCase.method}] Integration Test approve(spender, collection_id, item_id, amount) with collection admin permissions:`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('cannot be called by collection admin on non-owned item', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
-      await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
-      const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: charlie.address});
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-  });
-
-  describe(`[${testCase.method}] Negative Integration Test approve(spender, collection_id, item_id, amount):`, () => {
-    let alice: IKeyringPair;
-    let bob: IKeyringPair;
-    let charlie: IKeyringPair;
-    const NONEXISTENT_COLLECTION = (2 ** 32) - 1;
-
-    before(async () => {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        const donor = await privateKey({url: import.meta.url});
-        [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-      });
-    });
-
-    itSub('[nft] Approve for a collection that does not exist', async ({helper}) => {
-      const approveTx = (helper.nft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address});
-      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
-    });
-
-    itSub('[fungible] Approve for a collection that does not exist', async ({helper}) => {
-      const approveTx = (helper.ft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address});
-      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
-    });
-
-    itSub.ifWithPallets('[refungible] Approve for a collection that does not exist', [Pallets.ReFungible], async ({helper}) => {
-      const approveTx = (helper.rft as any)[testCase.method](bob, NONEXISTENT_COLLECTION, 1, {Substrate: charlie.address});
-      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
-    });
-
-    itSub('[nft] Approve for a collection that was destroyed', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.nft.burn(alice, collectionId);
-      const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address});
-      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
-    });
-
-    itSub('[fungible] Approve for a collection that was destroyed', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.ft.burn(alice, collectionId);
-      const approveTx = (helper.ft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address});
-      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
-    });
-
-    itSub.ifWithPallets('[refungible] Approve for a collection that was destroyed', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.rft.burn(alice, collectionId);
-      const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 1, {Substrate: bob.address});
-      await expect(approveTx).to.be.rejectedWith('common.CollectionNotFound');
-    });
-
-    itSub('[nft] Approve transfer of a token that does not exist', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address});
-      await expect(approveTx).to.be.rejectedWith('common.TokenNotFound');
-    });
-
-    itSub.ifWithPallets('[refungible] Approve transfer of a token that does not exist', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const approveTx = (helper.rft as any)[testCase.method](alice, collectionId, 2, {Substrate: bob.address});
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned'); // TODO: why the error is not common.TokenNotFound
-    });
-
-    itSub('[nft] Approve using the address that does not own the approved token', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice)});
-      const approveTx = (helper.nft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address});
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-
-    itSub('[fungible] Approve using the address that does not own the approved token', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.ft.mintTokens(alice, collectionId, 10n, testCase.account(alice));
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-      const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address});
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-
-    itSub.ifWithPallets('[refungible] Approve using the address that does not own the approved token', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(alice), pieces: 100n});
-      const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address});
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-
-    itSub.ifWithPallets('should fail if approved more ReFungibles than owned', [Pallets.ReFungible], async ({helper}) => {
-      const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n});
-      await helper.rft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 100n);
-      await (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 100n);
-
-      const approveTx = (helper.rft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 101n);
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-
-    itSub('should fail if approved more Fungibles than owned', async ({helper}) => {
-      const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      await helper.ft.mintTokens(alice, collectionId, 10n, alice.address);
-      const tokenId = await helper.ft.getLastTokenId(collectionId);
-
-      await helper.ft.transferToken(alice, collectionId, tokenId, testCase.account(bob), 10n);
-      await (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 10n);
-      const approveTx = (helper.ft as any)[testCase.method](bob, collectionId, tokenId, {Substrate: alice.address}, 11n);
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-
-    itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => {
-      const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-      const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: testCase.account(bob)});
-      await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: false});
-
-      const approveTx = (helper.nft as any)[testCase.method](alice, collectionId, tokenId, {Substrate: charlie.address});
-      await expect(approveTx).to.be.rejectedWith('common.CantApproveMoreThanOwned');
-    });
-  });
-});
-
-describe('Normal user can approve other users to be wallet operator:', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('[nft] Enable and disable approval', async ({helper}) => {
-    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-
-    const checkBeforeApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
-    expect(checkBeforeApproval).to.be.false;
-
-    await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true);
-    const checkAfterApproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
-    expect(checkAfterApproval).to.be.true;
-
-    await helper.nft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false);
-    const checkAfterDisapproval = await helper.nft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
-    expect(checkAfterDisapproval).to.be.false;
-  });
-
-  itSub.ifWithPallets('[rft] Enable and disable approval', [Pallets.ReFungible], async ({helper}) => {
-    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-
-    const checkBeforeApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
-    expect(checkBeforeApproval).to.be.false;
-
-    await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, true);
-    const checkAfterApproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
-    expect(checkAfterApproval).to.be.true;
-
-    await helper.rft.setAllowanceForAll(alice, collectionId, {Substrate: bob.address}, false);
-    const checkAfterDisapproval = await helper.rft.allowanceForAll(collectionId, {Substrate: alice.address}, {Substrate: bob.address});
-    expect(checkAfterDisapproval).to.be.false;
-  });
-});
-
-describe('Administrator and collection owner do not need approval in order to execute TransferFrom (with owner_can_transfer_flag = true):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-  let dave: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub('NFT', async ({helper}) => {
-    const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
-    const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: charlie.address});
-
-    await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address});
-    const owner1 = await helper.nft.getTokenOwner(collectionId, tokenId);
-    expect(owner1).to.be.deep.equal({Substrate: dave.address});
-
-    await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
-    await helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address});
-    const owner2 = await helper.nft.getTokenOwner(collectionId, tokenId);
-    expect(owner2).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itSub('Fungible up to an approved amount', async ({helper}) => {
-    const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
-    await helper.ft.mintTokens(alice, collectionId, 10n, charlie.address);
-    const tokenId = await helper.ft.getLastTokenId(collectionId);
-
-    const daveBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
-    await helper.ft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n);
-    const daveBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: dave.address});
-    expect(daveBalanceAfter - daveBalanceBefore).to.be.equal(1n);
-
-    await helper.collection.addAdmin(alice ,collectionId, {Substrate: bob.address});
-
-    const aliceBalanceBefore = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
-    await helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n);
-    const aliceBalanceAfter = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
-    expect(aliceBalanceAfter - aliceBalanceBefore).to.be.equal(1n);
-  });
-
-  itSub.ifWithPallets('ReFungible up to an approved amount', [Pallets.ReFungible], async ({helper}) => {
-    const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
-    const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: charlie.address, pieces: 100n});
-
-    const daveBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address});
-    await helper.rft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: charlie.address}, {Substrate: dave.address}, 1n);
-    const daveAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: dave.address});
-    expect(daveAfter - daveBefore).to.be.equal(1n);
-
-    await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
-
-    const aliceBefore = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
-    await helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: dave.address}, {Substrate: alice.address}, 1n);
-    const aliceAfter = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
-    expect(aliceAfter - aliceBefore).to.be.equal(1n);
-  });
-});
-
-describe('Repeated approvals add up', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-  let dave: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. Fungible', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {});
-    await collection.mint(alice, 10n);
-    await collection.approveTokens(alice, {Substrate: bob.address}, 1n);
-    await collection.approveTokens(alice, {Substrate: charlie.address}, 1n);
-    // const allowances1 = await getAllowance(collectionId, 0, Alice.address, Bob.address);
-    // const allowances2 = await getAllowance(collectionId, 0, Alice.address, Charlie.address);
-    // expect(allowances1 + allowances2).to.be.eq(2n);
-  });
-
-  itSub.skip('Owned 10, approval 1: 1, approval 2: 1, resulting approved value: 2. ReFungible', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice, 10n);
-    await token.approve(alice, {Substrate: bob.address}, 1n);
-    await token.approve(alice, {Substrate: charlie.address}, 1n);
-    // const allowances1 = await getAllowance(collectionId, itemId, Alice.address, Bob.address);
-    // const allowances2 = await getAllowance(collectionId, itemId, Alice.address, Charlie.address);
-    // expect(allowances1 + allowances2).to.be.eq(2n);
-  });
-
-  // Canceled by changing approve logic
-  itSub.skip('Cannot approve for more than total user\'s amount (owned: 10, approval 1: 5 - should succeed, approval 2: 6 - should fail). Fungible', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {});
-    await collection.mint(alice, 10n, {Substrate: dave.address});
-    await collection.approveTokens(dave, {Substrate: bob.address}, 5n);
-    await expect(collection.approveTokens(dave, {Substrate: charlie.address}, 6n))
-      .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received');
-  });
-
-  // Canceled by changing approve logic
-  itSub.skip('Cannot approve for more than total user\'s amount (owned: 100, approval 1: 50 - should succeed, approval 2: 51 - should fail). ReFungible', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice, 100n, {Substrate: dave.address});
-    await token.approve(dave, {Substrate: bob.address}, 50n);
-    await expect(token.approve(dave, {Substrate: charlie.address}, 51n))
-      .to.be.rejectedWith('this test would fail (since it is skipped), replace this expecting message with what would have been received');
-  });
-});
deletedjs-packages/tests/src/burnItem.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/burnItem.test.ts
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util/index.js';
-
-
-describe('integration test: ext. burnItem():', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  itSub('Burn item in NFT collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    const token = await collection.mintToken(alice);
-
-    await token.burn(alice);
-    expect(await token.doesExist()).to.be.false;
-  });
-
-  itSub('Burn item in Fungible collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {}, 10);
-    await collection.mint(alice, 10n);
-
-    await collection.burnTokens(alice, 1n);
-    expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n);
-  });
-});
-
-describe('integration test: ext. burnItem() with admin permissions:', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Burn item in NFT collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    const token = await collection.mintToken(alice);
-
-    await token.burnFrom(bob, {Substrate: alice.address});
-    expect(await token.doesExist()).to.be.false;
-  });
-
-  itSub('Burn item in Fungible collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {}, 0);
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await collection.mint(alice, 10n);
-
-    await collection.burnTokensFrom(bob, {Substrate: alice.address}, 1n);
-    expect(await collection.getBalance({Substrate: alice.address})).to.eq(9n);
-  });
-});
-
-describe('Negative integration test: ext. burnItem():', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Burn a token that was never created', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    await expect(collection.burnToken(alice, 10)).to.be.rejectedWith('common.TokenNotFound');
-  });
-
-  itSub('Burn a token using the address that does not own it', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    const token = await collection.mintToken(alice);
-
-    await expect(token.burn(bob)).to.be.rejectedWith('common.NoPermission');
-  });
-
-  itSub('Transfer a burned token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    const token = await collection.mintToken(alice);
-    await token.burn(alice);
-
-    await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith('common.TokenNotFound');
-  });
-
-  itSub('Burn more than owned in Fungible collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {}, 0);
-    await collection.mint(alice, 10n);
-
-    await expect(collection.burnTokens(alice, 11n)).to.be.rejectedWith('common.TokenValueTooLow');
-    expect(await collection.getBalance({Substrate: alice.address})).to.eq(10n);
-  });
-
-  itSub('Zero burn NFT', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'Coll', description: 'Desc', tokenPrefix: 'T'});
-    const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address});
-    const tokenBob = await collection.mintToken(alice, {Substrate: bob.address});
-
-    // 1. Zero burn of own tokens allowed:
-    await helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenAlice.tokenId, 0]);
-    // 2. Zero burn of non-owned tokens not allowed:
-    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission');
-    // 3. Zero burn of non-existing tokens not allowed:
-    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnItem', [collection.collectionId, 9999, 0])).to.be.rejectedWith('common.TokenNotFound');
-    expect(await tokenAlice.doesExist()).to.be.true;
-    expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address});
-    expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address});
-    // 4. Storage is not corrupted:
-    await tokenAlice.transfer(alice, {Substrate: bob.address});
-    await tokenBob.transfer(bob, {Substrate: alice.address});
-    expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address});
-    expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address});
-  });
-
-  itSub('zero burnFrom NFT', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'});
-    const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address});
-    const approvedNft = await collection.mintToken(alice, {Substrate: bob.address});
-    await approvedNft.approve(bob, {Substrate: alice.address});
-
-    // 1. Zero burnFrom of non-existing tokens not allowed:
-    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
-    // 2. Zero burnFrom of not approved tokens not allowed:
-    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
-    // 3. Zero burnFrom of approved tokens allowed:
-    await helper.executeExtrinsic(alice, 'api.tx.unique.burnFrom', [collection.collectionId, {Substrate: bob.address}, approvedNft.tokenId, 0]);
-
-    // 4.1 approvedNft still approved:
-    expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true;
-    // 4.2 bob is still the owner:
-    expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address});
-    expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address});
-    // 4.3 Alice can burn approved nft:
-    await approvedNft.burnFrom(alice, {Substrate: bob.address});
-    expect(await approvedNft.doesExist()).to.be.false;
-  });
-});
deletedjs-packages/tests/src/change-collection-owner.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/change-collection-owner.test.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Changing owner changes owner address', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const beforeChanging = await helper.collection.getData(collection.collectionId);
-    expect(beforeChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
-
-    await collection.changeOwner(alice, bob.address);
-    const afterChanging = await helper.collection.getData(collection.collectionId);
-    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
-  });
-});
-
-describe('Integration Test changeCollectionOwner(collection_id, new_owner) special checks for exOwner:', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub('Changing the owner of the collection is not allowed for the former owner', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-
-    await collection.changeOwner(alice, bob.address);
-
-    const changeOwnerTx = () => collection.changeOwner(alice, alice.address);
-    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
-
-    const afterChanging = await helper.collection.getData(collection.collectionId);
-    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
-  });
-
-  itSub('New collectionOwner has access to sponsorship management operations in the collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.changeOwner(alice, bob.address);
-
-    const afterChanging = await helper.collection.getData(collection.collectionId);
-    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
-
-    await collection.setSponsor(bob, charlie.address);
-    await collection.confirmSponsorship(charlie);
-    await collection.removeSponsor(bob);
-    const limits = {
-      accountTokenOwnershipLimit: 1,
-      tokenLimit: 1,
-      sponsorTransferTimeout: 1,
-      ownerCanDestroy: true,
-      ownerCanTransfer: true,
-    };
-
-    await collection.setLimits(bob, limits);
-    const gotLimits = await collection.getEffectiveLimits();
-    expect(gotLimits).to.be.deep.contains(limits);
-
-    await collection.setPermissions(bob, {access: 'AllowList', mintMode: true});
-
-    await collection.burn(bob);
-    const collectionData = await helper.collection.getData(collection.collectionId);
-    expect(collectionData).to.be.null;
-  });
-
-  itSub('New collectionOwner has access to changeCollectionOwner', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.changeOwner(alice, bob.address);
-    await collection.changeOwner(bob, charlie.address);
-    const collectionData = await collection.getData();
-    expect(collectionData?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(charlie.address));
-  });
-});
-
-describe('Negative Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub('Not owner can\'t change owner.', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const changeOwnerTx = () => collection.changeOwner(bob, bob.address);
-    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Collection admin can\'t change owner.', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    const changeOwnerTx = () => collection.changeOwner(bob, bob.address);
-    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Can\'t change owner of a non-existing collection.', async ({helper}) => {
-    const collectionId = NON_EXISTENT_COLLECTION_ID;
-    const changeOwnerTx = () => helper.collection.changeOwner(bob, collectionId, bob.address);
-    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('Former collectionOwner not allowed to sponsorship management operations in the collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.changeOwner(alice, bob.address);
-
-    const changeOwnerTx = () => collection.changeOwner(alice, alice.address);
-    await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
-
-    const afterChanging = await helper.collection.getData(collection.collectionId);
-    expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
-
-    const setSponsorTx = () => collection.setSponsor(alice, charlie.address);
-    const confirmSponsorshipTx = () => collection.confirmSponsorship(alice);
-    const removeSponsorTx = () => collection.removeSponsor(alice);
-    await expect(setSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
-    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
-    await expect(removeSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
-
-    const limits = {
-      accountTokenOwnershipLimit: 1,
-      tokenLimit: 1,
-      sponsorTransferTimeout: 1,
-      ownerCanDestroy: true,
-      ownerCanTransfer: true,
-    };
-
-    const setLimitsTx = () => collection.setLimits(alice, limits);
-    await expect(setLimitsTx()).to.be.rejectedWith(/common\.NoPermission/);
-
-    const setPermissionTx = () => collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
-    await expect(setPermissionTx()).to.be.rejectedWith(/common\.NoPermission/);
-
-    const burnTx = () => collection.burn(alice);
-    await expect(burnTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-});
deletedjs-packages/tests/src/check-event/burnItemEvent.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/check-event/burnItemEvent.test.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-
-describe('Burn Item event ', () => {
-  let alice: IKeyringPair;
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-  itSub('Check event from burnItem(): ', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    await token.burn(alice);
-    await helper.wait.newBlocks(1);
-
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contains('common.ItemDestroyed');
-    expect(eventStrings).to.contains('treasury.Deposit');
-    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
-  });
-});
deletedjs-packages/tests/src/check-event/createCollectionEvent.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/check-event/createCollectionEvent.test.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-describe('Create collection event ', () => {
-  let alice: IKeyringPair;
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-  itSub('Check event from createCollection(): ', async ({helper}) => {
-    await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    await helper.wait.newBlocks(1);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contains('common.CollectionCreated');
-    expect(eventStrings).to.contains('treasury.Deposit');
-    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
-  });
-});
deletedjs-packages/tests/src/check-event/createItemEvent.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/check-event/createItemEvent.test.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-describe('Create Item event ', () => {
-  let alice: IKeyringPair;
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-  itSub('Check event from createItem(): ', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    await collection.mintToken(alice, {Substrate: alice.address});
-    await helper.wait.newBlocks(1);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contains('common.ItemCreated');
-    expect(eventStrings).to.contains('treasury.Deposit');
-    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
-  });
-});
deletedjs-packages/tests/src/check-event/createMultipleItemsEvent.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/check-event/createMultipleItemsEvent.test.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-describe('Create Multiple Items Event event ', () => {
-  let alice: IKeyringPair;
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-  itSub('Check event from createMultipleItems(): ', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    await collection.mintMultipleTokens(alice, [
-      {owner: {Substrate: alice.address}},
-      {owner: {Substrate: alice.address}},
-    ]);
-
-    await helper.wait.newBlocks(1);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contains('common.ItemCreated');
-    expect(eventStrings).to.contains('treasury.Deposit');
-    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
-  });
-});
deletedjs-packages/tests/src/check-event/destroyCollectionEvent.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/check-event/destroyCollectionEvent.test.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-describe('Destroy collection event ', () => {
-  let alice: IKeyringPair;
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itSub('Check event from destroyCollection(): ', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    await collection.burn(alice);
-    await helper.wait.newBlocks(1);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contains('common.CollectionDestroyed');
-    expect(eventStrings).to.contains('treasury.Deposit');
-    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
-  });
-});
deletedjs-packages/tests/src/check-event/transferEvent.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/check-event/transferEvent.test.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-describe('Transfer event ', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
-    });
-  });
-
-  itSub('Check event from transfer(): ', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    await token.transfer(alice, {Substrate: bob.address});
-    await helper.wait.newBlocks(1);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contains('common.Transfer');
-    expect(eventStrings).to.contains('treasury.Deposit');
-    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
-  });
-});
deletedjs-packages/tests/src/check-event/transferFromEvent.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/check-event/transferFromEvent.test.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-describe('Transfer event ', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
-    });
-  });
-
-  itSub('Check event from transfer(): ', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    await token.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address});
-    await helper.wait.newBlocks(1);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contains('common.Transfer');
-    expect(eventStrings).to.contains('treasury.Deposit');
-    expect(eventStrings).to.contains('system.ExtrinsicSuccess');
-  });
-});
deletedjs-packages/tests/src/collator-selection/collatorSelection.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/collator-selection/collatorSelection.seqtest.ts
+++ /dev/null
@@ -1,423 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js';
-
-async function nodeAddress(name: string) {
-  // eslint-disable-next-line require-await
-  return await usingPlaygrounds(async (helper) => {
-    const envNodeStash = `RELAY_UNIQUE_NODE_${name.toUpperCase()}_STASH`;
-
-    const nodeStash = process.env[envNodeStash];
-    if(nodeStash) {
-      return helper.address.normalizeSubstrateToChainFormat(nodeStash);
-    } else {
-      throw Error(`"${envNodeStash}" env var is not set`);
-    }
-  });
-}
-
-async function getInitialInvulnerables() {
-  return await Promise.all([
-    nodeAddress('alpha'),
-    nodeAddress('beta'),
-    nodeAddress('gamma'),
-    nodeAddress('delta'),
-  ]);
-}
-
-async function resetInvulnerables() {
-  await usingPlaygrounds(async (helper, privateKey) => {
-    const superuser = await privateKey('//Alice');
-    const initialInvulnerables = await getInitialInvulnerables();
-
-    const invulnerables = await helper.collatorSelection.getInvulnerables();
-
-    // Remove all invulnerables but the first one
-    const firstInvulnerable = invulnerables[0];
-
-    let nonce = await helper.chain.getNonce(superuser.address);
-    await Promise.all(invulnerables.slice(1).map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [invulnerable], true, {nonce: nonce++})));
-
-    // Add the initial invulnerables
-    await Promise.all(initialInvulnerables.map(invulnerable => helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [invulnerable], true, {nonce: nonce++})));
-
-    // Remove the first invulnerable if it's not an initial one
-    if(!initialInvulnerables.includes(firstInvulnerable)) {
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [firstInvulnerable]);
-    }
-  });
-}
-
-// todo:collator Most preferable to launch this test in parallel somehow -- or change the session period (1 hr).
-describe('Integration Test: Collator Selection', () => {
-  let superuser: IKeyringPair;
-  let previousLicenseBond = 0n;
-  let licenseBond = 0n;
-
-  before(async function() {
-    if(!process.env.RUN_COLLATOR_TESTS) this.skip();
-
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.CollatorSelection]);
-      superuser = await privateKey('//Alice');
-
-      previousLicenseBond = await helper.collatorSelection.getLicenseBond();
-      licenseBond = 10n * helper.balance.getOneTokenNominal();
-      await helper.getSudo().collatorSelection.setLicenseBond(superuser, licenseBond);
-    });
-  });
-
-  describe('Dynamic shuffling of collators', () => {
-    // These two are the default invulnerables, and should return to be invulnerables after this suite.
-    let alphaNode: string;
-    let betaNode: string;
-
-    let gammaNode: string;
-    let deltaNode: string;
-
-    before(async function() {
-      await usingPlaygrounds(async (helper) => {
-        // todo:collator see again if blocks start to be finalized in dev mode
-        // Skip the collator block production in dev mode, since the blocks are sealed automatically.
-        if(await helper.arrange.isDevNode()) this.skip();
-
-        [alphaNode, betaNode, gammaNode, deltaNode] = await getInitialInvulnerables();
-
-        const invulnerables = await helper.collatorSelection.getInvulnerables();
-        expect(invulnerables.length, 'Invalid initial invulnerables number').to.be.equal(4);
-        expect(invulnerables, 'Invalid initial invulnerables').containSubset([alphaNode, betaNode, gammaNode, deltaNode]);
-      });
-    });
-
-    itSub('Change invulnerables and make sure they start producing blocks', async ({helper}) => {
-      let nonce = await helper.chain.getNonce(superuser.address);
-
-      nonce = await helper.chain.getNonce(superuser.address);
-      await expect(Promise.all([
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [alphaNode], true, {nonce: nonce++}),
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [betaNode], true, {nonce: nonce++}),
-      ])).to.be.fulfilled;
-
-      const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-      expect(newInvulnerables).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
-
-      await helper.wait.newSessions(2);
-
-      const newValidators = await helper.callRpc('api.query.session.validators');
-      expect(newValidators).to.contain(gammaNode).and.contain(deltaNode).and.be.length(2);
-
-      const lastBlockNumber = await helper.chain.getLatestBlockNumber();
-      await helper.wait.newBlocks(1);
-      const lastGammaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [gammaNode])).toNumber();
-      const lastDeltaBlock = (await helper.callRpc('api.query.collatorSelection.lastAuthoredBlock', [deltaNode])).toNumber();
-      expect(lastGammaBlock >= lastBlockNumber || lastDeltaBlock >= lastBlockNumber).to.be.true;
-    });
-
-    after(async () => {
-      await resetInvulnerables();
-    });
-  });
-
-  describe('Getting and releasing licenses to collate', () => {
-    let crowd: IKeyringPair[];
-
-    before(async function() {
-      await usingPlaygrounds(async (helper) => {
-        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
-
-        // set session keys for everyone
-        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
-      });
-    });
-
-    describe('Positive', () => {
-      itSub('Can lease and release a license', async ({helper}) => {
-        const account = crowd.pop()!;
-
-        // make sure it does not have any reserved funds
-        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(0n);
-
-        // getting a license reserves a license bond cost
-        await helper.collatorSelection.obtainLicense(account);
-        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
-        expect((await helper.balance.getSubstrateFull(account.address)).reserved).to.be.equal(licenseBond);
-
-        // releasing a license un-reserves the license bond cost
-        await helper.collatorSelection.releaseLicense(account);
-        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
-
-        const balance = await helper.balance.getSubstrateFull(account.address);
-        expect(balance.reserved).to.be.equal(0n);
-        expect(balance.free > 100n - licenseBond);
-      });
-
-      itSub('Can force revoke a license', async ({helper}) => {
-        const account = crowd.pop()!;
-
-        // getting a license reserves a license bond cost
-        const previousBalance = await helper.balance.getSubstrateFull(account.address);
-        await helper.collatorSelection.obtainLicense(account);
-        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
-
-        // force-releasing a license un-reserves the license bond cost as well
-        await helper.getSudo().collatorSelection.forceReleaseLicense(superuser, account.address);
-        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(previousBalance.reserved);
-
-        const balance = await helper.balance.getSubstrateFull(account.address);
-        expect(balance.reserved).to.be.equal(previousBalance.reserved);
-        expect(balance.free > previousBalance.free - licenseBond);
-      });
-    });
-
-    describe('Negative', () => {
-      itSub('Cannot get a license without session keys set', async ({helper}) => {
-        const [account] = await helper.arrange.createAccounts([100n], superuser);
-        await expect(helper.collatorSelection.obtainLicense(account))
-          .to.be.rejectedWith(/collatorSelection.ValidatorNotRegistered/);
-      });
-
-      itSub('Cannot register a license twice', async ({helper}) => {
-        const account = crowd.pop()!;
-        await helper.collatorSelection.obtainLicense(account);
-        await expect(helper.collatorSelection.obtainLicense(account))
-          .to.be.rejectedWith(/collatorSelection.AlreadyHoldingLicense/);
-      });
-
-      itSub('Cannot release a license twice', async ({helper}) => {
-        const account = crowd.pop()!;
-        await helper.collatorSelection.obtainLicense(account);
-        await helper.collatorSelection.releaseLicense(account);
-        await expect(helper.collatorSelection.releaseLicense(account))
-          .to.be.rejectedWith(/collatorSelection.NoLicense/);
-      });
-
-      itSub('Cannot force revoke a license as non-sudo', async ({helper}) => {
-        const account = crowd.pop()!;
-        await helper.collatorSelection.obtainLicense(account);
-        await expect(helper.collatorSelection.forceReleaseLicense(superuser, account.address))
-          .to.be.rejectedWith(/BadOrigin/);
-      });
-    });
-  });
-
-  describe('Onboarding, collating, and offboarding as collator candidates', () => {
-    let crowd: IKeyringPair[];
-
-    before(async function() {
-      await usingPlaygrounds(async (helper) => {
-        crowd = await helper.arrange.createCrowd(20, 100n, superuser);
-
-        // set session keys for everyone
-        await Promise.all(crowd.map(acc => helper.session.setOwnKeysFromAddress(acc)));
-      });
-    });
-
-    describe('Positive', () => {
-      itSub('Can onboard and offboard repeatedly', async ({helper}) => {
-        const account = crowd.pop()!;
-        await helper.collatorSelection.obtainLicense(account);
-        await helper.collatorSelection.onboard(account);
-        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
-
-        await helper.collatorSelection.offboard(account);
-        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
-
-        await helper.collatorSelection.onboard(account);
-        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([account.address]);
-
-        await helper.collatorSelection.offboard(account);
-        expect(await helper.collatorSelection.getCandidates()).to.be.deep.equal([]);
-      });
-
-      itSub('Penalizes and forfeits license from faulty collators', async ({helper}) => {
-        // This one shouldn't even be able to produce blocks.
-        const account = crowd.pop()!;
-        await helper.collatorSelection.obtainLicense(account);
-        await helper.collatorSelection.onboard(account);
-        expect(await helper.collatorSelection.getCandidates()).to.contain(account.address);
-
-        // Wait for 3 new sessions before checking that the collator will be kicked:
-        // one to get collator onboarded, and another two for the collator to fail
-        await helper.wait.newSessions(3);
-
-        expect(await helper.collatorSelection.getCandidates()).to.not.contain(account.address);
-        expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(0n);
-
-        // The account's reserved funds get slashed as a penalty
-        const balance = await helper.balance.getSubstrateFull(account.address);
-        expect(balance.reserved).to.be.equal(0n);
-        expect(balance.free < 100n - licenseBond);
-      });
-    });
-
-    describe('Negative', () => {
-      itSub('Cannot onboard without a license', async ({helper}) => {
-        const account = crowd.pop()!;
-        await expect(helper.collatorSelection.onboard(account))
-          .to.be.rejectedWith(/collatorSelection.NoLicense/);
-      });
-
-      itSub('Cannot offboard without a license', async ({helper}) => {
-        const account = crowd.pop()!;
-        await expect(helper.collatorSelection.offboard(account))
-          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
-      });
-
-      itSub('Cannot offboard while not onboarded', async ({helper}) => {
-        const account = crowd.pop()!;
-        await helper.collatorSelection.obtainLicense(account);
-        await expect(helper.collatorSelection.offboard(account))
-          .to.be.rejectedWith(/collatorSelection.NotCandidate/);
-      });
-
-      itSub('Cannot onboard while already onboarded', async ({helper}) => {
-        const account = crowd.pop()!;
-        await helper.collatorSelection.obtainLicense(account);
-        await helper.collatorSelection.onboard(account);
-        await expect(helper.collatorSelection.onboard(account))
-          .to.be.rejectedWith(/collatorSelection.AlreadyCandidate/);
-      });
-    });
-  });
-
-  describe('Addition and removal of invulnerables', () => {
-    describe('Positive', () => {
-      itSub('Adds an invulnerable', async ({helper}) => {
-        const [account] = await helper.arrange.createAccounts([10n], superuser);
-        const invulnerables = await helper.collatorSelection.getInvulnerables();
-
-        await helper.session.setOwnKeysFromAddress(account);
-        await helper.getSudo().collatorSelection.addInvulnerable(superuser, account.address);
-
-        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-        expect(invulnerables.concat(account.address)).to.have.all.members(newInvulnerables);
-      });
-
-      itSub('Removes an invulnerable', async ({helper}) => {
-        const invulnerables = await helper.collatorSelection.getInvulnerables();
-        const lastInvulnerable = invulnerables.pop()!;
-
-        await helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable);
-        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-        // invulnerables had its last element removed, so they should be equal
-        expect(newInvulnerables).to.have.all.members(invulnerables);
-      });
-    });
-
-    describe('Negative', () => {
-      itSub('Does not duplicate an invulnerable', async ({helper}) => {
-        const invulnerables = await helper.collatorSelection.getInvulnerables();
-        // adding an already invulnerable should not fail, but should not duplicate it either
-        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, invulnerables[0]))
-          .to.be.fulfilled;
-        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-        expect(newInvulnerables).to.have.all.members(invulnerables);
-      });
-
-      itSub('Cannot remove a non-existent invulnerable', async ({helper}) => {
-        const [account] = await helper.arrange.createAccounts([0n], superuser);
-        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, account.address))
-          .to.be.rejectedWith(/collatorSelection.NotInvulnerable/);
-      });
-
-      itSub('Cannot allow invulnerables to be empty', async ({helper}) => {
-        const invulnerables = await helper.collatorSelection.getInvulnerables();
-        const lastInvulnerable = invulnerables.pop()!;
-
-        let nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all(invulnerables.map((i: any) =>
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i], true, {nonce: nonce++})));
-
-        await expect(helper.getSudo().collatorSelection.removeInvulnerable(superuser, lastInvulnerable))
-          .to.be.rejectedWith(/collatorSelection.TooFewInvulnerables/);
-
-        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-        expect(newInvulnerables).to.be.deep.equal([lastInvulnerable]);
-
-        // restore the invulnerables to the previous state
-        nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all(invulnerables.map((i: any) =>
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i], true, {nonce: nonce++})));
-      });
-
-      itSub('Cannot have too many invulnerables', async ({helper}) => {
-        // todo:collator make sure that there is enough session time for a set of tests
-        // 28 non-functioning collators, teehee.
-
-        const invulnerablesLength = (await helper.collatorSelection.getInvulnerables()).length;
-        const invulnerablesUntilLimit = (await helper.collatorSelection.getDesiredCollators()) - invulnerablesLength;
-        const newInvulnerables = await helper.arrange.createAccounts(Array(invulnerablesUntilLimit).fill(10n), superuser);
-        const [lastInvulnerable] = await helper.arrange.createAccounts([10n], superuser);
-
-        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
-          helper.session.setOwnKeysFromAddress(i)));
-        await helper.session.setOwnKeysFromAddress(lastInvulnerable);
-
-        let nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.addInvulnerable', [i.address], true, {nonce: nonce++})));
-
-        await expect(helper.getSudo().collatorSelection.addInvulnerable(superuser, lastInvulnerable.address))
-          .to.be.rejectedWith(/collatorSelection.TooManyInvulnerables/);
-
-        // restore the invulnerables to the previous state
-        nonce = await helper.chain.getNonce(superuser.address);
-        await Promise.all(newInvulnerables.map((i: IKeyringPair) =>
-          helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.removeInvulnerable', [i.address], true, {nonce: nonce++})));
-      });
-
-      itSub('Forbids a non-sudo to add an invulnerable', async ({helper}) => {
-        const [account] = await helper.arrange.createAccounts([10n], superuser);
-        const invulnerables = await helper.collatorSelection.getInvulnerables();
-
-        await helper.session.setOwnKeysFromAddress(account);
-        await expect(helper.collatorSelection.addInvulnerable(superuser, account.address))
-          .to.be.rejectedWith(/BadOrigin/);
-
-        const newInvulnerables = await helper.collatorSelection.getInvulnerables();
-        expect(newInvulnerables).to.be.members(invulnerables);
-      });
-
-      itSub('Forbids a non-sudo to remove an invulnerable', async ({helper}) => {
-        const invulnerables = await helper.collatorSelection.getInvulnerables();
-        await expect(helper.collatorSelection.removeInvulnerable(superuser, invulnerables[0]))
-          .to.be.rejectedWith(/BadOrigin/);
-        expect(await helper.collatorSelection.getInvulnerables()).to.have.all.members(invulnerables);
-      });
-
-      after(async function() {
-        await resetInvulnerables();
-      });
-    });
-  });
-
-  after(async function() {
-    if(!process.env.RUN_COLLATOR_TESTS) return;
-
-    await usingPlaygrounds(async (helper) => {
-      if(helper.fetchMissingPalletNames([Pallets.CollatorSelection]).length != 0) return;
-
-      await helper.getSudo().collatorSelection.setLicenseBond(superuser, previousLicenseBond);
-
-      const candidates = await helper.collatorSelection.getCandidates();
-      let nonce = await helper.chain.getNonce(superuser.address);
-      await Promise.all(candidates.map(candidate =>
-        helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.forceReleaseLicense', [candidate], true, {nonce: nonce++})));
-    });
-  });
-});
deletedjs-packages/tests/src/collator-selection/identity.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/collator-selection/identity.seqtest.ts
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets, requirePalletsOrSkip} from '../util/index.js';
-import {UniqueHelper} from '@unique/playgrounds/src/unique.js';
-
-async function getIdentities(helper: UniqueHelper) {
-  const identities: [string, any][] = [];
-  for(const [key, value] of await helper.getApi().query.identity.identityOf.entries())
-    identities.push([(key as any).toHuman(), (value as any).unwrap()]);
-  return identities;
-}
-
-async function getIdentityAccounts(helper: UniqueHelper) {
-  return (await getIdentities(helper)).flatMap(([key, _value]) => key);
-}
-
-async function getSubIdentityAccounts(helper: UniqueHelper, address: string) {
-  return ((await helper.getApi().query.identity.subsOf(address)).toHuman() as any)[1];
-}
-
-async function getSubIdentityName(helper: UniqueHelper, address: string) {
-  return ((await helper.getApi().query.identity.superOf(address)).toHuman() as any);
-}
-
-describe('Integration Test: Identities Manipulation', () => {
-  let superuser: IKeyringPair;
-
-  before(async function() {
-    if(!process.env.RUN_COLLATOR_TESTS) this.skip();
-
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Identity]);
-      superuser = await privateKey('//Alice');
-    });
-  });
-
-  itSub('Normal calls do not work', async ({helper}) => {
-    // console.error = () => {};
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.identity.setIdentity', [{info: {display: {Raw: 'Meowser'}}}] as any))
-      .to.be.rejectedWith(/Transaction call is not expected/);
-  });
-
-  describe('Identities', () => {
-    itSub('Sets identities', async ({helper}) => {
-      const oldIdentitiesCount = (await getIdentityAccounts(helper)).length;
-
-      const crowdSize = 10;
-      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
-      const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
-
-      expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + crowdSize);
-    });
-
-    itSub('Setting identities does not delete existing but does overwrite', async ({helper}) => {
-      const crowd = await helper.arrange.createCrowd(10, 0n, superuser);
-      const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
-
-      // insert a single identity
-      let singleIdentity = identities.pop()!;
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [[singleIdentity]] as any);
-
-      const oldIdentitiesCount = (await getIdentityAccounts(helper)).length;
-
-      // change an identity and push it with a few new others
-      singleIdentity = [singleIdentity[0], {info: {display: {Raw: 'something special'}}}];
-      identities.push(singleIdentity);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
-
-      // oldIdentitiesCount + 9 because one identity is overwritten, not inserted on top
-      expect((await getIdentityAccounts(helper)).length).to.be.equal(oldIdentitiesCount + 9);
-      expect((await helper.callRpc('api.query.identity.identityOf', [singleIdentity[0]])).toHuman().info.display)
-        .to.be.deep.equal({Raw: 'something special'});
-    });
-
-    itSub('Removes identities', async ({helper}) => {
-      const crowd = await helper.arrange.createCrowd(10, 0n, superuser);
-      const identities = crowd.map((acc, i) => [acc.address, {info: {display: {Raw: `accounter #${i}`}}}]);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
-      const oldIdentities = await getIdentityAccounts(helper);
-
-      // delete a couple, check that they are no longer there
-      const scapegoats = [crowd.pop()!.address, crowd.pop()!.address];
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [scapegoats]);
-      const newIdentities = await getIdentityAccounts(helper);
-      expect(newIdentities.concat(scapegoats)).to.be.have.members(oldIdentities);
-    });
-  });
-
-  describe('Sub-identities', () => {
-    itSub('Sets subs', async ({helper}) => {
-      const crowdSize = 18;
-      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
-      const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!];
-
-      const subsPerSup = crowd.length / supers.length;
-      let subCount = 0;
-      const subs = [
-        crowd.slice(subCount, subCount += subsPerSup + 1),
-        crowd.slice(subCount, subCount += subsPerSup),
-        crowd.slice(subCount, subCount += subsPerSup - 1),
-      ];
-
-      const subsInfo = supers.map((acc, i) => [
-        acc.address, [
-          1000000n + BigInt(i + 1),
-          subs[i].map((sub, j) => [
-            sub.address, {Raw: `accounter #${j}`},
-          ]),
-        ],
-      ]);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any);
-
-      for(let i = 0; i < supers.length; i++) {
-        // check deposit
-        expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i);
-
-        const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address);
-        // check sub-identities as account ids
-        expect(subsAccounts).to.include.members(subs[i].map(x => x.address));
-
-        for(let j = 0; j < subsAccounts.length; j++) {
-          // check sub-identities' names
-          expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`});
-        }
-      }
-    });
-
-    itSub('Setting sub-identities does not delete other existing but does overwrite own', async ({helper}) => {
-      const crowdSize = 18;
-      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
-      const supers = [crowd.pop()!, crowd.pop()!, crowd.pop()!];
-
-      const subsPerSup = crowd.length / supers.length;
-      let subCount = 0;
-      const subs = [
-        crowd.slice(subCount, subCount += subsPerSup + 1),
-        crowd.slice(subCount, subCount += subsPerSup),
-        crowd.slice(subCount, subCount += subsPerSup - 1),
-      ];
-
-      const subsInfo1 = supers.map((acc, i) => [
-        acc.address, [
-          1000000n + BigInt(i + 1),
-          subs[i].map((sub, j) => [
-            sub.address, {Raw: `accounter #${j}`},
-          ]),
-        ],
-      ]);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any);
-
-      // change some sub-identities...
-      subs[2].pop(); subs[2].pop(); subs[2].push(...await helper.arrange.createAccounts([0n], superuser));
-
-      // ...and set them
-      const subsInfo2 = [[
-        supers[2].address, [
-          999999n,
-          subs[2].map((sub, j) => [
-            sub.address, {Raw: `discounter #${j}`},
-          ]),
-        ],
-      ]];
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any);
-
-      // make sure everything else is the same
-      for(let i = 0; i < supers.length - 1; i++) {
-        // check deposit
-        expect(((await helper.getApi().query.identity.subsOf(supers[i].address)).toJSON() as any)[0]).to.be.equal(1000001 + i);
-
-        const subsAccounts = await getSubIdentityAccounts(helper, supers[i].address);
-        // check sub-identities as account ids
-        expect(subsAccounts).to.include.members(subs[i].map(x => x.address));
-
-        for(let j = 0; j < subsAccounts; j++) {
-          // check sub-identities' names
-          expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `accounter #${j}`});
-        }
-      }
-
-      // check deposit
-      expect(((await helper.getApi().query.identity.subsOf(supers[2].address)).toJSON() as any)[0]).to.be.equal(999999);
-
-      const subsAccounts = await getSubIdentityAccounts(helper, supers[2].address);
-      // check sub-identities as account ids
-      expect(subsAccounts).to.include.members(subs[2].map(x => x.address));
-
-      for(let j = 0; j < subsAccounts.length; j++) {
-        // check sub-identities' names
-        expect((await getSubIdentityName(helper, subsAccounts[j]))[1]).to.be.deep.equal({Raw: `discounter #${j}`});
-      }
-    });
-
-    itSub('Removes sub-identities', async ({helper}) => {
-      const crowdSize = 3;
-      const crowd = await helper.arrange.createCrowd(crowdSize, 0n, superuser);
-      const sup = crowd.pop()!;
-
-      const subsInfo1 = [[
-        sup.address, [
-          1000000n,
-          crowd.map((sub, j) => [
-            sub.address, {Raw: `accounter #${j}`},
-          ]),
-        ],
-      ]];
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo1] as any);
-
-      // empty sub-identities should delete the records
-      const subsInfo2 = [[
-        sup.address, [
-          1000000n,
-          [],
-        ],
-      ]];
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo2] as any);
-
-      // check deposit
-      expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]);
-
-      for(let j = 0; j < crowd.length; j++) {
-        // check sub-identities' names
-        expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null;
-      }
-    });
-
-    itSub('Removing identities deletes associated sub-identities', async ({helper}) => {
-      const crowd = await helper.arrange.createCrowd(3, 0n, superuser);
-      const sup = crowd.pop()!;
-
-      // insert identity
-      const identities = [[sup.address, {info: {display: {Raw: 'mental'}}}]];
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceInsertIdentities', [identities] as any);
-
-      // and its sub-identities
-      const subsInfo = [[
-        sup.address, [
-          1000000n,
-          crowd.map((sub, j) => [
-            sub.address, {Raw: `accounter #${j}`},
-          ]),
-        ],
-      ]];
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceSetSubs', [subsInfo] as any);
-
-      // delete top identity
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [[sup.address]]);
-
-      // check that sub-identities are deleted
-      expect((await helper.getApi().query.identity.subsOf(sup.address)).toHuman()).to.be.deep.equal(['0', []]);
-
-      for(let j = 0; j < crowd.length; j++) {
-        // check sub-identities' names
-        expect((await getSubIdentityName(helper, crowd[j].address))).to.be.null;
-      }
-    });
-  });
-
-  after(async function() {
-    if(!process.env.RUN_COLLATOR_TESTS) return;
-
-    await usingPlaygrounds(async helper => {
-      if(helper.fetchMissingPalletNames([Pallets.Identity]).length != 0) return;
-
-      const identitiesToRemove: string[] = await getIdentityAccounts(helper);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.identity.forceRemoveIdentities', [identitiesToRemove]);
-    });
-  });
-});
deletedjs-packages/tests/src/config.tsdiffbeforeafterboth
--- a/js-packages/tests/src/config.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 process from 'process';
-
-const config = {
-  relayUrl: process.env.RELAY_URL || 'ws://127.0.0.1:9844',
-  substrateUrl: process.env.RELAY_OPAL_URL || process.env.RELAY_QUARTZ_URL || process.env.RELAY_UNIQUE_URL || process.env.RELAY_SAPPHIRE_URL || 'ws://127.0.0.1:9944',
-  acalaUrl: process.env.RELAY_ACALA_URL || 'ws://127.0.0.1:9946',
-  karuraUrl: process.env.RELAY_KARURA_URL || 'ws://127.0.0.1:9946',
-  moonbeamUrl: process.env.RELAY_MOONBEAM_URL || 'ws://127.0.0.1:9947',
-  moonriverUrl: process.env.RELAY_MOONRIVER_URL || 'ws://127.0.0.1:9947',
-  astarUrl: process.env.RELAY_ASTAR_URL || 'ws://127.0.0.1:9949',
-  shidenUrl: process.env.RELAY_SHIDEN_URL || 'ws://127.0.0.1:9949',
-  westmintUrl: process.env.RELAY_WESTMINT_URL || 'ws://127.0.0.1:9948',
-  statemineUrl: process.env.RELAY_STATEMINE_URL || 'ws://127.0.0.1:9948',
-  statemintUrl: process.env.RELAY_STATEMINT_URL || 'ws://127.0.0.1:9948',
-  polkadexUrl: process.env.RELAY_POLKADEX_URL || 'ws://127.0.0.1:9950',
-};
-
-export default config;
deletedjs-packages/tests/src/config_docker.tsdiffbeforeafterboth
--- a/js-packages/tests/src/config_docker.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 process from 'process';
-
-const config = {
-  substrateUrl: process.env.substrateUrl || 'ws://blockchain_nodes:9944',
-};
-
-export default config;
deletedjs-packages/tests/src/confirmSponsorship.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/confirmSponsorship.test.ts
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-async function setSponsorHelper(collection: any, signer: IKeyringPair, sponsorAddress: string) {
-  await collection.setSponsor(signer, sponsorAddress);
-  const raw = (await collection.getData())?.raw;
-  expect(raw.sponsorship.Unconfirmed).to.be.equal(sponsorAddress);
-}
-
-async function confirmSponsorHelper(collection: any, signer: IKeyringPair) {
-  await collection.confirmSponsorship(signer);
-  const raw = (await collection.getData())?.raw;
-  expect(raw.sponsorship.Confirmed).to.be.equal(signer.address);
-}
-
-describe('integration test: ext. confirmSponsorship():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-  let zeroBalance: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie, zeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n], donor);
-    });
-  });
-
-  itSub('Confirm collection sponsorship', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await setSponsorHelper(collection, alice, bob.address);
-    await confirmSponsorHelper(collection, bob);
-  });
-
-  itSub('Add sponsor to a collection after the same sponsor was already added and confirmed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await setSponsorHelper(collection, alice, bob.address);
-    await confirmSponsorHelper(collection, bob);
-    await setSponsorHelper(collection, alice, bob.address);
-  });
-  itSub('Add new sponsor to a collection after another sponsor was already added and confirmed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await setSponsorHelper(collection, alice, bob.address);
-    await confirmSponsorHelper(collection, bob);
-    await setSponsorHelper(collection, alice, charlie.address);
-  });
-
-  itSub('NFT: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    const token = await collection.mintToken(alice, {Substrate: zeroBalance.address});
-    await token.transfer(zeroBalance, {Substrate: alice.address});
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
-  });
-
-  itSub('Fungible: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    await collection.mint(alice, 100n, {Substrate: zeroBalance.address});
-    await collection.transfer(zeroBalance, {Substrate: alice.address}, 1n);
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
-  });
-
-  itSub.ifWithPallets('ReFungible: Transfer fees are paid by the sponsor after confirmation', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address});
-    await token.transfer(zeroBalance, {Substrate: alice.address}, 1n);
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
-  });
-
-  itSub('CreateItem fees are paid by the sponsor after confirmation', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
-    await collection.addToAllowList(alice, {Substrate: zeroBalance.address});
-
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
-  });
-
-  itSub('NFT: Sponsoring of transfers is rate limited', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
-      sponsorTransferTimeout: 1000,
-    }});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    await token.transfer(alice, {Substrate: zeroBalance.address});
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address});
-    await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
-  });
-
-  itSub('Fungible: Sponsoring is rate limited', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
-      sponsorTransferTimeout: 1000,
-    }});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-
-    await collection.mint(alice, 100n, {Substrate: zeroBalance.address});
-    await collection.transfer(zeroBalance, {Substrate: zeroBalance.address}, 1n);
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    const transferTx = () => collection.transfer(zeroBalance, {Substrate: zeroBalance.address});
-    await expect(transferTx()).to.be.rejected;
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
-  });
-
-  itSub.ifWithPallets('ReFungible: Sponsoring is rate limited', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
-      sponsorTransferTimeout: 1000,
-    }});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-
-    const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address});
-    await token.transfer(zeroBalance, {Substrate: alice.address});
-
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address});
-    await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
-  });
-
-  itSub('NFT: Sponsoring of createItem is rate limited', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL', limits: {
-      sponsoredDataRateLimit: {blocks: 1000},
-      sponsorTransferTimeout: 1000,
-    }});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    await collection.setPermissions(alice, {mintMode: true, access: 'AllowList'});
-    await collection.addToAllowList(alice, {Substrate: zeroBalance.address});
-
-    await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
-
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    const mintTx = () => collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
-    await expect(mintTx()).to.be.rejectedWith('Inability to pay some fees');
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
-  });
-});
-
-describe('(!negative test!) integration test: ext. confirmSponsorship():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-  let ownerZeroBalance: IKeyringPair;
-  let senderZeroBalance: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie, ownerZeroBalance, senderZeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n, 0n], donor);
-    });
-  });
-
-  itSub('(!negative test!) Confirm sponsorship for a collection that never existed', async ({helper}) => {
-    const collectionId = NON_EXISTENT_COLLECTION_ID;
-    const confirmSponsorshipTx = () => helper.collection.confirmSponsorship(bob, collectionId);
-    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('(!negative test!) Confirm sponsorship using a non-sponsor address', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
-    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
-  });
-
-  itSub('(!negative test!) Confirm sponsorship using owner address', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    const confirmSponsorshipTx = () => collection.confirmSponsorship(alice);
-    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
-  });
-
-  itSub('(!negative test!) Confirm sponsorship by collection admin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.addAdmin(alice, {Substrate: charlie.address});
-    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
-    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
-  });
-
-  itSub('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
-    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
-  });
-
-  itSub('(!negative test!) Confirm sponsorship in a collection that was destroyed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.burn(alice);
-    const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
-    await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    const token = await collection.mintToken(alice, {Substrate: ownerZeroBalance.address});
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    const transferTx = () =>  token.transfer(senderZeroBalance, {Substrate: alice.address});
-    await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
-    const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address);
-    expect(sponsorBalanceAfter).to.equal(sponsorBalanceBefore);
-  });
-});
deletedjs-packages/tests/src/connection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/connection.test.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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';
-
-describe('Connection smoke test', () => {
-  itSub('Connection can be established', async ({helper}) => {
-    const health = (await helper.callRpc('api.rpc.system.health')).toJSON();
-    expect(health).to.be.not.empty;
-  });
-
-  it('Cannot connect to 255.255.255.255', async () => {
-    await expect((async () => {
-      await usingPlaygrounds(async helper => {
-        await helper.callRpc('api.rpc.system.health');
-      }, 'ws://255.255.255.255:9944');
-    })()).to.be.eventually.rejected;
-  });
-});
deletedjs-packages/tests/src/createCollection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/createCollection.test.ts
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js';
-import {CollectionFlag} from '@unique/playgrounds/src/types.js';
-import type {ICollectionCreationOptions, IProperty} from '@unique/playgrounds/src/types.js';
-import {UniqueHelper} from '@unique/playgrounds/src/unique.js';
-
-async function mintCollectionHelper(helper: UniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') {
-  let collection;
-  if(type === 'nft') {
-    collection = await helper.nft.mintCollection(signer, options);
-  } else if(type === 'fungible') {
-    collection = await helper.ft.mintCollection(signer, options, 0);
-  } else {
-    collection = await helper.rft.mintCollection(signer, options);
-  }
-  const data = await collection.getData();
-  expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(signer.address));
-  expect(data?.name).to.be.equal(options.name);
-  expect(data?.description).to.be.equal(options.description);
-  expect(data?.raw.tokenPrefix).to.be.equal(options.tokenPrefix);
-  if(options.properties) {
-    expect(data?.raw.properties).to.be.deep.equal(options.properties);
-  }
-  if(options.adminList) {
-    expect(data?.admins).to.be.deep.equal(options.adminList);
-  }
-
-  if(options.flags) {
-    if((options.flags[0] & 64) != 0)
-      expect(data?.raw.flags.erc721metadata).to.be.true;
-    if((options.flags[0] & 128) != 0)
-      expect(data?.raw.flags.foreign).to.be.false;
-  }
-
-  if(options.tokenPropertyPermissions) {
-    expect(data?.raw.tokenPropertyPermissions).to.be.deep.equal(options.tokenPropertyPermissions);
-  }
-
-  return collection;
-}
-
-describe('integration test: ext. createCollection():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-  itSub('Create new NFT collection', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 'nft');
-  });
-  itSub('Create new NFT collection whith collection_name of maximum length (64 bytes)', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {name: 'A'.repeat(64), description: 'descr', tokenPrefix: 'COL'}, 'nft');
-  });
-  itSub('Create new NFT collection whith collection_description of maximum length (256 bytes)', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {name: 'name', description: 'A'.repeat(256), tokenPrefix: 'COL'}, 'nft');
-  });
-  itSub('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(16)}, 'nft');
-  });
-
-  itSub('Create new Fungible collection', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
-  });
-
-  itSub.ifWithPallets('Create new ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'refungible');
-  });
-
-  itSub('create new collection with properties', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {
-      name: 'name', description: 'descr', tokenPrefix: 'COL',
-      properties: [{key: 'key1', value: 'val1'}],
-      tokenPropertyPermissions: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}],
-    }, 'nft');
-  });
-
-  itSub('create new collection with admin', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {
-      name: 'name', description: 'descr', tokenPrefix: 'COL',
-      adminList: [{Substrate: bob.address}],
-    }, 'nft');
-  });
-
-  itSub('create new collection with flags', async ({helper}) => {
-    await mintCollectionHelper(helper, alice, {
-      name: 'name', description: 'descr', tokenPrefix: 'COL',
-      flags: [CollectionFlag.Erc721metadata],
-    }, 'nft');
-
-    // User can not set Foreign flag itself
-
-    await expect(mintCollectionHelper(helper, alice, {
-      name: 'name', description: 'descr', tokenPrefix: 'COL',
-      flags: [CollectionFlag.Foreign],
-    }, 'nft')).to.be.rejectedWith(/common.NoPermission/);
-
-    await expect(mintCollectionHelper(helper, alice, {
-      name: 'name', description: 'descr', tokenPrefix: 'COL',
-      flags: [CollectionFlag.Erc721metadata, CollectionFlag.Foreign],
-    }, 'nft')).to.be.rejectedWith(/common.NoPermission/);
-  });
-
-  itSub('Create new collection with extra fields', async ({helper}) => {
-    const collection = await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
-    await collection.setPermissions(alice, {access: 'AllowList'});
-    await collection.setLimits(alice, {accountTokenOwnershipLimit: 3});
-    const data = await collection.getData();
-    const limits = await collection.getEffectiveLimits();
-    const raw = data?.raw;
-
-    expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
-    expect(data?.name).to.be.equal('name');
-    expect(data?.description).to.be.equal('descr');
-    expect(raw.permissions.access).to.be.equal('AllowList');
-    expect(raw.mode).to.be.deep.equal({Fungible: '0'});
-    expect(limits.accountTokenOwnershipLimit).to.be.equal(3);
-  });
-
-  itSub('New collection is not external', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
-    const data = await collection.getData();
-    expect(data?.raw.readOnly).to.be.false;
-  });
-});
-
-describe('(!negative test!) integration test: ext. createCollection():', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  itSub('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async ({helper}) => {
-    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'A'.repeat(65), description: 'descr', tokenPrefix: 'COL'});
-    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
-  });
-  itSub('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async ({helper}) => {
-    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'A'.repeat(257), tokenPrefix: 'COL'});
-    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
-  });
-  itSub('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async ({helper}) => {
-    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)});
-    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
-  });
-
-  itSub('(!negative test!) fails when bad limits are set', async ({helper}) => {
-    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}});
-    await expect(mintCollectionTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
-  });
-
-  itSub('(!negative test!) create collection with incorrect property limit (64 elements)', async ({helper}) => {
-    const props: IProperty[] = [];
-
-    for(let i = 0; i < 65; i++) {
-      props.push({key: `key${i}`, value: `value${i}`});
-    }
-    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
-    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
-  });
-
-  itSub('(!negative test!) create collection with incorrect property limit (40 kb)', async ({helper}) => {
-    const props: IProperty[] = [];
-
-    for(let i = 0; i < 32; i++) {
-      props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)});
-    }
-
-    const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
-    await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
-  });
-});
deletedjs-packages/tests/src/createItem.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/createItem.test.ts
+++ /dev/null
@@ -1,272 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, itSub, Pallets} from './util/index.js';
-import type {IProperty, ICrossAccountId} from '@unique/playgrounds/src/types.js';
-import {UniqueHelper} from '@unique/playgrounds/src/unique.js';
-
-async function mintTokenHelper(helper: UniqueHelper, collection: any, signer: IKeyringPair, owner: ICrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) {
-  let token;
-  const itemCountBefore = await helper.collection.getLastTokenId(collection.collectionId);
-  const itemBalanceBefore = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
-  if(type === 'nft') {
-    token = await collection.mintToken(signer, owner, properties);
-  } else if(type === 'fungible') {
-    await collection.mint(signer, 10n, owner);
-  } else {
-    token = await collection.mintToken(signer, 100n, owner, properties);
-  }
-
-  const itemCountAfter = await helper.collection.getLastTokenId(collection.collectionId);
-  const itemBalanceAfter = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
-
-  if(type === 'fungible') {
-    expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);
-  } else {
-    expect(itemCountAfter).to.be.equal(itemCountBefore + 1);
-  }
-
-  return token;
-}
-
-
-describe('integration test: ext. ():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Create new item in NFT collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await mintTokenHelper(helper, collection, alice, {Substrate: alice.address});
-  });
-  itSub('Create new item in Fungible collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-    await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'fungible');
-  });
-  itSub('Check events on create new item in Fungible collection', async ({helper}) => {
-    const {collectionId} = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
-    const to = {Substrate: alice.address};
-    {
-      const createData = {fungible: {value: 100}};
-      const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
-      const result = helper.util.extractTokensFromCreationResult(events);
-      expect(result.tokens[0].amount).to.be.equal(100n);
-      expect(result.tokens[0].collectionId).to.be.equal(collectionId);
-      expect(result.tokens[0].owner).to.be.deep.equal(to);
-    }
-    {
-      const createData = {fungible: {value: 50}};
-      const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
-      const result = helper.util.extractTokensFromCreationResult(events);
-      expect(result.tokens[0].amount).to.be.equal(50n);
-      expect(result.tokens[0].collectionId).to.be.equal(collectionId);
-      expect(result.tokens[0].owner).to.be.deep.equal(to);
-    }
-  });
-  itSub.ifWithPallets('Create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) =>  {
-    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'refungible');
-  });
-  itSub('Create new item in NFT collection with collection admin permissions', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await mintTokenHelper(helper, collection, bob, {Substrate: alice.address});
-  });
-  itSub('Create new item in Fungible collection with collection admin permissions', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'fungible');
-  });
-  itSub.ifWithPallets('Create new item in ReFungible collection with collection admin permissions', [Pallets.ReFungible], async ({helper}) =>  {
-    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'refungible');
-  });
-
-  itSub('Set property Admin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
-      properties: [{key: 'k', value: 'v'}],
-      tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}}],
-    });
-    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
-  });
-
-  itSub('Set property AdminConst', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
-      properties: [{key: 'k', value: 'v'}],
-      tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}],
-    });
-    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
-  });
-
-  itSub('Set property itemOwnerOrAdmin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
-      properties: [{key: 'k', value: 'v'}],
-      tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}],
-    });
-    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
-  });
-
-  itSub('Check total pieces of Fungible token', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-    const amount = 10n;
-    await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'fungible');
-    {
-      const totalPieces = await collection.getTotalPieces();
-      expect(totalPieces).to.be.equal(amount);
-    }
-    await collection.transfer(bob, {Substrate: alice.address}, 1n);
-    {
-      const totalPieces = await collection.getTotalPieces();
-      expect(totalPieces).to.be.equal(amount);
-    }
-  });
-
-  itSub('Check total pieces of NFT token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const amount = 1n;
-    const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address});
-    {
-      const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
-      expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
-    }
-    await token.transfer(bob, {Substrate: alice.address});
-    {
-      const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
-      expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
-    }
-  });
-
-  itSub.ifWithPallets('Check total pieces of ReFungible token', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const amount = 100n;
-    const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'refungible');
-    {
-      const totalPieces = await token.getTotalPieces();
-      expect(totalPieces).to.be.equal(amount);
-    }
-    await token.transfer(bob, {Substrate: alice.address}, 60n);
-    {
-      const totalPieces = await token.getTotalPieces();
-      expect(totalPieces).to.be.equal(amount);
-    }
-  });
-});
-
-describe('Negative integration test: ext. createItem():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Regular user cannot create new item in NFT collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const mintTx = () => collection.mintToken(bob, {Substrate: bob.address});
-    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
-  });
-  itSub('Regular user cannot create new item in Fungible collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-    const mintTx = () => collection.mint(bob, 10n, {Substrate: bob.address});
-    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
-  });
-  itSub.ifWithPallets('Regular user cannot create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const mintTx = () => collection.mintToken(bob, 100n, {Substrate: bob.address});
-    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
-  });
-
-  itSub('No editing rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
-      tokenPropertyPermissions: [{key: 'k', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}],
-    });
-    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
-    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('User doesnt have editing rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
-      tokenPropertyPermissions: [{key: 'k', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}],
-    });
-    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
-    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Adding property without access rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
-    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Adding more than 64 prps', async ({helper}) => {
-    const props: IProperty[] = [];
-
-    for(let i = 0; i < 65; i++) {
-      props.push({key: `key${i}`, value: `value${i}`});
-    }
-
-
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, props);
-    await expect(mintTx()).to.be.rejectedWith('Verification Error');
-  });
-
-  itSub('Trying to add bigger property than allowed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'k1', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
-        {key: 'k2', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
-      ],
-    });
-    const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [
-      {key: 'k1', value: 'vvvvvv'.repeat(5000)},
-      {key: 'k2', value: 'vvv'.repeat(5000)},
-    ]);
-    await expect(mintTx()).to.be.rejectedWith(/common\.NoSpaceForProperty/);
-  });
-
-  itSub('Check total pieces for invalid Fungible token', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
-    const invalidTokenId = 1_000_000;
-    expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
-    expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
-  });
-
-  itSub('Check total pieces for invalid NFT token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const invalidTokenId = 1_000_000;
-    expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
-    expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
-  });
-
-  itSub.ifWithPallets('Check total pieces for invalid Refungible token', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
-    const invalidTokenId = 1_000_000;
-    expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
-    expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
-  });
-});
deletedjs-packages/tests/src/createMultipleItems.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/createMultipleItems.test.ts
+++ /dev/null
@@ -1,374 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js';
-
-describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  itSub('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: '1'}]},
-      {properties: [{key: 'data', value: '2'}]},
-      {properties: [{key: 'data', value: '3'}]},
-    ];
-    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    for(const [i, token] of tokens.entries()) {
-      const tokenData = await token.getData();
-      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
-      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
-    }
-  });
-
-  itSub('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-    const args = [
-      {value: 1n},
-      {value: 2n},
-      {value: 3n},
-    ];
-    await helper.ft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, args, {Substrate: alice.address});
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(6n);
-  });
-
-  itSub.ifWithPallets('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-    const args = [
-      {pieces: 1n},
-      {pieces: 2n},
-      {pieces: 3n},
-    ];
-    const tokens = await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-
-    for(const [i, token] of tokens.entries()) {
-      expect(await token.getBalance({Substrate: alice.address})).to.be.equal(BigInt(i + 1));
-    }
-  });
-
-  itSub('Can mint amount of items equals to collection limits', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      limits: {
-        tokenLimit: 2,
-      },
-    });
-    const args = [{}, {}];
-    await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-  });
-
-  itSub('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}},
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: '1'}]},
-      {properties: [{key: 'data', value: '2'}]},
-      {properties: [{key: 'data', value: '3'}]},
-    ];
-    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    for(const [i, token] of tokens.entries()) {
-      const tokenData = await token.getData();
-      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
-      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
-    }
-  });
-
-  itSub('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}},
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: '1'}]},
-      {properties: [{key: 'data', value: '2'}]},
-      {properties: [{key: 'data', value: '3'}]},
-    ];
-    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    for(const [i, token] of tokens.entries()) {
-      const tokenData = await token.getData();
-      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
-      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
-    }
-  });
-
-  itSub('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: '1'}]},
-      {properties: [{key: 'data', value: '2'}]},
-      {properties: [{key: 'data', value: '3'}]},
-    ];
-    const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    for(const [i, token] of tokens.entries()) {
-      const tokenData = await token.getData();
-      expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: helper.address.normalizeSubstrate(alice.address)});
-      expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
-    }
-  });
-});
-
-describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Regular user cannot create items in active NFT collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-    const args = [
-      {},
-      {},
-    ];
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
-  });
-
-  itSub('Regular user cannot create items in active Fungible collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-    const args = [
-      {value: 1n},
-      {value: 2n},
-      {value: 3n},
-    ];
-    const mintTx = () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, args, {Substrate: alice.address});
-    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
-  });
-
-  itSub.ifWithPallets('Regular user cannot create items in active ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-    const args = [
-      {pieces: 1n},
-      {pieces: 1n},
-      {pieces: 1n},
-    ];
-    const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
-  });
-
-  itSub('Create token in not existing collection', async ({helper}) => {
-    const collectionId = 1_000_000;
-    const args = [
-      {},
-      {},
-    ];
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('Create NFTs that has reached the maximum data limit', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: 'A'.repeat(32769)}]},
-      {properties: [{key: 'data', value: 'B'.repeat(32769)}]},
-      {properties: [{key: 'data', value: 'C'.repeat(32769)}]},
-    ];
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith('Verification Error');
-  });
-
-  itSub.ifWithPallets('Create Refungible tokens that has reached the maximum data limit', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
-      ],
-    });
-    const args = [
-      {pieces: 10n, properties: [{key: 'data', value: 'A'.repeat(32769)}]},
-      {pieces: 10n, properties: [{key: 'data', value: 'B'.repeat(32769)}]},
-      {pieces: 10n, properties: [{key: 'data', value: 'C'.repeat(32769)}]},
-    ];
-    const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith('Verification Error');
-  });
-
-  itSub.ifWithPallets('Create tokens with different types', [Pallets.ReFungible], async ({helper}) => {
-    const {collectionId} = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-
-    const types = ['NFT', 'Fungible', 'ReFungible'];
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.createMultipleItems',
-      [collectionId, {Substrate: alice.address}, types],
-    )).to.be.rejectedWith(/nonfungible\.NotNonfungibleDataUsedToMintFungibleCollectionToken/);
-  });
-
-  itSub('Create tokens with different data limits <> maximum data limit', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: 'A'}]},
-      {properties: [{key: 'data', value: 'B'.repeat(32769)}]},
-    ];
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith('Verification Error');
-  });
-
-  itSub('Fails when minting tokens exceeds collectionLimits amount', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
-      ],
-      limits: {
-        tokenLimit: 1,
-      },
-    });
-    const args = [
-      {},
-      {},
-    ];
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
-  });
-
-  itSub('User doesnt have editing rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: false}},
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: 'A'}]},
-    ];
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Adding property without access rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      properties: [
-        {
-          key: 'data',
-          value: 'v',
-        },
-      ],
-    });
-    const args = [
-      {properties: [{key: 'data', value: 'A'}]},
-    ];
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Adding more than 64 prps', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-    const prps = [];
-
-    for(let i = 0; i < 65; i++) {
-      prps.push({key: `key${i}`, value: `value${i}`});
-    }
-
-    const args = [
-      {properties: prps},
-      {properties: prps},
-      {properties: prps},
-    ];
-
-    const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
-    await expect(mintTx()).to.be.rejectedWith('Verification Error');
-  });
-});
deletedjs-packages/tests/src/createMultipleItemsEx.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/createMultipleItemsEx.test.ts
+++ /dev/null
@@ -1,442 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, expect, Pallets, itSub} from './util/index.js';
-import type {IProperty} from '@unique/playgrounds/src/types.js';
-
-describe('Integration Test: createMultipleItemsEx', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub('can initialize multiple NFT with different owners', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-      },
-      {
-        owner: {Substrate: bob.address},
-      },
-      {
-        owner: {Substrate: charlie.address},
-      },
-    ];
-
-    const tokens = await collection.mintMultipleTokens(alice, args);
-    for(const [i, token] of tokens.entries()) {
-      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
-    }
-  });
-
-  itSub('createMultipleItemsEx with property Admin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {
-          key: 'k',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-            tokenOwner: false,
-          },
-        },
-      ],
-    });
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: [{key: 'k', value: 'v1'}],
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: [{key: 'k', value: 'v2'}],
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: [{key: 'k', value: 'v3'}],
-      },
-    ];
-
-    const tokens = await collection.mintMultipleTokens(alice, args);
-    for(const [i, token] of tokens.entries()) {
-      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
-      expect(await token.getData()).to.not.be.empty;
-    }
-  });
-
-  itSub('createMultipleItemsEx with property AdminConst', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {
-          key: 'k',
-          permission: {
-            mutable: false,
-            collectionAdmin: true,
-            tokenOwner: false,
-          },
-        },
-      ],
-    });
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: [{key: 'k', value: 'v1'}],
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: [{key: 'k', value: 'v2'}],
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: [{key: 'k', value: 'v3'}],
-      },
-    ];
-
-    const tokens = await collection.mintMultipleTokens(alice, args);
-    for(const [i, token] of tokens.entries()) {
-      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
-      expect(await token.getData()).to.not.be.empty;
-    }
-  });
-
-  itSub('createMultipleItemsEx with property itemOwnerOrAdmin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {
-          key: 'k',
-          permission: {
-            mutable: false,
-            collectionAdmin: true,
-            tokenOwner: true,
-          },
-        },
-      ],
-    });
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: [{key: 'k', value: 'v1'}],
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: [{key: 'k', value: 'v2'}],
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: [{key: 'k', value: 'v3'}],
-      },
-    ];
-
-    const tokens = await collection.mintMultipleTokens(alice, args);
-    for(const [i, token] of tokens.entries()) {
-      expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
-      expect(await token.getData()).to.not.be.empty;
-    }
-  });
-
-  itSub('can initialize fungible with multiple owners', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    }, 0);
-
-    await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx',[collection.collectionId, {
-      Fungible: new Map([
-        [JSON.stringify({Substrate: alice.address}), 50],
-        [JSON.stringify({Substrate: bob.address}), 100],
-      ]),
-    }], true);
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(50n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(100n);
-  });
-
-  itSub.ifWithPallets('can initialize an RFT with multiple owners', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}},
-      ],
-    });
-
-    await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, {
-      RefungibleMultipleOwners: {
-        users: new Map([
-          [JSON.stringify({Substrate: alice.address}), 1],
-          [JSON.stringify({Substrate: bob.address}), 2],
-        ]),
-        properties: [
-          {key: 'k', value: 'v'},
-        ],
-      },
-    }], true);
-    const tokenId = await collection.getLastTokenId();
-    expect(tokenId).to.be.equal(1);
-    expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n);
-    expect(await collection.getTokenBalance(1, {Substrate: bob.address})).to.be.equal(2n);
-  });
-
-  itSub.ifWithPallets('can initialize multiple RFTs with the same owner', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {key: 'k', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},
-      ],
-    });
-
-    await helper.executeExtrinsic(alice, 'api.tx.unique.createMultipleItemsEx', [collection.collectionId, {
-      RefungibleMultipleItems: [
-        {
-          user: {Substrate: alice.address}, pieces: 1,
-          properties: [
-            {key: 'k', value: 'v1'},
-          ],
-        },
-        {
-          user: {Substrate: alice.address}, pieces: 3,
-          properties: [
-            {key: 'k', value: 'v2'},
-          ],
-        },
-      ],
-    }], true);
-
-    expect(await collection.getLastTokenId()).to.be.equal(2);
-    expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n);
-    expect(await collection.getTokenBalance(2, {Substrate: alice.address})).to.be.equal(3n);
-
-    const tokenData1 = await helper.rft.getToken(collection.collectionId, 1);
-    expect(tokenData1).to.not.be.null;
-    expect(tokenData1?.properties[0]).to.be.deep.equal({key: 'k', value: 'v1'});
-
-    const tokenData2 = await helper.rft.getToken(collection.collectionId, 2);
-    expect(tokenData2).to.not.be.null;
-    expect(tokenData2?.properties[0]).to.be.deep.equal({key: 'k', value: 'v2'});
-  });
-});
-
-describe('Negative test: createMultipleItemsEx', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub('No editing rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {
-          key: 'k',
-          permission: {
-            mutable: true,
-            collectionAdmin: false,
-            tokenOwner: false,
-          },
-        },
-      ],
-    });
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: [{key: 'k', value: 'v1'}],
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: [{key: 'k', value: 'v2'}],
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: [{key: 'k', value: 'v3'}],
-      },
-    ];
-
-    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('User doesnt have editing rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {
-          key: 'k',
-          permission: {
-            mutable: false,
-            collectionAdmin: false,
-            tokenOwner: false,
-          },
-        },
-      ],
-    });
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: [{key: 'k', value: 'v1'}],
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: [{key: 'k', value: 'v2'}],
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: [{key: 'k', value: 'v3'}],
-      },
-    ];
-
-    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Adding property without access rights', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-    });
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: [{key: 'k', value: 'v1'}],
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: [{key: 'k', value: 'v2'}],
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: [{key: 'k', value: 'v3'}],
-      },
-    ];
-
-    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Adding more than 64 properties', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {
-          key: 'k',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-            tokenOwner: true,
-          },
-        },
-      ],
-    });
-
-    const properties: IProperty[] = [];
-
-    for(let i = 0; i < 65; i++) {
-      properties.push({key: `k${i}`, value: `v${i}`});
-    }
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: properties,
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: properties,
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: properties,
-      },
-    ];
-
-    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error');
-  });
-
-  itSub('Trying to add bigger property than allowed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'name',
-      description: 'descr',
-      tokenPrefix: 'COL',
-      tokenPropertyPermissions: [
-        {
-          key: 'k',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-            tokenOwner: true,
-          },
-        },
-      ],
-    });
-
-    const args = [
-      {
-        owner: {Substrate: alice.address},
-        properties: [{key: 'k', value: 'A'.repeat(32769)}],
-      },
-      {
-        owner: {Substrate: bob.address},
-        properties: [{key: 'k', value: 'A'.repeat(32769)}],
-      },
-      {
-        owner: {Substrate: charlie.address},
-        properties: [{key: 'k', value: 'A'.repeat(32769)}],
-      },
-    ];
-
-    await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error');
-  });
-});
deletedjs-packages/tests/src/creditFeesToTreasury.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/creditFeesToTreasury.seqtest.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {ApiPromise} from '@polkadot/api';
-import {usingPlaygrounds, expect, itSub} from './util/index.js';
-import type {u32} from '@polkadot/types-codec';
-
-const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z';
-const saneMinimumFee = 0.05;
-const saneMaximumFee = 0.5;
-const createCollectionDeposit = 100;
-
-// Skip the inflation block pauses if the block is close to inflation block
-// until the inflation happens
-/*eslint no-async-promise-executor: "off"*/
-function skipInflationBlock(api: ApiPromise): Promise<void> {
-  const promise = new Promise<void>(async (resolve) => {
-    const inflationBlockInterval = api.consts.inflation.inflationBlockInterval as u32;
-    const blockInterval = inflationBlockInterval.toNumber();
-    const unsubscribe = await api.rpc.chain.subscribeNewHeads(head => {
-      const currentBlock = head.number.toNumber();
-      if(currentBlock % blockInterval < blockInterval - 10) {
-        unsubscribe();
-        resolve();
-      } else {
-        console.log(`Skipping inflation block, current block: ${currentBlock}`);
-      }
-    });
-  });
-
-  return promise;
-}
-
-describe('integration test: Fees must be credited to Treasury:', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Total issuance does not change', async ({helper}) => {
-    const api = helper.getApi();
-    await skipInflationBlock(api);
-    await helper.wait.newBlocks(1);
-
-    const totalBefore = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt();
-
-    await helper.balance.transferToSubstrate(alice, bob.address, 1n);
-
-    const totalAfter = (await helper.callRpc('api.query.balances.totalIssuance', [])).toBigInt();
-
-    expect(totalAfter).to.be.equal(totalBefore);
-  });
-
-  itSub('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async ({helper}) => {
-    await skipInflationBlock(helper.getApi());
-    await helper.wait.newBlocks(1);
-
-    const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-
-    const amount = 1n;
-    await helper.balance.transferToSubstrate(alice, bob.address, amount);
-
-    const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-
-    const fee = aliceBalanceBefore - aliceBalanceAfter - amount;
-    const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
-
-    expect(treasuryIncrease).to.be.equal(fee);
-  });
-
-  itSub('Treasury balance increased by failed tx fee', async ({helper}) => {
-    const api = helper.getApi();
-    await helper.wait.newBlocks(1);
-
-    const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await expect(helper.signTransaction(bob, api.tx.balances.forceSetBalance(alice.address, 0))).to.be.rejected;
-
-    const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    const fee = bobBalanceBefore - bobBalanceAfter;
-    const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
-
-    expect(treasuryIncrease).to.be.equal(fee);
-  });
-
-  itSub('NFT Transactions also send fees to Treasury', async ({helper}) => {
-    await skipInflationBlock(helper.getApi());
-    await helper.wait.newBlocks(1);
-
-    const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-
-    await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-    const fee = aliceBalanceBefore - aliceBalanceAfter;
-    const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
-
-    expect(treasuryIncrease).to.be.equal(fee);
-  });
-
-  itSub('Fees are sane', async ({helper}) => {
-    const unique = helper.balance.getOneTokenNominal();
-    await skipInflationBlock(helper.getApi());
-    await helper.wait.newBlocks(1);
-
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-
-    await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-    const fee = aliceBalanceBefore - aliceBalanceAfter;
-
-    expect(fee / unique < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true;
-    expect(fee / unique < BigInt(Math.ceil(saneMinimumFee  + createCollectionDeposit))).to.be.true;
-  });
-
-  itSub('NFT Transfer fee is close to 0.1 Unique', async ({helper}) => {
-    await skipInflationBlock(helper.getApi());
-    await helper.wait.newBlocks(1);
-
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    });
-    // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-    await token.transfer(alice, {Substrate: bob.address});
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-
-    const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal());
-    const expectedTransferFee = 0.1;
-    // fee drifts because of NextFeeMultiplier
-    const tolerance = 0.001;
-
-    expect(Math.abs(fee - expectedTransferFee)).to.be.lessThan(tolerance);
-  });
-});
deletedjs-packages/tests/src/destroyCollection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/destroyCollection.test.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, expect, usingPlaygrounds, Pallets} from './util/index.js';
-
-describe('integration test: ext. destroyCollection():', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  itSub('NFT collection can be destroyed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    });
-    await collection.burn(alice);
-    expect(await collection.getData()).to.be.null;
-  });
-  itSub('Fungible collection can be destroyed', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    }, 0);
-    await collection.burn(alice);
-    expect(await collection.getData()).to.be.null;
-  });
-  itSub.ifWithPallets('ReFungible collection can be destroyed', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    });
-    await collection.burn(alice);
-    expect(await collection.getData()).to.be.null;
-  });
-});
-
-describe('(!negative test!) integration test: ext. destroyCollection():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('(!negative test!) Destroy a collection that never existed', async ({helper}) => {
-    const collectionId = 1_000_000;
-    await expect(helper.collection.burn(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-  itSub('(!negative test!) Destroy a collection that has already been destroyed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    });
-    await collection.burn(alice);
-    await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-  itSub('(!negative test!) Destroy a collection using non-owner account', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    });
-    await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/);
-  });
-  itSub('(!negative test!) Destroy a collection using collection admin account', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    });
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/);
-  });
-  itSub('fails when OwnerCanDestroy == false', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-      limits: {
-        ownerCanDestroy: false,
-      },
-    });
-    await expect(collection.burn(alice)).to.be.rejectedWith(/common\.NoPermission/);
-  });
-  itSub('fails when a collection still has a token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-    });
-    await collection.mintToken(alice, {Substrate: alice.address});
-    await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CantDestroyNotEmptyCollection/);
-  });
-});
deletedjs-packages/tests/src/enableDisableTransfer.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/enableDisableTransfer.test.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-
-describe('Enable/Disable Transfers', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('User can transfer token with enabled transfer flag', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-      limits: {
-        transfersEnabled: true,
-      },
-    });
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    await token.transfer(alice, {Substrate: bob.address});
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-
-  itSub('User can\'n transfer token with disabled transfer flag', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-      limits: {
-        transfersEnabled: false,
-      },
-    });
-    const token = await collection.mintToken(alice, {Substrate: alice.address});
-    await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TransferNotAllowed/);
-  });
-});
-
-describe('Negative Enable/Disable Transfers', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('Non-owner cannot change transfer flag', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test',
-      description: 'test',
-      tokenPrefix: 'test',
-      limits: {
-        transfersEnabled: true,
-      },
-    });
-
-    await expect(collection.setLimits(bob, {transfersEnabled: false})).to.be.rejectedWith(/common\.NoPermission/);
-  });
-});
deletedjs-packages/tests/src/eth/abi/collectionHelpers.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/collectionHelpers.json
+++ /dev/null
@@ -1,267 +0,0 @@
-[
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "collectionId",
-        "type": "address"
-      }
-    ],
-    "name": "CollectionChanged",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "collectionId",
-        "type": "address"
-      }
-    ],
-    "name": "CollectionCreated",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "collectionId",
-        "type": "address"
-      }
-    ],
-    "name": "CollectionDestroyed",
-    "type": "event"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint32", "name": "collectionId", "type": "uint32" }
-    ],
-    "name": "collectionAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionCreationFee",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "collectionAddress",
-        "type": "address"
-      }
-    ],
-    "name": "collectionId",
-    "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "name", "type": "string" },
-          { "internalType": "string", "name": "description", "type": "string" },
-          {
-            "internalType": "string",
-            "name": "token_prefix",
-            "type": "string"
-          },
-          {
-            "internalType": "enum CollectionMode",
-            "name": "mode",
-            "type": "uint8"
-          },
-          { "internalType": "uint8", "name": "decimals", "type": "uint8" },
-          {
-            "components": [
-              { "internalType": "string", "name": "key", "type": "string" },
-              { "internalType": "bytes", "name": "value", "type": "bytes" }
-            ],
-            "internalType": "struct Property[]",
-            "name": "properties",
-            "type": "tuple[]"
-          },
-          {
-            "components": [
-              { "internalType": "string", "name": "key", "type": "string" },
-              {
-                "components": [
-                  {
-                    "internalType": "enum TokenPermissionField",
-                    "name": "code",
-                    "type": "uint8"
-                  },
-                  { "internalType": "bool", "name": "value", "type": "bool" }
-                ],
-                "internalType": "struct PropertyPermission[]",
-                "name": "permissions",
-                "type": "tuple[]"
-              }
-            ],
-            "internalType": "struct TokenPropertyPermission[]",
-            "name": "token_property_permissions",
-            "type": "tuple[]"
-          },
-          {
-            "components": [
-              { "internalType": "address", "name": "eth", "type": "address" },
-              { "internalType": "uint256", "name": "sub", "type": "uint256" }
-            ],
-            "internalType": "struct CrossAddress[]",
-            "name": "admin_list",
-            "type": "tuple[]"
-          },
-          {
-            "components": [
-              { "internalType": "bool", "name": "token_owner", "type": "bool" },
-              {
-                "internalType": "bool",
-                "name": "collection_admin",
-                "type": "bool"
-              },
-              {
-                "internalType": "address[]",
-                "name": "restricted",
-                "type": "address[]"
-              }
-            ],
-            "internalType": "struct CollectionNestingAndPermission",
-            "name": "nesting_settings",
-            "type": "tuple"
-          },
-          {
-            "components": [
-              {
-                "internalType": "enum CollectionLimitField",
-                "name": "field",
-                "type": "uint8"
-              },
-              { "internalType": "uint256", "name": "value", "type": "uint256" }
-            ],
-            "internalType": "struct CollectionLimitValue[]",
-            "name": "limits",
-            "type": "tuple[]"
-          },
-          {
-            "components": [
-              { "internalType": "address", "name": "eth", "type": "address" },
-              { "internalType": "uint256", "name": "sub", "type": "uint256" }
-            ],
-            "internalType": "struct CrossAddress",
-            "name": "pending_sponsor",
-            "type": "tuple"
-          },
-          {
-            "internalType": "CollectionFlags",
-            "name": "flags",
-            "type": "uint8"
-          }
-        ],
-        "internalType": "struct CreateCollectionData",
-        "name": "data",
-        "type": "tuple"
-      }
-    ],
-    "name": "createCollection",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string", "name": "name", "type": "string" },
-      { "internalType": "uint8", "name": "decimals", "type": "uint8" },
-      { "internalType": "string", "name": "description", "type": "string" },
-      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
-    ],
-    "name": "createFTCollection",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string", "name": "name", "type": "string" },
-      { "internalType": "string", "name": "description", "type": "string" },
-      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
-    ],
-    "name": "createNFTCollection",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string", "name": "name", "type": "string" },
-      { "internalType": "string", "name": "description", "type": "string" },
-      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
-    ],
-    "name": "createRFTCollection",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "collectionAddress",
-        "type": "address"
-      }
-    ],
-    "name": "destroyCollection",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "collectionAddress",
-        "type": "address"
-      }
-    ],
-    "name": "isCollectionExist",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "collection", "type": "address" },
-      { "internalType": "string", "name": "baseUri", "type": "string" }
-    ],
-    "name": "makeCollectionERC721MetadataCompatible",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
-    ],
-    "name": "supportsInterface",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/contractHelpers.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/contractHelpers.json
+++ /dev/null
@@ -1,326 +0,0 @@
-[
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "ContractSponsorRemoved",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "sponsor",
-        "type": "address"
-      }
-    ],
-    "name": "ContractSponsorSet",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "address",
-        "name": "sponsor",
-        "type": "address"
-      }
-    ],
-    "name": "ContractSponsorshipConfirmed",
-    "type": "event"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "allowed",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "allowlistEnabled",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "confirmSponsorship",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "contractOwner",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "hasPendingSponsor",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "hasSponsor",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "removeSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "selfSponsoredEnable",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      { "internalType": "address", "name": "sponsor", "type": "address" }
-    ],
-    "name": "setSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      { "internalType": "uint256", "name": "feeLimit", "type": "uint256" }
-    ],
-    "name": "setSponsoringFeeLimit",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      {
-        "internalType": "enum SponsoringModeT",
-        "name": "mode",
-        "type": "uint8"
-      }
-    ],
-    "name": "setSponsoringMode",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      { "internalType": "uint32", "name": "rateLimit", "type": "uint32" }
-    ],
-    "name": "setSponsoringRateLimit",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "sponsor",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "status", "type": "bool" },
-          {
-            "components": [
-              { "internalType": "address", "name": "eth", "type": "address" },
-              { "internalType": "uint256", "name": "sub", "type": "uint256" }
-            ],
-            "internalType": "struct CrossAddress",
-            "name": "value",
-            "type": "tuple"
-          }
-        ],
-        "internalType": "struct OptionCrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "sponsoringEnabled",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "sponsoringFeeLimit",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      }
-    ],
-    "name": "sponsoringRateLimit",
-    "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
-    ],
-    "name": "supportsInterface",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      { "internalType": "address", "name": "user", "type": "address" },
-      { "internalType": "bool", "name": "isAllowed", "type": "bool" }
-    ],
-    "name": "toggleAllowed",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "internalType": "address",
-        "name": "contractAddress",
-        "type": "address"
-      },
-      { "internalType": "bool", "name": "enabled", "type": "bool" }
-    ],
-    "name": "toggleAllowlist",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/fungible.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/fungible.json
+++ /dev/null
@@ -1,722 +0,0 @@
-[
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "spender",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "value",
-        "type": "uint256"
-      }
-    ],
-    "name": "Approval",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "from",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "to",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "value",
-        "type": "uint256"
-      }
-    ],
-    "name": "Transfer",
-    "type": "event"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "newAdmin",
-        "type": "tuple"
-      }
-    ],
-    "name": "addCollectionAdminCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "addToCollectionAllowListCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" },
-      { "internalType": "address", "name": "spender", "type": "address" }
-    ],
-    "name": "allowance",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "owner",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "spender",
-        "type": "tuple"
-      }
-    ],
-    "name": "allowanceCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "allowlistedCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "spender", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "approve",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "spender",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "approveCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" }
-    ],
-    "name": "balanceOf",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "owner",
-        "type": "tuple"
-      }
-    ],
-    "name": "balanceOfCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "burnFromCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "newOwner",
-        "type": "tuple"
-      }
-    ],
-    "name": "changeCollectionOwnerCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionAdmins",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionHelperAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionLimits",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionLimitField",
-            "name": "field",
-            "type": "uint8"
-          },
-          {
-            "components": [
-              { "internalType": "bool", "name": "status", "type": "bool" },
-              { "internalType": "uint256", "name": "value", "type": "uint256" }
-            ],
-            "internalType": "struct OptionUint256",
-            "name": "value",
-            "type": "tuple"
-          }
-        ],
-        "internalType": "struct CollectionLimit[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNesting",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          {
-            "internalType": "bool",
-            "name": "collection_admin",
-            "type": "bool"
-          },
-          {
-            "internalType": "address[]",
-            "name": "restricted",
-            "type": "address[]"
-          }
-        ],
-        "internalType": "struct CollectionNestingAndPermission",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionOwner",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "collectionProperties",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
-    "name": "collectionProperty",
-    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionSponsor",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "confirmCollectionSponsorship",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "contractAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "decimals",
-    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "deleteCollectionProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "description",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "hasCollectionPendingSponsor",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "isOwnerOrAdminCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "mint",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "to", "type": "address" },
-          { "internalType": "uint256", "name": "amount", "type": "uint256" }
-        ],
-        "internalType": "struct AmountForAddress[]",
-        "name": "amounts",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "mintBulk",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "mintCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "name",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "admin",
-        "type": "tuple"
-      }
-    ],
-    "name": "removeCollectionAdminCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "removeCollectionSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "removeFromCollectionAllowListCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
-    ],
-    "name": "setCollectionAccess",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionLimitField",
-            "name": "field",
-            "type": "uint8"
-          },
-          {
-            "components": [
-              { "internalType": "bool", "name": "status", "type": "bool" },
-              { "internalType": "uint256", "name": "value", "type": "uint256" }
-            ],
-            "internalType": "struct OptionUint256",
-            "name": "value",
-            "type": "tuple"
-          }
-        ],
-        "internalType": "struct CollectionLimit",
-        "name": "limit",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionLimit",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],
-    "name": "setCollectionMintMode",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          {
-            "internalType": "bool",
-            "name": "collection_admin",
-            "type": "bool"
-          },
-          {
-            "internalType": "address[]",
-            "name": "restricted",
-            "type": "address[]"
-          }
-        ],
-        "internalType": "struct CollectionNestingAndPermission",
-        "name": "collectionNestingAndPermissions",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "properties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "setCollectionProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "sponsor",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionSponsorCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
-    ],
-    "name": "supportsInterface",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "symbol",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "totalSupply",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transfer",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferFrom",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferFromCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "uniqueCollectionType",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/fungibleDeprecated.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/fungibleDeprecated.json
+++ /dev/null
@@ -1,151 +0,0 @@
-[
-  {
-    "inputs": [
-      { "internalType": "address", "name": "newAdmin", "type": "address" }
-    ],
-    "name": "addCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "addToCollectionAllowList",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "burnFrom",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "newOwner", "type": "address" }
-    ],
-    "name": "changeCollectionOwner",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
-    "name": "deleteCollectionProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "isOwnerOrAdmin",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "admin", "type": "address" }
-    ],
-    "name": "removeCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "removeFromCollectionAllowList",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string", "name": "key", "type": "string" },
-      { "internalType": "bytes", "name": "value", "type": "bytes" }
-    ],
-    "name": "setCollectionProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "sponsor", "type": "address" }
-    ],
-    "name": "setCollectionSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNestingPermissions",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionPermissionField",
-            "name": "field",
-            "type": "uint8"
-          },
-          { "internalType": "bool", "name": "value", "type": "bool" }
-        ],
-        "internalType": "struct CollectionNestingPermission[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNestingRestrictedCollectionIds",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }
-        ],
-        "internalType": "struct CollectionNesting",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bool", "name": "enable", "type": "bool" },
-      {
-        "internalType": "address[]",
-        "name": "collections",
-        "type": "address[]"
-      }
-    ],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/nativeFungible.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/nativeFungible.json
+++ /dev/null
@@ -1,201 +0,0 @@
-[
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "spender",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "value",
-        "type": "uint256"
-      }
-    ],
-    "name": "Approval",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "from",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "to",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "value",
-        "type": "uint256"
-      }
-    ],
-    "name": "Transfer",
-    "type": "event"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" },
-      { "internalType": "address", "name": "spender", "type": "address" }
-    ],
-    "name": "allowance",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "spender", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "approve",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" }
-    ],
-    "name": "balanceOf",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "owner",
-        "type": "tuple"
-      }
-    ],
-    "name": "balanceOfCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "decimals",
-    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "name",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
-    ],
-    "name": "supportsInterface",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "symbol",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "totalSupply",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transfer",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferFrom",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferFromCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/nonFungible.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/nonFungible.json
+++ /dev/null
@@ -1,988 +0,0 @@
-[
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "approved",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "uint256",
-        "name": "tokenId",
-        "type": "uint256"
-      }
-    ],
-    "name": "Approval",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "operator",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "bool",
-        "name": "approved",
-        "type": "bool"
-      }
-    ],
-    "name": "ApprovalForAll",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "uint256",
-        "name": "tokenId",
-        "type": "uint256"
-      }
-    ],
-    "name": "TokenChanged",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "from",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "to",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "uint256",
-        "name": "tokenId",
-        "type": "uint256"
-      }
-    ],
-    "name": "Transfer",
-    "type": "event"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "newAdmin",
-        "type": "tuple"
-      }
-    ],
-    "name": "addCollectionAdminCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "addToCollectionAllowListCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "allowlistedCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "approved", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "approve",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "approved",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "approveCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" }
-    ],
-    "name": "balanceOf",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "owner",
-        "type": "tuple"
-      }
-    ],
-    "name": "balanceOfCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "burn",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "burnFromCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "newOwner",
-        "type": "tuple"
-      }
-    ],
-    "name": "changeCollectionOwnerCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionAdmins",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionHelperAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionLimits",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionLimitField",
-            "name": "field",
-            "type": "uint8"
-          },
-          {
-            "components": [
-              { "internalType": "bool", "name": "status", "type": "bool" },
-              { "internalType": "uint256", "name": "value", "type": "uint256" }
-            ],
-            "internalType": "struct OptionUint256",
-            "name": "value",
-            "type": "tuple"
-          }
-        ],
-        "internalType": "struct CollectionLimit[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNesting",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          {
-            "internalType": "bool",
-            "name": "collection_admin",
-            "type": "bool"
-          },
-          {
-            "internalType": "address[]",
-            "name": "restricted",
-            "type": "address[]"
-          }
-        ],
-        "internalType": "struct CollectionNestingAndPermission",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionOwner",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "collectionProperties",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
-    "name": "collectionProperty",
-    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionSponsor",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "confirmCollectionSponsorship",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "contractAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "deleteCollectionProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "deleteProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "description",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "getApproved",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "hasCollectionPendingSponsor",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" },
-      { "internalType": "address", "name": "operator", "type": "address" }
-    ],
-    "name": "isApprovedForAll",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "isOwnerOrAdminCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],
-    "name": "mint",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          {
-            "components": [
-              { "internalType": "address", "name": "eth", "type": "address" },
-              { "internalType": "uint256", "name": "sub", "type": "uint256" }
-            ],
-            "internalType": "struct CrossAddress",
-            "name": "owner",
-            "type": "tuple"
-          },
-          {
-            "components": [
-              { "internalType": "string", "name": "key", "type": "string" },
-              { "internalType": "bytes", "name": "value", "type": "bytes" }
-            ],
-            "internalType": "struct Property[]",
-            "name": "properties",
-            "type": "tuple[]"
-          }
-        ],
-        "internalType": "struct MintTokenData[]",
-        "name": "data",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "mintBulkCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "properties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "mintCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "string", "name": "tokenUri", "type": "string" }
-    ],
-    "name": "mintWithTokenURI",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "name",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "nextTokenId",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "ownerOf",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "ownerOfCross",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "properties",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string", "name": "key", "type": "string" }
-    ],
-    "name": "property",
-    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "admin",
-        "type": "tuple"
-      }
-    ],
-    "name": "removeCollectionAdminCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "removeCollectionSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "removeFromCollectionAllowListCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "safeTransferFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "bytes", "name": "data", "type": "bytes" }
-    ],
-    "name": "safeTransferFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "operator", "type": "address" },
-      { "internalType": "bool", "name": "approved", "type": "bool" }
-    ],
-    "name": "setApprovalForAll",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
-    ],
-    "name": "setCollectionAccess",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionLimitField",
-            "name": "field",
-            "type": "uint8"
-          },
-          {
-            "components": [
-              { "internalType": "bool", "name": "status", "type": "bool" },
-              { "internalType": "uint256", "name": "value", "type": "uint256" }
-            ],
-            "internalType": "struct OptionUint256",
-            "name": "value",
-            "type": "tuple"
-          }
-        ],
-        "internalType": "struct CollectionLimit",
-        "name": "limit",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionLimit",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],
-    "name": "setCollectionMintMode",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          {
-            "internalType": "bool",
-            "name": "collection_admin",
-            "type": "bool"
-          },
-          {
-            "internalType": "address[]",
-            "name": "restricted",
-            "type": "address[]"
-          }
-        ],
-        "internalType": "struct CollectionNestingAndPermission",
-        "name": "collectionNestingAndPermissions",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "properties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "setCollectionProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "sponsor",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionSponsorCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "properties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "setProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          {
-            "components": [
-              {
-                "internalType": "enum TokenPermissionField",
-                "name": "code",
-                "type": "uint8"
-              },
-              { "internalType": "bool", "name": "value", "type": "bool" }
-            ],
-            "internalType": "struct PropertyPermission[]",
-            "name": "permissions",
-            "type": "tuple[]"
-          }
-        ],
-        "internalType": "struct TokenPropertyPermission[]",
-        "name": "permissions",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "setTokenPropertyPermissions",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
-    ],
-    "name": "supportsInterface",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "symbol",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "index", "type": "uint256" }
-    ],
-    "name": "tokenByIndex",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" },
-      { "internalType": "uint256", "name": "index", "type": "uint256" }
-    ],
-    "name": "tokenOfOwnerByIndex",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "tokenPropertyPermissions",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          {
-            "components": [
-              {
-                "internalType": "enum TokenPermissionField",
-                "name": "code",
-                "type": "uint8"
-              },
-              { "internalType": "bool", "name": "value", "type": "bool" }
-            ],
-            "internalType": "struct PropertyPermission[]",
-            "name": "permissions",
-            "type": "tuple[]"
-          }
-        ],
-        "internalType": "struct TokenPropertyPermission[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "tokenURI",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "totalSupply",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transfer",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transferCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transferFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transferFromCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "uniqueCollectionType",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/nonFungibleDeprecated.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/nonFungibleDeprecated.json
+++ /dev/null
@@ -1,172 +0,0 @@
-[
-  {
-    "inputs": [
-      { "internalType": "address", "name": "newAdmin", "type": "address" }
-    ],
-    "name": "addCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "addToCollectionAllowList",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "burnFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
-    "name": "deleteCollectionProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "isOwnerOrAdmin",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "admin", "type": "address" }
-    ],
-    "name": "removeCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "removeFromCollectionAllowList",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string", "name": "key", "type": "string" },
-      { "internalType": "bytes", "name": "value", "type": "bytes" }
-    ],
-    "name": "setCollectionProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "sponsor", "type": "address" }
-    ],
-    "name": "setCollectionSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string", "name": "key", "type": "string" },
-      { "internalType": "bytes", "name": "value", "type": "bytes" }
-    ],
-    "name": "setProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "newOwner", "type": "address" }
-    ],
-    "name": "changeCollectionOwner",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string", "name": "key", "type": "string" }
-    ],
-    "name": "deleteProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNestingPermissions",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionPermissionField",
-            "name": "field",
-            "type": "uint8"
-          },
-          { "internalType": "bool", "name": "value", "type": "bool" }
-        ],
-        "internalType": "struct CollectionNestingPermission[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNestingRestrictedCollectionIds",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }
-        ],
-        "internalType": "struct CollectionNesting",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bool", "name": "enable", "type": "bool" },
-      {
-        "internalType": "address[]",
-        "name": "collections",
-        "type": "address[]"
-      }
-    ],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/reFungible.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/reFungible.json
+++ /dev/null
@@ -1,995 +0,0 @@
-[
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "approved",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "uint256",
-        "name": "tokenId",
-        "type": "uint256"
-      }
-    ],
-    "name": "Approval",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "operator",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "bool",
-        "name": "approved",
-        "type": "bool"
-      }
-    ],
-    "name": "ApprovalForAll",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "uint256",
-        "name": "tokenId",
-        "type": "uint256"
-      }
-    ],
-    "name": "TokenChanged",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "from",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "to",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "uint256",
-        "name": "tokenId",
-        "type": "uint256"
-      }
-    ],
-    "name": "Transfer",
-    "type": "event"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "newAdmin",
-        "type": "tuple"
-      }
-    ],
-    "name": "addCollectionAdminCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "addToCollectionAllowListCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "allowlistedCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "approved", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "approve",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" }
-    ],
-    "name": "balanceOf",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "owner",
-        "type": "tuple"
-      }
-    ],
-    "name": "balanceOfCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "burn",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "burnFromCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "newOwner",
-        "type": "tuple"
-      }
-    ],
-    "name": "changeCollectionOwnerCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionAdmins",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionHelperAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionLimits",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionLimitField",
-            "name": "field",
-            "type": "uint8"
-          },
-          {
-            "components": [
-              { "internalType": "bool", "name": "status", "type": "bool" },
-              { "internalType": "uint256", "name": "value", "type": "uint256" }
-            ],
-            "internalType": "struct OptionUint256",
-            "name": "value",
-            "type": "tuple"
-          }
-        ],
-        "internalType": "struct CollectionLimit[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNesting",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          {
-            "internalType": "bool",
-            "name": "collection_admin",
-            "type": "bool"
-          },
-          {
-            "internalType": "address[]",
-            "name": "restricted",
-            "type": "address[]"
-          }
-        ],
-        "internalType": "struct CollectionNestingAndPermission",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionOwner",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "collectionProperties",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
-    "name": "collectionProperty",
-    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionSponsor",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "confirmCollectionSponsorship",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "contractAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "deleteCollectionProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "deleteProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "description",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "getApproved",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "hasCollectionPendingSponsor",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" },
-      { "internalType": "address", "name": "operator", "type": "address" }
-    ],
-    "name": "isApprovedForAll",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "isOwnerOrAdminCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],
-    "name": "mint",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          {
-            "components": [
-              {
-                "components": [
-                  {
-                    "internalType": "address",
-                    "name": "eth",
-                    "type": "address"
-                  },
-                  {
-                    "internalType": "uint256",
-                    "name": "sub",
-                    "type": "uint256"
-                  }
-                ],
-                "internalType": "struct CrossAddress",
-                "name": "owner",
-                "type": "tuple"
-              },
-              { "internalType": "uint128", "name": "pieces", "type": "uint128" }
-            ],
-            "internalType": "struct OwnerPieces[]",
-            "name": "owners",
-            "type": "tuple[]"
-          },
-          {
-            "components": [
-              { "internalType": "string", "name": "key", "type": "string" },
-              { "internalType": "bytes", "name": "value", "type": "bytes" }
-            ],
-            "internalType": "struct Property[]",
-            "name": "properties",
-            "type": "tuple[]"
-          }
-        ],
-        "internalType": "struct MintTokenData[]",
-        "name": "tokenProperties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "mintBulkCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "properties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "mintCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "string", "name": "tokenUri", "type": "string" }
-    ],
-    "name": "mintWithTokenURI",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "name",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "nextTokenId",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "ownerOf",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "ownerOfCross",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string[]", "name": "keys", "type": "string[]" }
-    ],
-    "name": "properties",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string", "name": "key", "type": "string" }
-    ],
-    "name": "property",
-    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "admin",
-        "type": "tuple"
-      }
-    ],
-    "name": "removeCollectionAdminCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "removeCollectionSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "user",
-        "type": "tuple"
-      }
-    ],
-    "name": "removeFromCollectionAllowListCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "safeTransferFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "bytes", "name": "data", "type": "bytes" }
-    ],
-    "name": "safeTransferFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "operator", "type": "address" },
-      { "internalType": "bool", "name": "approved", "type": "bool" }
-    ],
-    "name": "setApprovalForAll",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }
-    ],
-    "name": "setCollectionAccess",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionLimitField",
-            "name": "field",
-            "type": "uint8"
-          },
-          {
-            "components": [
-              { "internalType": "bool", "name": "status", "type": "bool" },
-              { "internalType": "uint256", "name": "value", "type": "uint256" }
-            ],
-            "internalType": "struct OptionUint256",
-            "name": "value",
-            "type": "tuple"
-          }
-        ],
-        "internalType": "struct CollectionLimit",
-        "name": "limit",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionLimit",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],
-    "name": "setCollectionMintMode",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          {
-            "internalType": "bool",
-            "name": "collection_admin",
-            "type": "bool"
-          },
-          {
-            "internalType": "address[]",
-            "name": "restricted",
-            "type": "address[]"
-          }
-        ],
-        "internalType": "struct CollectionNestingAndPermission",
-        "name": "collectionNestingAndPermissions",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "properties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "setCollectionProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "sponsor",
-        "type": "tuple"
-      }
-    ],
-    "name": "setCollectionSponsorCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          { "internalType": "bytes", "name": "value", "type": "bytes" }
-        ],
-        "internalType": "struct Property[]",
-        "name": "properties",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "setProperties",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          {
-            "components": [
-              {
-                "internalType": "enum TokenPermissionField",
-                "name": "code",
-                "type": "uint8"
-              },
-              { "internalType": "bool", "name": "value", "type": "bool" }
-            ],
-            "internalType": "struct PropertyPermission[]",
-            "name": "permissions",
-            "type": "tuple[]"
-          }
-        ],
-        "internalType": "struct TokenPropertyPermission[]",
-        "name": "permissions",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "setTokenPropertyPermissions",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
-    ],
-    "name": "supportsInterface",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "symbol",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "index", "type": "uint256" }
-    ],
-    "name": "tokenByIndex",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "token", "type": "uint256" }
-    ],
-    "name": "tokenContractAddress",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" },
-      { "internalType": "uint256", "name": "index", "type": "uint256" }
-    ],
-    "name": "tokenOfOwnerByIndex",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "tokenPropertyPermissions",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "string", "name": "key", "type": "string" },
-          {
-            "components": [
-              {
-                "internalType": "enum TokenPermissionField",
-                "name": "code",
-                "type": "uint8"
-              },
-              { "internalType": "bool", "name": "value", "type": "bool" }
-            ],
-            "internalType": "struct PropertyPermission[]",
-            "name": "permissions",
-            "type": "tuple[]"
-          }
-        ],
-        "internalType": "struct TokenPropertyPermission[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "tokenURI",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "totalSupply",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transfer",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transferCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transferFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "transferFromCross",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "uniqueCollectionType",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/reFungibleDeprecated.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/reFungibleDeprecated.json
+++ /dev/null
@@ -1,200 +0,0 @@
-[
-  {
-    "inputs": [
-      { "internalType": "address", "name": "newAdmin", "type": "address" }
-    ],
-    "name": "addCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "addToCollectionAllowList",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "burnFrom",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
-    "name": "deleteCollectionProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "isOwnerOrAdmin",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "admin", "type": "address" }
-    ],
-    "name": "removeCollectionAdmin",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "user", "type": "address" }
-    ],
-    "name": "removeFromCollectionAllowList",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "string", "name": "key", "type": "string" },
-      { "internalType": "bytes", "name": "value", "type": "bytes" }
-    ],
-    "name": "setCollectionProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "sponsor", "type": "address" }
-    ],
-    "name": "setCollectionSponsor",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string", "name": "key", "type": "string" },
-      { "internalType": "bytes", "name": "value", "type": "bytes" }
-    ],
-    "name": "setProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "newOwner", "type": "address" }
-    ],
-    "name": "changeCollectionOwner",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
-      { "internalType": "string", "name": "key", "type": "string" }
-    ],
-    "name": "deleteProperty",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" }
-    ],
-    "name": "mintBulk",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      {
-        "components": [
-          { "internalType": "uint256", "name": "field_0", "type": "uint256" },
-          { "internalType": "string", "name": "field_1", "type": "string" }
-        ],
-        "internalType": "struct Tuple0[]",
-        "name": "tokens",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "mintBulkWithTokenURI",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNestingPermissions",
-    "outputs": [
-      {
-        "components": [
-          {
-            "internalType": "enum CollectionPermissionField",
-            "name": "field",
-            "type": "uint8"
-          },
-          { "internalType": "bool", "name": "value", "type": "bool" }
-        ],
-        "internalType": "struct CollectionNestingPermission[]",
-        "name": "",
-        "type": "tuple[]"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "collectionNestingRestrictedCollectionIds",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "bool", "name": "token_owner", "type": "bool" },
-          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }
-        ],
-        "internalType": "struct CollectionNesting",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bool", "name": "enable", "type": "bool" },
-      {
-        "internalType": "address[]",
-        "name": "collections",
-        "type": "address[]"
-      }
-    ],
-    "name": "setCollectionNesting",
-    "outputs": [],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/reFungibleToken.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/reFungibleToken.json
+++ /dev/null
@@ -1,286 +0,0 @@
-[
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "owner",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "spender",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "value",
-        "type": "uint256"
-      }
-    ],
-    "name": "Approval",
-    "type": "event"
-  },
-  {
-    "anonymous": false,
-    "inputs": [
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "from",
-        "type": "address"
-      },
-      {
-        "indexed": true,
-        "internalType": "address",
-        "name": "to",
-        "type": "address"
-      },
-      {
-        "indexed": false,
-        "internalType": "uint256",
-        "name": "value",
-        "type": "uint256"
-      }
-    ],
-    "name": "Transfer",
-    "type": "event"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" },
-      { "internalType": "address", "name": "spender", "type": "address" }
-    ],
-    "name": "allowance",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "owner",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "spender",
-        "type": "tuple"
-      }
-    ],
-    "name": "allowanceCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "spender", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "approve",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "spender",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "approveCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "owner", "type": "address" }
-    ],
-    "name": "balanceOf",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "owner",
-        "type": "tuple"
-      }
-    ],
-    "name": "balanceOfCross",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "burnFromCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "decimals",
-    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "name",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "parentToken",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "parentTokenId",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "repartition",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
-    ],
-    "name": "supportsInterface",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "symbol",
-    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [],
-    "name": "totalSupply",
-    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transfer",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferFrom",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "from",
-        "type": "tuple"
-      },
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "to",
-        "type": "tuple"
-      },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "transferFromCross",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/abi/reFungibleTokenDeprecated.jsondiffbeforeafterboth
--- a/js-packages/tests/src/eth/abi/reFungibleTokenDeprecated.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
-  {
-    "inputs": [
-      { "internalType": "address", "name": "from", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "burnFrom",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  }
-]
deletedjs-packages/tests/src/eth/allowlist.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/allowlist.test.ts
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../util/index.js';
-import {itEth, usingEthPlaygrounds, expect, SponsoringMode} from './util/index.js';
-import {CreateCollectionData} from './util/playgrounds/types.js';
-
-describe('EVM contract allowlist', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Contract allowlist can be toggled', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const flipper = await helper.eth.deployFlipper(owner);
-    const helpers = helper.ethNativeContract.contractHelpers(owner);
-
-    // Any user is allowed by default
-    expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false;
-
-    // Enable
-    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
-    expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.true;
-
-    // Disable
-    await helpers.methods.toggleAllowlist(flipper.options.address, false).send({from: owner});
-    expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false;
-  });
-
-  itEth('Non-allowlisted user can\'t call contract with allowlist enabled', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const flipper = await helper.eth.deployFlipper(owner);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-
-    // User can flip with allowlist disabled
-    await flipper.methods.flip().send({from: caller});
-    expect(await flipper.methods.getValue().call()).to.be.true;
-
-    // Tx will be reverted if user is not in allowlist
-    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
-    await expect(flipper.methods.flip().send({from: caller})).to.rejected;
-    expect(await flipper.methods.getValue().call()).to.be.true;
-
-    // Adding caller to allowlist will make contract callable again
-    await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
-    await flipper.methods.flip().send({from: caller});
-    expect(await flipper.methods.getValue().call()).to.be.false;
-  });
-});
-
-describe('EVM collection allowlist', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  // Soft-deprecated
-  itEth('Collection allowlist can be added and removed by [eth] address', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const user = helper.eth.createAccount();
-    const crossUser = helper.ethCrossAccount.fromAddress(user);
-
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;
-    await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
-    expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.true;
-
-    await collectionEvm.methods.removeFromCollectionAllowList(user).send({from: owner});
-    expect(await collectionEvm.methods.allowlistedCross(crossUser).call({from: owner})).to.be.false;
-  });
-
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'ft' as const, requiredPallets: []},
-  ].map(testCase =>
-    itEth.ifWithPallets(`Collection allowlist can be added and removed by [cross] address for ${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {
-      const owner = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
-      const [userSub] = await helper.arrange.createAccounts([10n], donor);
-      const userEth = await helper.eth.createAccountWithBalance(donor);
-      const mintParams = testCase.mode === 'ft' ? [userEth, 100] : [userEth];
-
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-      const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);
-      const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);
-      const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);
-
-      // Can addToCollectionAllowListCross:
-      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;
-      await collectionEvm.methods.addToCollectionAllowListCross(userCrossSub).send({from: owner});
-      await collectionEvm.methods.addToCollectionAllowListCross(userCrossEth).send({from: owner});
-      await collectionEvm.methods.addToCollectionAllowListCross(ownerCrossEth).send({from: owner});
-
-      // Accounts are in allowed list:
-      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;
-      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;
-      expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.true;
-      expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.true;
-
-      await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #1
-      await collectionEvm.methods.mint(...mintParams).send({from: owner}); // token #2
-      await collectionEvm.methods.setCollectionAccess(SponsoringMode.Allowlisted).send({from: owner});
-
-      // allowlisted account can transfer and transferCross from eth:
-      await collectionEvm.methods.transfer(owner, 1).send({from: userEth});
-      await collectionEvm.methods.transferCross(userCrossSub, 2).send({from: userEth});
-
-      if(testCase.mode === 'ft') {
-        expect(await helper.ft.getBalance(collectionId, {Ethereum: owner})).to.eq(1n);
-        expect(await helper.ft.getBalance(collectionId, {Substrate: userSub.address})).to.eq(2n);
-      } else {
-        expect(await helper.nft.getTokenOwner(collectionId, 1)).to.deep.eq({Ethereum: owner});
-        expect(await helper.nft.getTokenOwner(collectionId, 2)).to.deep.eq({Substrate: userSub.address});
-      }
-
-      // allowlisted cross substrate accounts can transfer from Substrate:
-      testCase.mode === 'ft'
-        ? await helper.ft.transfer(userSub, collectionId, {Ethereum: userEth}, 2n)
-        : await helper.collection.transferToken(userSub, collectionId, 2, {Ethereum: userEth});
-
-      // can removeFromCollectionAllowListCross:
-      await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossSub).send({from: owner});
-      await collectionEvm.methods.removeFromCollectionAllowListCross(userCrossEth).send({from: owner});
-      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;
-      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false;
-      expect(await collectionEvm.methods.allowlistedCross(userCrossSub).call({from: owner})).to.be.false;
-      expect(await collectionEvm.methods.allowlistedCross(userCrossEth).call({from: owner})).to.be.false;
-
-      // cannot transfer anymore
-      await collectionEvm.methods.mint(...mintParams).send({from: owner});
-      await expect(collectionEvm.methods.transfer(owner, 2).send({from: userEth})).to.be.rejectedWith(/Transaction has been reverted/);
-    }));
-
-  [
-    // cross-methods
-    {mode: 'nft' as const, cross: true, requiredPallets: []},
-    {mode: 'rft' as const, cross: true, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'ft' as const, cross: true, requiredPallets: []},
-    // soft-deprecated
-    {mode: 'nft' as const, cross: false, requiredPallets: []},
-    {mode: 'rft' as const, cross: false, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'ft' as const, cross: false, requiredPallets: []},
-  ].map(testCase =>
-    itEth.ifWithPallets(`Non-owner cannot add or remove from collection allowlist ${testCase.cross ? 'cross ' : ''}${testCase.mode}`, testCase.requiredPallets, async ({helper}) => {
-      // Select methods:
-      const addToAllowList = testCase.cross ? 'addToCollectionAllowListCross' : 'addToCollectionAllowList';
-      const removeFromAllowList = testCase.cross ? 'removeFromCollectionAllowListCross' : 'removeFromCollectionAllowList';
-
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const notOwner = await helper.eth.createAccountWithBalance(donor);
-      const userSub = donor;
-      const userCrossSub = helper.ethCrossAccount.fromKeyringPair(userSub);
-      const userEth = helper.eth.createAccount();
-      const userCrossEth = helper.ethCrossAccount.fromAddress(userEth);
-
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, !testCase.cross);
-
-      expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.false;
-      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.false;
-
-      // 1. notOwner cannot add to allow list:
-      // 1.1 plain ethereum or cross address:
-      await expect(collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-      // 1.2 cross-substrate address:
-      if(testCase.cross)
-        await expect(collectionEvm.methods[addToAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-
-      // 2. owner can add to allow list:
-      // 2.1 plain ethereum or cross address:
-      await collectionEvm.methods[addToAllowList](testCase.cross ? userCrossEth : userEth).send({from: owner});
-      // 2.2 cross-substrate address:
-      if(testCase.cross) {
-        await collectionEvm.methods[addToAllowList](userCrossSub).send({from: owner});
-        expect(await helper.collection.allowed(collectionId, {Substrate: userSub.address})).to.be.true;
-      }
-      expect(await helper.collection.allowed(collectionId, {Ethereum: userEth})).to.be.true;
-
-      // 3. notOwner cannot remove from allow list:
-      // 3.1 plain ethereum or cross address:
-      await expect(collectionEvm.methods[removeFromAllowList](testCase.cross ? userCrossEth : userEth).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-      // 3.2 cross-substrate address:
-      if(testCase.cross)
-        await expect(collectionEvm.methods[removeFromAllowList](userCrossSub).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-    }));
-});
deletedjs-packages/tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/api/CollectionHelpers.sol
+++ /dev/null
@@ -1,233 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-/// @dev common stubs holder
-interface Dummy {
-
-}
-
-interface ERC165 is Dummy {
-	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-/// @dev inlined interface
-interface CollectionHelpersEvents {
-	event CollectionCreated(address indexed owner, address indexed collectionId);
-	event CollectionDestroyed(address indexed collectionId);
-	event CollectionChanged(address indexed collectionId);
-}
-
-/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x94e5af0d
-interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
-	/// Create a collection
-	/// @return address Address of the newly created collection
-	/// @dev EVM selector for this function is: 0x72b5bea7,
-	///  or in textual repr: createCollection((string,string,string,uint8,uint8,(string,bytes)[],(string,(uint8,bool)[])[],(address,uint256)[],(bool,bool,address[]),(uint8,uint256)[],(address,uint256),uint8))
-	function createCollection(CreateCollectionData memory data) external payable returns (address);
-
-	/// Create an NFT collection
-	/// @param name Name of the collection
-	/// @param description Informative description of the collection
-	/// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
-	/// @return address Address of the newly created collection
-	/// @dev EVM selector for this function is: 0x844af658,
-	///  or in textual repr: createNFTCollection(string,string,string)
-	function createNFTCollection(
-		string memory name,
-		string memory description,
-		string memory tokenPrefix
-	) external payable returns (address);
-
-	// /// Create an NFT collection
-	// /// @param name Name of the collection
-	// /// @param description Informative description of the collection
-	// /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications
-	// /// @return address Address of the newly created collection
-	// /// @dev EVM selector for this function is: 0xe34a6844,
-	// ///  or in textual repr: createNonfungibleCollection(string,string,string)
-	// function createNonfungibleCollection(string memory name, string memory description, string memory tokenPrefix) external payable returns (address);
-
-	/// @dev EVM selector for this function is: 0xab173450,
-	///  or in textual repr: createRFTCollection(string,string,string)
-	function createRFTCollection(
-		string memory name,
-		string memory description,
-		string memory tokenPrefix
-	) external payable returns (address);
-
-	/// @dev EVM selector for this function is: 0x7335b79f,
-	///  or in textual repr: createFTCollection(string,uint8,string,string)
-	function createFTCollection(
-		string memory name,
-		uint8 decimals,
-		string memory description,
-		string memory tokenPrefix
-	) external payable returns (address);
-
-	/// @dev EVM selector for this function is: 0x85624258,
-	///  or in textual repr: makeCollectionERC721MetadataCompatible(address,string)
-	function makeCollectionERC721MetadataCompatible(address collection, string memory baseUri) external;
-
-	/// @dev EVM selector for this function is: 0x564e321f,
-	///  or in textual repr: destroyCollection(address)
-	function destroyCollection(address collectionAddress) external;
-
-	/// Check if a collection exists
-	/// @param collectionAddress Address of the collection in question
-	/// @return bool Does the collection exist?
-	/// @dev EVM selector for this function is: 0xc3de1494,
-	///  or in textual repr: isCollectionExist(address)
-	function isCollectionExist(address collectionAddress) external view returns (bool);
-
-	/// @dev EVM selector for this function is: 0xd23a7ab1,
-	///  or in textual repr: collectionCreationFee()
-	function collectionCreationFee() external view returns (uint256);
-
-	/// Returns address of a collection.
-	/// @param collectionId  - CollectionId  of the collection
-	/// @return eth mirror address of the collection
-	/// @dev EVM selector for this function is: 0x2e716683,
-	///  or in textual repr: collectionAddress(uint32)
-	function collectionAddress(uint32 collectionId) external view returns (address);
-
-	/// Returns collectionId of a collection.
-	/// @param collectionAddress  - Eth address of the collection
-	/// @return collectionId of the collection
-	/// @dev EVM selector for this function is: 0xb5cb7498,
-	///  or in textual repr: collectionId(address)
-	function collectionId(address collectionAddress) external view returns (uint32);
-}
-
-/// Collection properties
-struct CreateCollectionData {
-	/// Collection name
-	string name;
-	/// Collection description
-	string description;
-	/// Token prefix
-	string token_prefix;
-	/// Token type (NFT, FT or RFT)
-	CollectionMode mode;
-	/// Fungible token precision
-	uint8 decimals;
-	/// Custom Properties
-	Property[] properties;
-	/// Permissions for token properties
-	TokenPropertyPermission[] token_property_permissions;
-	/// Collection admins
-	CrossAddress[] admin_list;
-	/// Nesting settings
-	CollectionNestingAndPermission nesting_settings;
-	/// Collection limits
-	CollectionLimitValue[] limits;
-	/// Collection sponsor
-	CrossAddress pending_sponsor;
-	/// Extra collection flags
-	CollectionFlags flags;
-}
-
-type CollectionFlags is uint8;
-
-library CollectionFlagsLib {
-	/// Tokens in foreign collections can be transferred, but not burnt
-	CollectionFlags constant foreignField = CollectionFlags.wrap(128);
-	/// Supports ERC721Metadata
-	CollectionFlags constant erc721metadataField = CollectionFlags.wrap(64);
-	/// External collections can't be managed using `unique` api
-	CollectionFlags constant externalField = CollectionFlags.wrap(1);
-
-	/// Reserved flags
-	function reservedField(uint8 value) public pure returns (CollectionFlags) {
-		require(value < 1 << 5, "out of bound value");
-		return CollectionFlags.wrap(value << 1);
-	}
-}
-
-/// Cross account struct
-struct CrossAddress {
-	address eth;
-	uint256 sub;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
-struct CollectionLimitValue {
-	CollectionLimitField field;
-	uint256 value;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
-enum CollectionLimitField {
-	/// How many tokens can a user have on one account.
-	AccountTokenOwnership,
-	/// How many bytes of data are available for sponsorship.
-	SponsoredDataSize,
-	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
-	SponsoredDataRateLimit,
-	/// How many tokens can be mined into this collection.
-	TokenLimit,
-	/// Timeouts for transfer sponsoring.
-	SponsorTransferTimeout,
-	/// Timeout for sponsoring an approval in passed blocks.
-	SponsorApproveTimeout,
-	/// Whether the collection owner of the collection can send tokens (which belong to other users).
-	OwnerCanTransfer,
-	/// Can the collection owner burn other people's tokens.
-	OwnerCanDestroy,
-	/// Is it possible to send tokens from this collection between users.
-	TransferEnabled
-}
-
-/// Nested collections and permissions
-struct CollectionNestingAndPermission {
-	/// Owner of token can nest tokens under it.
-	bool token_owner;
-	/// Admin of token collection can nest tokens under token.
-	bool collection_admin;
-	/// If set - only tokens from specified collections can be nested.
-	address[] restricted;
-}
-
-/// Ethereum representation of Token Property Permissions.
-struct TokenPropertyPermission {
-	/// Token property key.
-	string key;
-	/// Token property permissions.
-	PropertyPermission[] permissions;
-}
-
-/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.
-struct PropertyPermission {
-	/// TokenPermission field.
-	TokenPermissionField code;
-	/// TokenPermission value.
-	bool value;
-}
-
-/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.
-enum TokenPermissionField {
-	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]
-	Mutable,
-	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]
-	TokenOwner,
-	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]
-	CollectionAdmin
-}
-
-/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
-struct Property {
-	string key;
-	bytes value;
-}
-
-/// Type of tokens in collection
-enum CollectionMode {
-	/// Nonfungible
-	Nonfungible,
-	/// Fungible
-	Fungible,
-	/// Refungible
-	Refungible
-}
deletedjs-packages/tests/src/eth/api/ContractHelpers.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/api/ContractHelpers.sol
+++ /dev/null
@@ -1,196 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-/// @dev common stubs holder
-interface Dummy {
-
-}
-
-interface ERC165 is Dummy {
-	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-/// @dev inlined interface
-interface ContractHelpersEvents {
-	event ContractSponsorSet(address indexed contractAddress, address sponsor);
-	event ContractSponsorshipConfirmed(address indexed contractAddress, address sponsor);
-	event ContractSponsorRemoved(address indexed contractAddress);
-}
-
-/// @title Magic contract, which allows users to reconfigure other contracts
-/// @dev the ERC-165 identifier for this interface is 0x30afad04
-interface ContractHelpers is Dummy, ERC165, ContractHelpersEvents {
-	/// Get user, which deployed specified contract
-	/// @dev May return zero address in case if contract is deployed
-	///  using uniquenetwork evm-migration pallet, or using other terms not
-	///  intended by pallet-evm
-	/// @dev Returns zero address if contract does not exists
-	/// @param contractAddress Contract to get owner of
-	/// @return address Owner of contract
-	/// @dev EVM selector for this function is: 0x5152b14c,
-	///  or in textual repr: contractOwner(address)
-	function contractOwner(address contractAddress) external view returns (address);
-
-	/// Set sponsor.
-	/// @param contractAddress Contract for which a sponsor is being established.
-	/// @param sponsor User address who set as pending sponsor.
-	/// @dev EVM selector for this function is: 0xf01fba93,
-	///  or in textual repr: setSponsor(address,address)
-	function setSponsor(address contractAddress, address sponsor) external;
-
-	/// Set contract as self sponsored.
-	///
-	/// @param contractAddress Contract for which a self sponsoring is being enabled.
-	/// @dev EVM selector for this function is: 0x89f7d9ae,
-	///  or in textual repr: selfSponsoredEnable(address)
-	function selfSponsoredEnable(address contractAddress) external;
-
-	/// Remove sponsor.
-	///
-	/// @param contractAddress Contract for which a sponsorship is being removed.
-	/// @dev EVM selector for this function is: 0xef784250,
-	///  or in textual repr: removeSponsor(address)
-	function removeSponsor(address contractAddress) external;
-
-	/// Confirm sponsorship.
-	///
-	/// @dev Caller must be same that set via [`setSponsor`].
-	///
-	/// @param contractAddress Сontract for which need to confirm sponsorship.
-	/// @dev EVM selector for this function is: 0xabc00001,
-	///  or in textual repr: confirmSponsorship(address)
-	function confirmSponsorship(address contractAddress) external;
-
-	/// Get current sponsor.
-	///
-	/// @param contractAddress The contract for which a sponsor is requested.
-	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
-	/// @dev EVM selector for this function is: 0x766c4f37,
-	///  or in textual repr: sponsor(address)
-	function sponsor(address contractAddress) external view returns (OptionCrossAddress memory);
-
-	/// Check tat contract has confirmed sponsor.
-	///
-	/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.
-	/// @return **true** if contract has confirmed sponsor.
-	/// @dev EVM selector for this function is: 0x97418603,
-	///  or in textual repr: hasSponsor(address)
-	function hasSponsor(address contractAddress) external view returns (bool);
-
-	/// Check tat contract has pending sponsor.
-	///
-	/// @param contractAddress The contract for which the presence of a pending sponsor is checked.
-	/// @return **true** if contract has pending sponsor.
-	/// @dev EVM selector for this function is: 0x39b9b242,
-	///  or in textual repr: hasPendingSponsor(address)
-	function hasPendingSponsor(address contractAddress) external view returns (bool);
-
-	/// @dev EVM selector for this function is: 0x6027dc61,
-	///  or in textual repr: sponsoringEnabled(address)
-	function sponsoringEnabled(address contractAddress) external view returns (bool);
-
-	/// @dev EVM selector for this function is: 0xfde8a560,
-	///  or in textual repr: setSponsoringMode(address,uint8)
-	function setSponsoringMode(address contractAddress, SponsoringModeT mode) external;
-
-	/// Get current contract sponsoring rate limit
-	/// @param contractAddress Contract to get sponsoring rate limit of
-	/// @return uint32 Amount of blocks between two sponsored transactions
-	/// @dev EVM selector for this function is: 0xf29694d8,
-	///  or in textual repr: sponsoringRateLimit(address)
-	function sponsoringRateLimit(address contractAddress) external view returns (uint32);
-
-	/// Set contract sponsoring rate limit
-	/// @dev Sponsoring rate limit - is a minimum amount of blocks that should
-	///  pass between two sponsored transactions
-	/// @param contractAddress Contract to change sponsoring rate limit of
-	/// @param rateLimit Target rate limit
-	/// @dev Only contract owner can change this setting
-	/// @dev EVM selector for this function is: 0x77b6c908,
-	///  or in textual repr: setSponsoringRateLimit(address,uint32)
-	function setSponsoringRateLimit(address contractAddress, uint32 rateLimit) external;
-
-	/// Set contract sponsoring fee limit
-	/// @dev Sponsoring fee limit - is maximum fee that could be spent by
-	///  single transaction
-	/// @param contractAddress Contract to change sponsoring fee limit of
-	/// @param feeLimit Fee limit
-	/// @dev Only contract owner can change this setting
-	/// @dev EVM selector for this function is: 0x03aed665,
-	///  or in textual repr: setSponsoringFeeLimit(address,uint256)
-	function setSponsoringFeeLimit(address contractAddress, uint256 feeLimit) external;
-
-	/// Get current contract sponsoring fee limit
-	/// @param contractAddress Contract to get sponsoring fee limit of
-	/// @return uint256 Maximum amount of fee that could be spent by single
-	///  transaction
-	/// @dev EVM selector for this function is: 0x75b73606,
-	///  or in textual repr: sponsoringFeeLimit(address)
-	function sponsoringFeeLimit(address contractAddress) external view returns (uint256);
-
-	/// Is specified user present in contract allow list
-	/// @dev Contract owner always implicitly included
-	/// @param contractAddress Contract to check allowlist of
-	/// @param user User to check
-	/// @return bool Is specified users exists in contract allowlist
-	/// @dev EVM selector for this function is: 0x5c658165,
-	///  or in textual repr: allowed(address,address)
-	function allowed(address contractAddress, address user) external view returns (bool);
-
-	/// Toggle user presence in contract allowlist
-	/// @param contractAddress Contract to change allowlist of
-	/// @param user Which user presence should be toggled
-	/// @param isAllowed `true` if user should be allowed to be sponsored
-	///  or call this contract, `false` otherwise
-	/// @dev Only contract owner can change this setting
-	/// @dev EVM selector for this function is: 0x4706cc1c,
-	///  or in textual repr: toggleAllowed(address,address,bool)
-	function toggleAllowed(
-		address contractAddress,
-		address user,
-		bool isAllowed
-	) external;
-
-	/// Is this contract has allowlist access enabled
-	/// @dev Allowlist always can have users, and it is used for two purposes:
-	///  in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist
-	///  in case of allowlist access enabled, only users from allowlist may call this contract
-	/// @param contractAddress Contract to get allowlist access of
-	/// @return bool Is specified contract has allowlist access enabled
-	/// @dev EVM selector for this function is: 0xc772ef6c,
-	///  or in textual repr: allowlistEnabled(address)
-	function allowlistEnabled(address contractAddress) external view returns (bool);
-
-	/// Toggle contract allowlist access
-	/// @param contractAddress Contract to change allowlist access of
-	/// @param enabled Should allowlist access to be enabled?
-	/// @dev EVM selector for this function is: 0x36de20f5,
-	///  or in textual repr: toggleAllowlist(address,bool)
-	function toggleAllowlist(address contractAddress, bool enabled) external;
-}
-
-/// Available contract sponsoring modes
-enum SponsoringModeT {
-	/// Sponsoring is disabled
-	Disabled,
-	/// Only users from allowlist will be sponsored
-	Allowlisted,
-	/// All users will be sponsored
-	Generous
-}
-
-/// Optional value
-struct OptionCrossAddress {
-	/// Shows the status of accessibility of value
-	bool status;
-	/// Actual value if `status` is true
-	CrossAddress value;
-}
-
-/// Cross account struct
-struct CrossAddress {
-	address eth;
-	uint256 sub;
-}
deletedjs-packages/tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/api/UniqueFungible.sol
+++ /dev/null
@@ -1,510 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-/// @dev common stubs holder
-interface Dummy {
-
-}
-
-interface ERC165 is Dummy {
-	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xb34d97e9
-interface Collection is Dummy, ERC165 {
-	// /// Set collection property.
-	// ///
-	// /// @param key Property key.
-	// /// @param value Propery value.
-	// /// @dev EVM selector for this function is: 0x2f073f66,
-	// ///  or in textual repr: setCollectionProperty(string,bytes)
-	// function setCollectionProperty(string memory key, bytes memory value) external;
-
-	/// Set collection properties.
-	///
-	/// @param properties Vector of properties key/value pair.
-	/// @dev EVM selector for this function is: 0x50b26b2a,
-	///  or in textual repr: setCollectionProperties((string,bytes)[])
-	function setCollectionProperties(Property[] memory properties) external;
-
-	// /// Delete collection property.
-	// ///
-	// /// @param key Property key.
-	// /// @dev EVM selector for this function is: 0x7b7debce,
-	// ///  or in textual repr: deleteCollectionProperty(string)
-	// function deleteCollectionProperty(string memory key) external;
-
-	/// Delete collection properties.
-	///
-	/// @param keys Properties keys.
-	/// @dev EVM selector for this function is: 0xee206ee3,
-	///  or in textual repr: deleteCollectionProperties(string[])
-	function deleteCollectionProperties(string[] memory keys) external;
-
-	/// Get collection property.
-	///
-	/// @dev Throws error if key not found.
-	///
-	/// @param key Property key.
-	/// @return bytes The property corresponding to the key.
-	/// @dev EVM selector for this function is: 0xcf24fd6d,
-	///  or in textual repr: collectionProperty(string)
-	function collectionProperty(string memory key) external view returns (bytes memory);
-
-	/// Get collection properties.
-	///
-	/// @param keys Properties keys. Empty keys for all propertyes.
-	/// @return Vector of properties key/value pairs.
-	/// @dev EVM selector for this function is: 0x285fb8e6,
-	///  or in textual repr: collectionProperties(string[])
-	function collectionProperties(string[] memory keys) external view returns (Property[] memory);
-
-	// /// Set the sponsor of the collection.
-	// ///
-	// /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	// ///
-	// /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	// /// @dev EVM selector for this function is: 0x7623402e,
-	// ///  or in textual repr: setCollectionSponsor(address)
-	// function setCollectionSponsor(address sponsor) external;
-
-	/// Set the sponsor of the collection.
-	///
-	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	///
-	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
-	/// @dev EVM selector for this function is: 0x84a1d5a8,
-	///  or in textual repr: setCollectionSponsorCross((address,uint256))
-	function setCollectionSponsorCross(CrossAddress memory sponsor) external;
-
-	/// Whether there is a pending sponsor.
-	/// @dev EVM selector for this function is: 0x058ac185,
-	///  or in textual repr: hasCollectionPendingSponsor()
-	function hasCollectionPendingSponsor() external view returns (bool);
-
-	/// Collection sponsorship confirmation.
-	///
-	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	/// @dev EVM selector for this function is: 0x3c50e97a,
-	///  or in textual repr: confirmCollectionSponsorship()
-	function confirmCollectionSponsorship() external;
-
-	/// Remove collection sponsor.
-	/// @dev EVM selector for this function is: 0x6e0326a3,
-	///  or in textual repr: removeCollectionSponsor()
-	function removeCollectionSponsor() external;
-
-	/// Get current sponsor.
-	///
-	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
-	/// @dev EVM selector for this function is: 0x6ec0a9f1,
-	///  or in textual repr: collectionSponsor()
-	function collectionSponsor() external view returns (CrossAddress memory);
-
-	/// Get current collection limits.
-	///
-	/// @return Array of collection limits
-	/// @dev EVM selector for this function is: 0xf63bc572,
-	///  or in textual repr: collectionLimits()
-	function collectionLimits() external view returns (CollectionLimit[] memory);
-
-	/// Set limits for the collection.
-	/// @dev Throws error if limit not found.
-	/// @param limit Some limit.
-	/// @dev EVM selector for this function is: 0x2316ee74,
-	///  or in textual repr: setCollectionLimit((uint8,(bool,uint256)))
-	function setCollectionLimit(CollectionLimit memory limit) external;
-
-	/// Get contract address.
-	/// @dev EVM selector for this function is: 0xf6b4dfb4,
-	///  or in textual repr: contractAddress()
-	function contractAddress() external view returns (address);
-
-	/// Add collection admin.
-	/// @param newAdmin Cross account administrator address.
-	/// @dev EVM selector for this function is: 0x859aa7d6,
-	///  or in textual repr: addCollectionAdminCross((address,uint256))
-	function addCollectionAdminCross(CrossAddress memory newAdmin) external;
-
-	/// Remove collection admin.
-	/// @param admin Cross account administrator address.
-	/// @dev EVM selector for this function is: 0x6c0cd173,
-	///  or in textual repr: removeCollectionAdminCross((address,uint256))
-	function removeCollectionAdminCross(CrossAddress memory admin) external;
-
-	// /// Add collection admin.
-	// /// @param newAdmin Address of the added administrator.
-	// /// @dev EVM selector for this function is: 0x92e462c7,
-	// ///  or in textual repr: addCollectionAdmin(address)
-	// function addCollectionAdmin(address newAdmin) external;
-
-	// /// Remove collection admin.
-	// ///
-	// /// @param admin Address of the removed administrator.
-	// /// @dev EVM selector for this function is: 0xfafd7b42,
-	// ///  or in textual repr: removeCollectionAdmin(address)
-	// function removeCollectionAdmin(address admin) external;
-
-	/// @dev EVM selector for this function is: 0x0b9f3890,
-	///  or in textual repr: setCollectionNesting((bool,bool,address[]))
-	function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external;
-
-	// /// Toggle accessibility of collection nesting.
-	// ///
-	// /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	// /// @dev EVM selector for this function is: 0x112d4586,
-	// ///  or in textual repr: setCollectionNesting(bool)
-	// function setCollectionNesting(bool enable) external;
-
-	// /// Toggle accessibility of collection nesting.
-	// ///
-	// /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// /// @param collections Addresses of collections that will be available for nesting.
-	// /// @dev EVM selector for this function is: 0x64872396,
-	// ///  or in textual repr: setCollectionNesting(bool,address[])
-	// function setCollectionNesting(bool enable, address[] memory collections) external;
-
-	/// @dev EVM selector for this function is: 0x92c660a8,
-	///  or in textual repr: collectionNesting()
-	function collectionNesting() external view returns (CollectionNestingAndPermission memory);
-
-	// /// Returns nesting for a collection
-	// /// @dev EVM selector for this function is: 0x22d25bfe,
-	// ///  or in textual repr: collectionNestingRestrictedCollectionIds()
-	// function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory);
-
-	// /// Returns permissions for a collection
-	// /// @dev EVM selector for this function is: 0x5b2eaf4b,
-	// ///  or in textual repr: collectionNestingPermissions()
-	// function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory);
-
-	/// Set the collection access method.
-	/// @param mode Access mode
-	/// @dev EVM selector for this function is: 0x41835d4c,
-	///  or in textual repr: setCollectionAccess(uint8)
-	function setCollectionAccess(AccessMode mode) external;
-
-	/// Checks that user allowed to operate with collection.
-	///
-	/// @param user User address to check.
-	/// @dev EVM selector for this function is: 0x91b6df49,
-	///  or in textual repr: allowlistedCross((address,uint256))
-	function allowlistedCross(CrossAddress memory user) external view returns (bool);
-
-	// /// Add the user to the allowed list.
-	// ///
-	// /// @param user Address of a trusted user.
-	// /// @dev EVM selector for this function is: 0x67844fe6,
-	// ///  or in textual repr: addToCollectionAllowList(address)
-	// function addToCollectionAllowList(address user) external;
-
-	/// Add user to allowed list.
-	///
-	/// @param user User cross account address.
-	/// @dev EVM selector for this function is: 0xa0184a3a,
-	///  or in textual repr: addToCollectionAllowListCross((address,uint256))
-	function addToCollectionAllowListCross(CrossAddress memory user) external;
-
-	// /// Remove the user from the allowed list.
-	// ///
-	// /// @param user Address of a removed user.
-	// /// @dev EVM selector for this function is: 0x85c51acb,
-	// ///  or in textual repr: removeFromCollectionAllowList(address)
-	// function removeFromCollectionAllowList(address user) external;
-
-	/// Remove user from allowed list.
-	///
-	/// @param user User cross account address.
-	/// @dev EVM selector for this function is: 0x09ba452a,
-	///  or in textual repr: removeFromCollectionAllowListCross((address,uint256))
-	function removeFromCollectionAllowListCross(CrossAddress memory user) external;
-
-	/// Switch permission for minting.
-	///
-	/// @param mode Enable if "true".
-	/// @dev EVM selector for this function is: 0x00018e84,
-	///  or in textual repr: setCollectionMintMode(bool)
-	function setCollectionMintMode(bool mode) external;
-
-	// /// Check that account is the owner or admin of the collection
-	// ///
-	// /// @param user account to verify
-	// /// @return "true" if account is the owner or admin
-	// /// @dev EVM selector for this function is: 0x9811b0c7,
-	// ///  or in textual repr: isOwnerOrAdmin(address)
-	// function isOwnerOrAdmin(address user) external view returns (bool);
-
-	/// Check that account is the owner or admin of the collection
-	///
-	/// @param user User cross account to verify
-	/// @return "true" if account is the owner or admin
-	/// @dev EVM selector for this function is: 0x3e75a905,
-	///  or in textual repr: isOwnerOrAdminCross((address,uint256))
-	function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool);
-
-	/// Returns collection type
-	///
-	/// @return `Fungible` or `NFT` or `ReFungible`
-	/// @dev EVM selector for this function is: 0xd34b55b8,
-	///  or in textual repr: uniqueCollectionType()
-	function uniqueCollectionType() external view returns (string memory);
-
-	/// Get collection owner.
-	///
-	/// @return Tuble with sponsor address and his substrate mirror.
-	/// If address is canonical then substrate mirror is zero and vice versa.
-	/// @dev EVM selector for this function is: 0xdf727d3b,
-	///  or in textual repr: collectionOwner()
-	function collectionOwner() external view returns (CrossAddress memory);
-
-	// /// Changes collection owner to another account
-	// ///
-	// /// @dev Owner can be changed only by current owner
-	// /// @param newOwner new owner account
-	// /// @dev EVM selector for this function is: 0x4f53e226,
-	// ///  or in textual repr: changeCollectionOwner(address)
-	// function changeCollectionOwner(address newOwner) external;
-
-	/// Get collection administrators
-	///
-	/// @return Vector of tuples with admins address and his substrate mirror.
-	/// If address is canonical then substrate mirror is zero and vice versa.
-	/// @dev EVM selector for this function is: 0x5813216b,
-	///  or in textual repr: collectionAdmins()
-	function collectionAdmins() external view returns (CrossAddress[] memory);
-
-	/// Changes collection owner to another account
-	///
-	/// @dev Owner can be changed only by current owner
-	/// @param newOwner new owner cross account
-	/// @dev EVM selector for this function is: 0x6496c497,
-	///  or in textual repr: changeCollectionOwnerCross((address,uint256))
-	function changeCollectionOwnerCross(CrossAddress memory newOwner) external;
-}
-
-/// Cross account struct
-struct CrossAddress {
-	address eth;
-	uint256 sub;
-}
-
-/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
-enum AccessMode {
-	/// Access grant for owner and admins. Used as default.
-	Normal,
-	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
-	AllowList
-}
-
-/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
-struct CollectionNestingPermission {
-	CollectionPermissionField field;
-	bool value;
-}
-
-/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.
-enum CollectionPermissionField {
-	/// Owner of token can nest tokens under it.
-	TokenOwner,
-	/// Admin of token collection can nest tokens under token.
-	CollectionAdmin
-}
-
-/// Nested collections.
-struct CollectionNesting {
-	bool token_owner;
-	uint256[] ids;
-}
-
-/// Nested collections and permissions
-struct CollectionNestingAndPermission {
-	/// Owner of token can nest tokens under it.
-	bool token_owner;
-	/// Admin of token collection can nest tokens under token.
-	bool collection_admin;
-	/// If set - only tokens from specified collections can be nested.
-	address[] restricted;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
-struct CollectionLimit {
-	CollectionLimitField field;
-	OptionUint256 value;
-}
-
-/// Optional value
-struct OptionUint256 {
-	/// Shows the status of accessibility of value
-	bool status;
-	/// Actual value if `status` is true
-	uint256 value;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
-enum CollectionLimitField {
-	/// How many tokens can a user have on one account.
-	AccountTokenOwnership,
-	/// How many bytes of data are available for sponsorship.
-	SponsoredDataSize,
-	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
-	SponsoredDataRateLimit,
-	/// How many tokens can be mined into this collection.
-	TokenLimit,
-	/// Timeouts for transfer sponsoring.
-	SponsorTransferTimeout,
-	/// Timeout for sponsoring an approval in passed blocks.
-	SponsorApproveTimeout,
-	/// Whether the collection owner of the collection can send tokens (which belong to other users).
-	OwnerCanTransfer,
-	/// Can the collection owner burn other people's tokens.
-	OwnerCanDestroy,
-	/// Is it possible to send tokens from this collection between users.
-	TransferEnabled
-}
-
-/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
-struct Property {
-	string key;
-	bytes value;
-}
-
-/// @dev the ERC-165 identifier for this interface is 0x69d14d3e
-interface ERC20UniqueExtensions is Dummy, ERC165 {
-	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
-	/// @param owner crossAddress The address which owns the funds.
-	/// @param spender crossAddress The address which will spend the funds.
-	/// @return A uint256 specifying the amount of tokens still available for the spender.
-	/// @dev EVM selector for this function is: 0xe0af4bd7,
-	///  or in textual repr: allowanceCross((address,uint256),(address,uint256))
-	function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256);
-
-	/// @notice A description for the collection.
-	/// @dev EVM selector for this function is: 0x7284e416,
-	///  or in textual repr: description()
-	function description() external view returns (string memory);
-
-	/// @dev EVM selector for this function is: 0x269e6158,
-	///  or in textual repr: mintCross((address,uint256),uint256)
-	function mintCross(CrossAddress memory to, uint256 amount) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0x0ecd0ab0,
-	///  or in textual repr: approveCross((address,uint256),uint256)
-	function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool);
-
-	// /// Burn tokens from account
-	// /// @dev Function that burns an `amount` of the tokens of a given account,
-	// /// deducting from the sender's allowance for said account.
-	// /// @param from The account whose tokens will be burnt.
-	// /// @param amount The amount that will be burnt.
-	// /// @dev EVM selector for this function is: 0x79cc6790,
-	// ///  or in textual repr: burnFrom(address,uint256)
-	// function burnFrom(address from, uint256 amount) external returns (bool);
-
-	/// Burn tokens from account
-	/// @dev Function that burns an `amount` of the tokens of a given account,
-	/// deducting from the sender's allowance for said account.
-	/// @param from The account whose tokens will be burnt.
-	/// @param amount The amount that will be burnt.
-	/// @dev EVM selector for this function is: 0xbb2f5a58,
-	///  or in textual repr: burnFromCross((address,uint256),uint256)
-	function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool);
-
-	/// Mint tokens for multiple accounts.
-	/// @param amounts array of pairs of account address and amount
-	/// @dev EVM selector for this function is: 0x1acf2d55,
-	///  or in textual repr: mintBulk((address,uint256)[])
-	function mintBulk(AmountForAddress[] memory amounts) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0x2ada85ff,
-	///  or in textual repr: transferCross((address,uint256),uint256)
-	function transferCross(CrossAddress memory to, uint256 amount) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0xd5cf430b,
-	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
-	function transferFromCross(
-		CrossAddress memory from,
-		CrossAddress memory to,
-		uint256 amount
-	) external returns (bool);
-
-	/// @notice Returns collection helper contract address
-	/// @dev EVM selector for this function is: 0x1896cce6,
-	///  or in textual repr: collectionHelperAddress()
-	function collectionHelperAddress() external view returns (address);
-
-	/// @notice Balance of account
-	/// @param owner An cross address for whom to query the balance
-	/// @return The number of fingibles owned by `owner`, possibly zero
-	/// @dev EVM selector for this function is: 0xec069398,
-	///  or in textual repr: balanceOfCross((address,uint256))
-	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
-}
-
-struct AmountForAddress {
-	address to;
-	uint256 amount;
-}
-
-/// @dev the ERC-165 identifier for this interface is 0x40c10f19
-interface ERC20Mintable is Dummy, ERC165 {
-	/// Mint tokens for `to` account.
-	/// @param to account that will receive minted tokens
-	/// @param amount amount of tokens to mint
-	/// @dev EVM selector for this function is: 0x40c10f19,
-	///  or in textual repr: mint(address,uint256)
-	function mint(address to, uint256 amount) external returns (bool);
-}
-
-/// @dev inlined interface
-interface ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(address indexed owner, address indexed spender, uint256 value);
-}
-
-/// @dev the ERC-165 identifier for this interface is 0x942e8b22
-interface ERC20 is Dummy, ERC165, ERC20Events {
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() external view returns (string memory);
-
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() external view returns (string memory);
-
-	/// @dev EVM selector for this function is: 0x18160ddd,
-	///  or in textual repr: totalSupply()
-	function totalSupply() external view returns (uint256);
-
-	/// @dev EVM selector for this function is: 0x313ce567,
-	///  or in textual repr: decimals()
-	function decimals() external view returns (uint8);
-
-	/// @dev EVM selector for this function is: 0x70a08231,
-	///  or in textual repr: balanceOf(address)
-	function balanceOf(address owner) external view returns (uint256);
-
-	/// @dev EVM selector for this function is: 0xa9059cbb,
-	///  or in textual repr: transfer(address,uint256)
-	function transfer(address to, uint256 amount) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0x23b872dd,
-	///  or in textual repr: transferFrom(address,address,uint256)
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0x095ea7b3,
-	///  or in textual repr: approve(address,uint256)
-	function approve(address spender, uint256 amount) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0xdd62ed3e,
-	///  or in textual repr: allowance(address,address)
-	function allowance(address owner, address spender) external view returns (uint256);
-}
-
-interface UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {}
deletedjs-packages/tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/api/UniqueNFT.sol
+++ /dev/null
@@ -1,855 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-/// @dev common stubs holder
-interface Dummy {
-
-}
-
-interface ERC165 is Dummy {
-	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-/// @dev inlined interface
-interface ERC721TokenEvent {
-	event TokenChanged(uint256 indexed tokenId);
-}
-
-/// @title A contract that allows to set and delete token properties and change token property permissions.
-/// @dev the ERC-165 identifier for this interface is 0xde0695c2
-interface TokenProperties is Dummy, ERC165, ERC721TokenEvent {
-	// /// @notice Set permissions for token property.
-	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	// /// @param key Property key.
-	// /// @param isMutable Permission to mutate property.
-	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
-	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
-	// /// @dev EVM selector for this function is: 0x222d97fa,
-	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
-	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external;
-
-	/// @notice Set permissions for token property.
-	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	/// @param permissions Permissions for keys.
-	/// @dev EVM selector for this function is: 0xbd92983a,
-	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
-	function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external;
-
-	/// @notice Get permissions for token properties.
-	/// @dev EVM selector for this function is: 0xf23d7790,
-	///  or in textual repr: tokenPropertyPermissions()
-	function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory);
-
-	// /// @notice Set token property value.
-	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// /// @param tokenId ID of the token.
-	// /// @param key Property key.
-	// /// @param value Property value.
-	// /// @dev EVM selector for this function is: 0x1752d67b,
-	// ///  or in textual repr: setProperty(uint256,string,bytes)
-	// function setProperty(uint256 tokenId, string memory key, bytes memory value) external;
-
-	/// @notice Set token properties value.
-	/// @dev Throws error if `msg.sender` has no permission to edit the property.
-	/// @param tokenId ID of the token.
-	/// @param properties settable properties
-	/// @dev EVM selector for this function is: 0x14ed3a6e,
-	///  or in textual repr: setProperties(uint256,(string,bytes)[])
-	function setProperties(uint256 tokenId, Property[] memory properties) external;
-
-	// /// @notice Delete token property value.
-	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// /// @param tokenId ID of the token.
-	// /// @param key Property key.
-	// /// @dev EVM selector for this function is: 0x066111d1,
-	// ///  or in textual repr: deleteProperty(uint256,string)
-	// function deleteProperty(uint256 tokenId, string memory key) external;
-
-	/// @notice Delete token properties value.
-	/// @dev Throws error if `msg.sender` has no permission to edit the property.
-	/// @param tokenId ID of the token.
-	/// @param keys Properties key.
-	/// @dev EVM selector for this function is: 0xc472d371,
-	///  or in textual repr: deleteProperties(uint256,string[])
-	function deleteProperties(uint256 tokenId, string[] memory keys) external;
-
-	/// @notice Get token property value.
-	/// @dev Throws error if key not found
-	/// @param tokenId ID of the token.
-	/// @param key Property key.
-	/// @return Property value bytes
-	/// @dev EVM selector for this function is: 0x7228c327,
-	///  or in textual repr: property(uint256,string)
-	function property(uint256 tokenId, string memory key) external view returns (bytes memory);
-}
-
-/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
-struct Property {
-	string key;
-	bytes value;
-}
-
-/// Ethereum representation of Token Property Permissions.
-struct TokenPropertyPermission {
-	/// Token property key.
-	string key;
-	/// Token property permissions.
-	PropertyPermission[] permissions;
-}
-
-/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.
-struct PropertyPermission {
-	/// TokenPermission field.
-	TokenPermissionField code;
-	/// TokenPermission value.
-	bool value;
-}
-
-/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.
-enum TokenPermissionField {
-	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]
-	Mutable,
-	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]
-	TokenOwner,
-	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]
-	CollectionAdmin
-}
-
-/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xb34d97e9
-interface Collection is Dummy, ERC165 {
-	// /// Set collection property.
-	// ///
-	// /// @param key Property key.
-	// /// @param value Propery value.
-	// /// @dev EVM selector for this function is: 0x2f073f66,
-	// ///  or in textual repr: setCollectionProperty(string,bytes)
-	// function setCollectionProperty(string memory key, bytes memory value) external;
-
-	/// Set collection properties.
-	///
-	/// @param properties Vector of properties key/value pair.
-	/// @dev EVM selector for this function is: 0x50b26b2a,
-	///  or in textual repr: setCollectionProperties((string,bytes)[])
-	function setCollectionProperties(Property[] memory properties) external;
-
-	// /// Delete collection property.
-	// ///
-	// /// @param key Property key.
-	// /// @dev EVM selector for this function is: 0x7b7debce,
-	// ///  or in textual repr: deleteCollectionProperty(string)
-	// function deleteCollectionProperty(string memory key) external;
-
-	/// Delete collection properties.
-	///
-	/// @param keys Properties keys.
-	/// @dev EVM selector for this function is: 0xee206ee3,
-	///  or in textual repr: deleteCollectionProperties(string[])
-	function deleteCollectionProperties(string[] memory keys) external;
-
-	/// Get collection property.
-	///
-	/// @dev Throws error if key not found.
-	///
-	/// @param key Property key.
-	/// @return bytes The property corresponding to the key.
-	/// @dev EVM selector for this function is: 0xcf24fd6d,
-	///  or in textual repr: collectionProperty(string)
-	function collectionProperty(string memory key) external view returns (bytes memory);
-
-	/// Get collection properties.
-	///
-	/// @param keys Properties keys. Empty keys for all propertyes.
-	/// @return Vector of properties key/value pairs.
-	/// @dev EVM selector for this function is: 0x285fb8e6,
-	///  or in textual repr: collectionProperties(string[])
-	function collectionProperties(string[] memory keys) external view returns (Property[] memory);
-
-	// /// Set the sponsor of the collection.
-	// ///
-	// /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	// ///
-	// /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	// /// @dev EVM selector for this function is: 0x7623402e,
-	// ///  or in textual repr: setCollectionSponsor(address)
-	// function setCollectionSponsor(address sponsor) external;
-
-	/// Set the sponsor of the collection.
-	///
-	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	///
-	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
-	/// @dev EVM selector for this function is: 0x84a1d5a8,
-	///  or in textual repr: setCollectionSponsorCross((address,uint256))
-	function setCollectionSponsorCross(CrossAddress memory sponsor) external;
-
-	/// Whether there is a pending sponsor.
-	/// @dev EVM selector for this function is: 0x058ac185,
-	///  or in textual repr: hasCollectionPendingSponsor()
-	function hasCollectionPendingSponsor() external view returns (bool);
-
-	/// Collection sponsorship confirmation.
-	///
-	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	/// @dev EVM selector for this function is: 0x3c50e97a,
-	///  or in textual repr: confirmCollectionSponsorship()
-	function confirmCollectionSponsorship() external;
-
-	/// Remove collection sponsor.
-	/// @dev EVM selector for this function is: 0x6e0326a3,
-	///  or in textual repr: removeCollectionSponsor()
-	function removeCollectionSponsor() external;
-
-	/// Get current sponsor.
-	///
-	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
-	/// @dev EVM selector for this function is: 0x6ec0a9f1,
-	///  or in textual repr: collectionSponsor()
-	function collectionSponsor() external view returns (CrossAddress memory);
-
-	/// Get current collection limits.
-	///
-	/// @return Array of collection limits
-	/// @dev EVM selector for this function is: 0xf63bc572,
-	///  or in textual repr: collectionLimits()
-	function collectionLimits() external view returns (CollectionLimit[] memory);
-
-	/// Set limits for the collection.
-	/// @dev Throws error if limit not found.
-	/// @param limit Some limit.
-	/// @dev EVM selector for this function is: 0x2316ee74,
-	///  or in textual repr: setCollectionLimit((uint8,(bool,uint256)))
-	function setCollectionLimit(CollectionLimit memory limit) external;
-
-	/// Get contract address.
-	/// @dev EVM selector for this function is: 0xf6b4dfb4,
-	///  or in textual repr: contractAddress()
-	function contractAddress() external view returns (address);
-
-	/// Add collection admin.
-	/// @param newAdmin Cross account administrator address.
-	/// @dev EVM selector for this function is: 0x859aa7d6,
-	///  or in textual repr: addCollectionAdminCross((address,uint256))
-	function addCollectionAdminCross(CrossAddress memory newAdmin) external;
-
-	/// Remove collection admin.
-	/// @param admin Cross account administrator address.
-	/// @dev EVM selector for this function is: 0x6c0cd173,
-	///  or in textual repr: removeCollectionAdminCross((address,uint256))
-	function removeCollectionAdminCross(CrossAddress memory admin) external;
-
-	// /// Add collection admin.
-	// /// @param newAdmin Address of the added administrator.
-	// /// @dev EVM selector for this function is: 0x92e462c7,
-	// ///  or in textual repr: addCollectionAdmin(address)
-	// function addCollectionAdmin(address newAdmin) external;
-
-	// /// Remove collection admin.
-	// ///
-	// /// @param admin Address of the removed administrator.
-	// /// @dev EVM selector for this function is: 0xfafd7b42,
-	// ///  or in textual repr: removeCollectionAdmin(address)
-	// function removeCollectionAdmin(address admin) external;
-
-	/// @dev EVM selector for this function is: 0x0b9f3890,
-	///  or in textual repr: setCollectionNesting((bool,bool,address[]))
-	function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external;
-
-	// /// Toggle accessibility of collection nesting.
-	// ///
-	// /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	// /// @dev EVM selector for this function is: 0x112d4586,
-	// ///  or in textual repr: setCollectionNesting(bool)
-	// function setCollectionNesting(bool enable) external;
-
-	// /// Toggle accessibility of collection nesting.
-	// ///
-	// /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// /// @param collections Addresses of collections that will be available for nesting.
-	// /// @dev EVM selector for this function is: 0x64872396,
-	// ///  or in textual repr: setCollectionNesting(bool,address[])
-	// function setCollectionNesting(bool enable, address[] memory collections) external;
-
-	/// @dev EVM selector for this function is: 0x92c660a8,
-	///  or in textual repr: collectionNesting()
-	function collectionNesting() external view returns (CollectionNestingAndPermission memory);
-
-	// /// Returns nesting for a collection
-	// /// @dev EVM selector for this function is: 0x22d25bfe,
-	// ///  or in textual repr: collectionNestingRestrictedCollectionIds()
-	// function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory);
-
-	// /// Returns permissions for a collection
-	// /// @dev EVM selector for this function is: 0x5b2eaf4b,
-	// ///  or in textual repr: collectionNestingPermissions()
-	// function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory);
-
-	/// Set the collection access method.
-	/// @param mode Access mode
-	/// @dev EVM selector for this function is: 0x41835d4c,
-	///  or in textual repr: setCollectionAccess(uint8)
-	function setCollectionAccess(AccessMode mode) external;
-
-	/// Checks that user allowed to operate with collection.
-	///
-	/// @param user User address to check.
-	/// @dev EVM selector for this function is: 0x91b6df49,
-	///  or in textual repr: allowlistedCross((address,uint256))
-	function allowlistedCross(CrossAddress memory user) external view returns (bool);
-
-	// /// Add the user to the allowed list.
-	// ///
-	// /// @param user Address of a trusted user.
-	// /// @dev EVM selector for this function is: 0x67844fe6,
-	// ///  or in textual repr: addToCollectionAllowList(address)
-	// function addToCollectionAllowList(address user) external;
-
-	/// Add user to allowed list.
-	///
-	/// @param user User cross account address.
-	/// @dev EVM selector for this function is: 0xa0184a3a,
-	///  or in textual repr: addToCollectionAllowListCross((address,uint256))
-	function addToCollectionAllowListCross(CrossAddress memory user) external;
-
-	// /// Remove the user from the allowed list.
-	// ///
-	// /// @param user Address of a removed user.
-	// /// @dev EVM selector for this function is: 0x85c51acb,
-	// ///  or in textual repr: removeFromCollectionAllowList(address)
-	// function removeFromCollectionAllowList(address user) external;
-
-	/// Remove user from allowed list.
-	///
-	/// @param user User cross account address.
-	/// @dev EVM selector for this function is: 0x09ba452a,
-	///  or in textual repr: removeFromCollectionAllowListCross((address,uint256))
-	function removeFromCollectionAllowListCross(CrossAddress memory user) external;
-
-	/// Switch permission for minting.
-	///
-	/// @param mode Enable if "true".
-	/// @dev EVM selector for this function is: 0x00018e84,
-	///  or in textual repr: setCollectionMintMode(bool)
-	function setCollectionMintMode(bool mode) external;
-
-	// /// Check that account is the owner or admin of the collection
-	// ///
-	// /// @param user account to verify
-	// /// @return "true" if account is the owner or admin
-	// /// @dev EVM selector for this function is: 0x9811b0c7,
-	// ///  or in textual repr: isOwnerOrAdmin(address)
-	// function isOwnerOrAdmin(address user) external view returns (bool);
-
-	/// Check that account is the owner or admin of the collection
-	///
-	/// @param user User cross account to verify
-	/// @return "true" if account is the owner or admin
-	/// @dev EVM selector for this function is: 0x3e75a905,
-	///  or in textual repr: isOwnerOrAdminCross((address,uint256))
-	function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool);
-
-	/// Returns collection type
-	///
-	/// @return `Fungible` or `NFT` or `ReFungible`
-	/// @dev EVM selector for this function is: 0xd34b55b8,
-	///  or in textual repr: uniqueCollectionType()
-	function uniqueCollectionType() external view returns (string memory);
-
-	/// Get collection owner.
-	///
-	/// @return Tuble with sponsor address and his substrate mirror.
-	/// If address is canonical then substrate mirror is zero and vice versa.
-	/// @dev EVM selector for this function is: 0xdf727d3b,
-	///  or in textual repr: collectionOwner()
-	function collectionOwner() external view returns (CrossAddress memory);
-
-	// /// Changes collection owner to another account
-	// ///
-	// /// @dev Owner can be changed only by current owner
-	// /// @param newOwner new owner account
-	// /// @dev EVM selector for this function is: 0x4f53e226,
-	// ///  or in textual repr: changeCollectionOwner(address)
-	// function changeCollectionOwner(address newOwner) external;
-
-	/// Get collection administrators
-	///
-	/// @return Vector of tuples with admins address and his substrate mirror.
-	/// If address is canonical then substrate mirror is zero and vice versa.
-	/// @dev EVM selector for this function is: 0x5813216b,
-	///  or in textual repr: collectionAdmins()
-	function collectionAdmins() external view returns (CrossAddress[] memory);
-
-	/// Changes collection owner to another account
-	///
-	/// @dev Owner can be changed only by current owner
-	/// @param newOwner new owner cross account
-	/// @dev EVM selector for this function is: 0x6496c497,
-	///  or in textual repr: changeCollectionOwnerCross((address,uint256))
-	function changeCollectionOwnerCross(CrossAddress memory newOwner) external;
-}
-
-/// Cross account struct
-struct CrossAddress {
-	address eth;
-	uint256 sub;
-}
-
-/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
-enum AccessMode {
-	/// Access grant for owner and admins. Used as default.
-	Normal,
-	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
-	AllowList
-}
-
-/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
-struct CollectionNestingPermission {
-	CollectionPermissionField field;
-	bool value;
-}
-
-/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.
-enum CollectionPermissionField {
-	/// Owner of token can nest tokens under it.
-	TokenOwner,
-	/// Admin of token collection can nest tokens under token.
-	CollectionAdmin
-}
-
-/// Nested collections.
-struct CollectionNesting {
-	bool token_owner;
-	uint256[] ids;
-}
-
-/// Nested collections and permissions
-struct CollectionNestingAndPermission {
-	/// Owner of token can nest tokens under it.
-	bool token_owner;
-	/// Admin of token collection can nest tokens under token.
-	bool collection_admin;
-	/// If set - only tokens from specified collections can be nested.
-	address[] restricted;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
-struct CollectionLimit {
-	CollectionLimitField field;
-	OptionUint256 value;
-}
-
-/// Optional value
-struct OptionUint256 {
-	/// Shows the status of accessibility of value
-	bool status;
-	/// Actual value if `status` is true
-	uint256 value;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
-enum CollectionLimitField {
-	/// How many tokens can a user have on one account.
-	AccountTokenOwnership,
-	/// How many bytes of data are available for sponsorship.
-	SponsoredDataSize,
-	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
-	SponsoredDataRateLimit,
-	/// How many tokens can be mined into this collection.
-	TokenLimit,
-	/// Timeouts for transfer sponsoring.
-	SponsorTransferTimeout,
-	/// Timeout for sponsoring an approval in passed blocks.
-	SponsorApproveTimeout,
-	/// Whether the collection owner of the collection can send tokens (which belong to other users).
-	OwnerCanTransfer,
-	/// Can the collection owner burn other people's tokens.
-	OwnerCanDestroy,
-	/// Is it possible to send tokens from this collection between users.
-	TransferEnabled
-}
-
-/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
-/// @dev See https://eips.ethereum.org/EIPS/eip-721
-/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
-interface ERC721Metadata is Dummy, ERC165 {
-	// /// @notice A descriptive name for a collection of NFTs in this contract
-	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
-	// /// @dev EVM selector for this function is: 0x06fdde03,
-	// ///  or in textual repr: name()
-	// function name() external view returns (string memory);
-
-	// /// @notice An abbreviated name for NFTs in this contract
-	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
-	// /// @dev EVM selector for this function is: 0x95d89b41,
-	// ///  or in textual repr: symbol()
-	// function symbol() external view returns (string memory);
-
-	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	///
-	/// @dev If the token has a `url` property and it is not empty, it is returned.
-	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	///
-	/// @return token's const_metadata
-	/// @dev EVM selector for this function is: 0xc87b56dd,
-	///  or in textual repr: tokenURI(uint256)
-	function tokenURI(uint256 tokenId) external view returns (string memory);
-}
-
-/// @title ERC721 Token that can be irreversibly burned (destroyed).
-/// @dev the ERC-165 identifier for this interface is 0x42966c68
-interface ERC721Burnable is Dummy, ERC165 {
-	/// @notice Burns a specific ERC721 token.
-	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
-	///  operator of the current owner.
-	/// @param tokenId The NFT to approve
-	/// @dev EVM selector for this function is: 0x42966c68,
-	///  or in textual repr: burn(uint256)
-	function burn(uint256 tokenId) external;
-}
-
-/// @title ERC721 minting logic.
-/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
-interface ERC721UniqueMintable is Dummy, ERC165 {
-	/// @notice Function to mint a token.
-	/// @param to The new owner
-	/// @return uint256 The id of the newly minted token
-	/// @dev EVM selector for this function is: 0x6a627842,
-	///  or in textual repr: mint(address)
-	function mint(address to) external returns (uint256);
-
-	// /// @notice Function to mint a token.
-	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
-	// ///  unlike standard, you can't specify it manually
-	// /// @param to The new owner
-	// /// @param tokenId ID of the minted NFT
-	// /// @dev EVM selector for this function is: 0x40c10f19,
-	// ///  or in textual repr: mint(address,uint256)
-	// function mint(address to, uint256 tokenId) external returns (bool);
-
-	/// @notice Function to mint token with the given tokenUri.
-	/// @param to The new owner
-	/// @param tokenUri Token URI that would be stored in the NFT properties
-	/// @return uint256 The id of the newly minted token
-	/// @dev EVM selector for this function is: 0x45c17782,
-	///  or in textual repr: mintWithTokenURI(address,string)
-	function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);
-	// /// @notice Function to mint token with the given tokenUri.
-	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
-	// ///  unlike standard, you can't specify it manually
-	// /// @param to The new owner
-	// /// @param tokenId ID of the minted NFT
-	// /// @param tokenUri Token URI that would be stored in the NFT properties
-	// /// @dev EVM selector for this function is: 0x50bb4e7f,
-	// ///  or in textual repr: mintWithTokenURI(address,uint256,string)
-	// function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);
-
-}
-
-/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x9b397d16
-interface ERC721UniqueExtensions is Dummy, ERC165 {
-	/// @notice A descriptive name for a collection of NFTs in this contract
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() external view returns (string memory);
-
-	/// @notice An abbreviated name for NFTs in this contract
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() external view returns (string memory);
-
-	/// @notice A description for the collection.
-	/// @dev EVM selector for this function is: 0x7284e416,
-	///  or in textual repr: description()
-	function description() external view returns (string memory);
-
-	// /// Returns the owner (in cross format) of the token.
-	// ///
-	// /// @param tokenId Id for the token.
-	// /// @dev EVM selector for this function is: 0x2b29dace,
-	// ///  or in textual repr: crossOwnerOf(uint256)
-	// function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
-
-	/// Returns the owner (in cross format) of the token.
-	///
-	/// @param tokenId Id for the token.
-	/// @dev EVM selector for this function is: 0xcaa3a4d0,
-	///  or in textual repr: ownerOfCross(uint256)
-	function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
-
-	/// @notice Count all NFTs assigned to an owner
-	/// @param owner An cross address for whom to query the balance
-	/// @return The number of NFTs owned by `owner`, possibly zero
-	/// @dev EVM selector for this function is: 0xec069398,
-	///  or in textual repr: balanceOfCross((address,uint256))
-	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
-
-	/// Returns the token properties.
-	///
-	/// @param tokenId Id for the token.
-	/// @param keys Properties keys. Empty keys for all propertyes.
-	/// @return Vector of properties key/value pairs.
-	/// @dev EVM selector for this function is: 0xe07ede7e,
-	///  or in textual repr: properties(uint256,string[])
-	function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory);
-
-	/// @notice Set or reaffirm the approved address for an NFT
-	/// @dev The zero address indicates there is no approved address.
-	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
-	///  operator of the current owner.
-	/// @param approved The new substrate address approved NFT controller
-	/// @param tokenId The NFT to approve
-	/// @dev EVM selector for this function is: 0x0ecd0ab0,
-	///  or in textual repr: approveCross((address,uint256),uint256)
-	function approveCross(CrossAddress memory approved, uint256 tokenId) external;
-
-	/// @notice Transfer ownership of an NFT
-	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	///  is the zero address. Throws if `tokenId` is not a valid NFT.
-	/// @param to The new owner
-	/// @param tokenId The NFT to transfer
-	/// @dev EVM selector for this function is: 0xa9059cbb,
-	///  or in textual repr: transfer(address,uint256)
-	function transfer(address to, uint256 tokenId) external;
-
-	/// @notice Transfer ownership of an NFT
-	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	///  is the zero address. Throws if `tokenId` is not a valid NFT.
-	/// @param to The new owner
-	/// @param tokenId The NFT to transfer
-	/// @dev EVM selector for this function is: 0x2ada85ff,
-	///  or in textual repr: transferCross((address,uint256),uint256)
-	function transferCross(CrossAddress memory to, uint256 tokenId) external;
-
-	/// @notice Transfer ownership of an NFT from cross account address to cross account address
-	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	///  is the zero address. Throws if `tokenId` is not a valid NFT.
-	/// @param from Cross acccount address of current owner
-	/// @param to Cross acccount address of new owner
-	/// @param tokenId The NFT to transfer
-	/// @dev EVM selector for this function is: 0xd5cf430b,
-	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
-	function transferFromCross(
-		CrossAddress memory from,
-		CrossAddress memory to,
-		uint256 tokenId
-	) external;
-
-	// /// @notice Burns a specific ERC721 token.
-	// /// @dev Throws unless `msg.sender` is the current owner or an authorized
-	// ///  operator for this NFT. Throws if `from` is not the current owner. Throws
-	// ///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
-	// /// @param from The current owner of the NFT
-	// /// @param tokenId The NFT to transfer
-	// /// @dev EVM selector for this function is: 0x79cc6790,
-	// ///  or in textual repr: burnFrom(address,uint256)
-	// function burnFrom(address from, uint256 tokenId) external;
-
-	/// @notice Burns a specific ERC721 token.
-	/// @dev Throws unless `msg.sender` is the current owner or an authorized
-	///  operator for this NFT. Throws if `from` is not the current owner. Throws
-	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
-	/// @param from The current owner of the NFT
-	/// @param tokenId The NFT to transfer
-	/// @dev EVM selector for this function is: 0xbb2f5a58,
-	///  or in textual repr: burnFromCross((address,uint256),uint256)
-	function burnFromCross(CrossAddress memory from, uint256 tokenId) external;
-
-	/// @notice Returns next free NFT ID.
-	/// @dev EVM selector for this function is: 0x75794a3c,
-	///  or in textual repr: nextTokenId()
-	function nextTokenId() external view returns (uint256);
-
-	// /// @notice Function to mint multiple tokens.
-	// /// @dev `tokenIds` should be an array of consecutive numbers and first number
-	// ///  should be obtained with `nextTokenId` method
-	// /// @param to The new owner
-	// /// @param tokenIds IDs of the minted NFTs
-	// /// @dev EVM selector for this function is: 0x44a9945e,
-	// ///  or in textual repr: mintBulk(address,uint256[])
-	// function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);
-
-	/// @notice Function to mint a token.
-	/// @param data Array of pairs of token owner and token's properties for minted token
-	/// @dev EVM selector for this function is: 0xab427b0c,
-	///  or in textual repr: mintBulkCross(((address,uint256),(string,bytes)[])[])
-	function mintBulkCross(MintTokenData[] memory data) external returns (bool);
-
-	// /// @notice Function to mint multiple tokens with the given tokenUris.
-	// /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	// ///  numbers and first number should be obtained with `nextTokenId` method
-	// /// @param to The new owner
-	// /// @param tokens array of pairs of token ID and token URI for minted tokens
-	// /// @dev EVM selector for this function is: 0x36543006,
-	// ///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
-	// function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool);
-
-	/// @notice Function to mint a token.
-	/// @param to The new owner crossAccountId
-	/// @param properties Properties of minted token
-	/// @return uint256 The id of the newly minted token
-	/// @dev EVM selector for this function is: 0xb904db03,
-	///  or in textual repr: mintCross((address,uint256),(string,bytes)[])
-	function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256);
-
-	/// @notice Returns collection helper contract address
-	/// @dev EVM selector for this function is: 0x1896cce6,
-	///  or in textual repr: collectionHelperAddress()
-	function collectionHelperAddress() external view returns (address);
-}
-
-/// Data for creation token with uri.
-struct TokenUri {
-	/// Id of new token.
-	uint256 id;
-	/// Uri of new token.
-	string uri;
-}
-
-/// Token minting parameters
-struct MintTokenData {
-	/// Minted token owner
-	CrossAddress owner;
-	/// Minted token properties
-	Property[] properties;
-}
-
-/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
-/// @dev See https://eips.ethereum.org/EIPS/eip-721
-/// @dev the ERC-165 identifier for this interface is 0x780e9d63
-interface ERC721Enumerable is Dummy, ERC165 {
-	/// @notice Enumerate valid NFTs
-	/// @param index A counter less than `totalSupply()`
-	/// @return The token identifier for the `index`th NFT,
-	///  (sort order not specified)
-	/// @dev EVM selector for this function is: 0x4f6ccce7,
-	///  or in textual repr: tokenByIndex(uint256)
-	function tokenByIndex(uint256 index) external view returns (uint256);
-
-	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0x2f745c59,
-	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
-	function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
-
-	/// @notice Count NFTs tracked by this contract
-	/// @return A count of valid NFTs tracked by this contract, where each one of
-	///  them has an assigned and queryable owner not equal to the zero address
-	/// @dev EVM selector for this function is: 0x18160ddd,
-	///  or in textual repr: totalSupply()
-	function totalSupply() external view returns (uint256);
-}
-
-/// @dev inlined interface
-interface ERC721Events {
-	event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
-	event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
-	event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
-}
-
-/// @title ERC-721 Non-Fungible Token Standard
-/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
-interface ERC721 is Dummy, ERC165, ERC721Events {
-	/// @notice Count all NFTs assigned to an owner
-	/// @dev NFTs assigned to the zero address are considered invalid, and this
-	///  function throws for queries about the zero address.
-	/// @param owner An address for whom to query the balance
-	/// @return The number of NFTs owned by `owner`, possibly zero
-	/// @dev EVM selector for this function is: 0x70a08231,
-	///  or in textual repr: balanceOf(address)
-	function balanceOf(address owner) external view returns (uint256);
-
-	/// @notice Find the owner of an NFT
-	/// @dev NFTs assigned to zero address are considered invalid, and queries
-	///  about them do throw.
-	/// @param tokenId The identifier for an NFT
-	/// @return The address of the owner of the NFT
-	/// @dev EVM selector for this function is: 0x6352211e,
-	///  or in textual repr: ownerOf(uint256)
-	function ownerOf(uint256 tokenId) external view returns (address);
-
-	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0xb88d4fde,
-	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId,
-		bytes memory data
-	) external;
-
-	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0x42842e0e,
-	///  or in textual repr: safeTransferFrom(address,address,uint256)
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
-
-	/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
-	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
-	///  THEY MAY BE PERMANENTLY LOST
-	/// @dev Throws unless `msg.sender` is the current owner or an authorized
-	///  operator for this NFT. Throws if `from` is not the current owner. Throws
-	///  if `to` is the zero address. Throws if `tokenId` is not a valid NFT.
-	/// @param from The current owner of the NFT
-	/// @param to The new owner
-	/// @param tokenId The NFT to transfer
-	/// @dev EVM selector for this function is: 0x23b872dd,
-	///  or in textual repr: transferFrom(address,address,uint256)
-	function transferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
-
-	/// @notice Set or reaffirm the approved address for an NFT
-	/// @dev The zero address indicates there is no approved address.
-	/// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized
-	///  operator of the current owner.
-	/// @param approved The new approved NFT controller
-	/// @param tokenId The NFT to approve
-	/// @dev EVM selector for this function is: 0x095ea7b3,
-	///  or in textual repr: approve(address,uint256)
-	function approve(address approved, uint256 tokenId) external;
-
-	/// @notice Sets or unsets the approval of a given operator.
-	/// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.
-	/// @param operator Operator
-	/// @param approved Should operator status be granted or revoked?
-	/// @dev EVM selector for this function is: 0xa22cb465,
-	///  or in textual repr: setApprovalForAll(address,bool)
-	function setApprovalForAll(address operator, bool approved) external;
-
-	/// @notice Get the approved address for a single NFT
-	/// @dev Throws if `tokenId` is not a valid NFT
-	/// @param tokenId The NFT to find the approved address for
-	/// @return The approved address for this NFT, or the zero address if there is none
-	/// @dev EVM selector for this function is: 0x081812fc,
-	///  or in textual repr: getApproved(uint256)
-	function getApproved(uint256 tokenId) external view returns (address);
-
-	/// @notice Tells whether the given `owner` approves the `operator`.
-	/// @dev EVM selector for this function is: 0xe985e9c5,
-	///  or in textual repr: isApprovedForAll(address,address)
-	function isApprovedForAll(address owner, address operator) external view returns (bool);
-}
-
-interface UniqueNFT is
-	Dummy,
-	ERC165,
-	ERC721,
-	ERC721Enumerable,
-	ERC721UniqueExtensions,
-	ERC721UniqueMintable,
-	ERC721Burnable,
-	ERC721Metadata,
-	Collection,
-	TokenProperties
-{}
deletedjs-packages/tests/src/eth/api/UniqueNativeFungible.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/api/UniqueNativeFungible.sol
+++ /dev/null
@@ -1,89 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-/// @dev common stubs holder
-interface Dummy {
-
-}
-
-interface ERC165 is Dummy {
-	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-/// @dev the ERC-165 identifier for this interface is 0x1313556c
-interface ERC20UniqueExtensions is Dummy, ERC165 {
-	/// @dev EVM selector for this function is: 0xec069398,
-	///  or in textual repr: balanceOfCross((address,uint256))
-	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
-
-	/// @dev EVM selector for this function is: 0x2ada85ff,
-	///  or in textual repr: transferCross((address,uint256),uint256)
-	function transferCross(CrossAddress memory to, uint256 amount) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0xd5cf430b,
-	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
-	function transferFromCross(
-		CrossAddress memory from,
-		CrossAddress memory to,
-		uint256 amount
-	) external returns (bool);
-}
-
-/// Cross account struct
-struct CrossAddress {
-	address eth;
-	uint256 sub;
-}
-
-/// @dev inlined interface
-interface ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(address indexed owner, address indexed spender, uint256 value);
-}
-
-/// @dev the ERC-165 identifier for this interface is 0x942e8b22
-interface ERC20 is Dummy, ERC165, ERC20Events {
-	/// @dev EVM selector for this function is: 0xdd62ed3e,
-	///  or in textual repr: allowance(address,address)
-	function allowance(address owner, address spender) external view returns (uint256);
-
-	/// @dev EVM selector for this function is: 0x095ea7b3,
-	///  or in textual repr: approve(address,uint256)
-	function approve(address spender, uint256 amount) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0x70a08231,
-	///  or in textual repr: balanceOf(address)
-	function balanceOf(address owner) external view returns (uint256);
-
-	/// @dev EVM selector for this function is: 0x313ce567,
-	///  or in textual repr: decimals()
-	function decimals() external view returns (uint8);
-
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() external view returns (string memory);
-
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() external view returns (string memory);
-
-	/// @dev EVM selector for this function is: 0x18160ddd,
-	///  or in textual repr: totalSupply()
-	function totalSupply() external view returns (uint256);
-
-	/// @dev EVM selector for this function is: 0xa9059cbb,
-	///  or in textual repr: transfer(address,uint256)
-	function transfer(address to, uint256 amount) external returns (bool);
-
-	/// @dev EVM selector for this function is: 0x23b872dd,
-	///  or in textual repr: transferFrom(address,address,uint256)
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) external returns (bool);
-}
-
-interface UniqueNativeFungible is Dummy, ERC165, ERC20, ERC20UniqueExtensions {}
deletedjs-packages/tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/api/UniqueRefungible.sol
+++ /dev/null
@@ -1,859 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-/// @dev common stubs holder
-interface Dummy {
-
-}
-
-interface ERC165 is Dummy {
-	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-/// @dev inlined interface
-interface ERC721TokenEvent {
-	event TokenChanged(uint256 indexed tokenId);
-}
-
-/// @title A contract that allows to set and delete token properties and change token property permissions.
-/// @dev the ERC-165 identifier for this interface is 0xde0695c2
-interface TokenProperties is Dummy, ERC165, ERC721TokenEvent {
-	// /// @notice Set permissions for token property.
-	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	// /// @param key Property key.
-	// /// @param isMutable Permission to mutate property.
-	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
-	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
-	// /// @dev EVM selector for this function is: 0x222d97fa,
-	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
-	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external;
-
-	/// @notice Set permissions for token property.
-	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	/// @param permissions Permissions for keys.
-	/// @dev EVM selector for this function is: 0xbd92983a,
-	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
-	function setTokenPropertyPermissions(TokenPropertyPermission[] memory permissions) external;
-
-	/// @notice Get permissions for token properties.
-	/// @dev EVM selector for this function is: 0xf23d7790,
-	///  or in textual repr: tokenPropertyPermissions()
-	function tokenPropertyPermissions() external view returns (TokenPropertyPermission[] memory);
-
-	// /// @notice Set token property value.
-	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// /// @param tokenId ID of the token.
-	// /// @param key Property key.
-	// /// @param value Property value.
-	// /// @dev EVM selector for this function is: 0x1752d67b,
-	// ///  or in textual repr: setProperty(uint256,string,bytes)
-	// function setProperty(uint256 tokenId, string memory key, bytes memory value) external;
-
-	/// @notice Set token properties value.
-	/// @dev Throws error if `msg.sender` has no permission to edit the property.
-	/// @param tokenId ID of the token.
-	/// @param properties settable properties
-	/// @dev EVM selector for this function is: 0x14ed3a6e,
-	///  or in textual repr: setProperties(uint256,(string,bytes)[])
-	function setProperties(uint256 tokenId, Property[] memory properties) external;
-
-	// /// @notice Delete token property value.
-	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
-	// /// @param tokenId ID of the token.
-	// /// @param key Property key.
-	// /// @dev EVM selector for this function is: 0x066111d1,
-	// ///  or in textual repr: deleteProperty(uint256,string)
-	// function deleteProperty(uint256 tokenId, string memory key) external;
-
-	/// @notice Delete token properties value.
-	/// @dev Throws error if `msg.sender` has no permission to edit the property.
-	/// @param tokenId ID of the token.
-	/// @param keys Properties key.
-	/// @dev EVM selector for this function is: 0xc472d371,
-	///  or in textual repr: deleteProperties(uint256,string[])
-	function deleteProperties(uint256 tokenId, string[] memory keys) external;
-
-	/// @notice Get token property value.
-	/// @dev Throws error if key not found
-	/// @param tokenId ID of the token.
-	/// @param key Property key.
-	/// @return Property value bytes
-	/// @dev EVM selector for this function is: 0x7228c327,
-	///  or in textual repr: property(uint256,string)
-	function property(uint256 tokenId, string memory key) external view returns (bytes memory);
-}
-
-/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).
-struct Property {
-	string key;
-	bytes value;
-}
-
-/// Ethereum representation of Token Property Permissions.
-struct TokenPropertyPermission {
-	/// Token property key.
-	string key;
-	/// Token property permissions.
-	PropertyPermission[] permissions;
-}
-
-/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.
-struct PropertyPermission {
-	/// TokenPermission field.
-	TokenPermissionField code;
-	/// TokenPermission value.
-	bool value;
-}
-
-/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.
-enum TokenPermissionField {
-	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]
-	Mutable,
-	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]
-	TokenOwner,
-	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]
-	CollectionAdmin
-}
-
-/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xb34d97e9
-interface Collection is Dummy, ERC165 {
-	// /// Set collection property.
-	// ///
-	// /// @param key Property key.
-	// /// @param value Propery value.
-	// /// @dev EVM selector for this function is: 0x2f073f66,
-	// ///  or in textual repr: setCollectionProperty(string,bytes)
-	// function setCollectionProperty(string memory key, bytes memory value) external;
-
-	/// Set collection properties.
-	///
-	/// @param properties Vector of properties key/value pair.
-	/// @dev EVM selector for this function is: 0x50b26b2a,
-	///  or in textual repr: setCollectionProperties((string,bytes)[])
-	function setCollectionProperties(Property[] memory properties) external;
-
-	// /// Delete collection property.
-	// ///
-	// /// @param key Property key.
-	// /// @dev EVM selector for this function is: 0x7b7debce,
-	// ///  or in textual repr: deleteCollectionProperty(string)
-	// function deleteCollectionProperty(string memory key) external;
-
-	/// Delete collection properties.
-	///
-	/// @param keys Properties keys.
-	/// @dev EVM selector for this function is: 0xee206ee3,
-	///  or in textual repr: deleteCollectionProperties(string[])
-	function deleteCollectionProperties(string[] memory keys) external;
-
-	/// Get collection property.
-	///
-	/// @dev Throws error if key not found.
-	///
-	/// @param key Property key.
-	/// @return bytes The property corresponding to the key.
-	/// @dev EVM selector for this function is: 0xcf24fd6d,
-	///  or in textual repr: collectionProperty(string)
-	function collectionProperty(string memory key) external view returns (bytes memory);
-
-	/// Get collection properties.
-	///
-	/// @param keys Properties keys. Empty keys for all propertyes.
-	/// @return Vector of properties key/value pairs.
-	/// @dev EVM selector for this function is: 0x285fb8e6,
-	///  or in textual repr: collectionProperties(string[])
-	function collectionProperties(string[] memory keys) external view returns (Property[] memory);
-
-	// /// Set the sponsor of the collection.
-	// ///
-	// /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	// ///
-	// /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
-	// /// @dev EVM selector for this function is: 0x7623402e,
-	// ///  or in textual repr: setCollectionSponsor(address)
-	// function setCollectionSponsor(address sponsor) external;
-
-	/// Set the sponsor of the collection.
-	///
-	/// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.
-	///
-	/// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.
-	/// @dev EVM selector for this function is: 0x84a1d5a8,
-	///  or in textual repr: setCollectionSponsorCross((address,uint256))
-	function setCollectionSponsorCross(CrossAddress memory sponsor) external;
-
-	/// Whether there is a pending sponsor.
-	/// @dev EVM selector for this function is: 0x058ac185,
-	///  or in textual repr: hasCollectionPendingSponsor()
-	function hasCollectionPendingSponsor() external view returns (bool);
-
-	/// Collection sponsorship confirmation.
-	///
-	/// @dev After setting the sponsor for the collection, it must be confirmed with this function.
-	/// @dev EVM selector for this function is: 0x3c50e97a,
-	///  or in textual repr: confirmCollectionSponsorship()
-	function confirmCollectionSponsorship() external;
-
-	/// Remove collection sponsor.
-	/// @dev EVM selector for this function is: 0x6e0326a3,
-	///  or in textual repr: removeCollectionSponsor()
-	function removeCollectionSponsor() external;
-
-	/// Get current sponsor.
-	///
-	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
-	/// @dev EVM selector for this function is: 0x6ec0a9f1,
-	///  or in textual repr: collectionSponsor()
-	function collectionSponsor() external view returns (CrossAddress memory);
-
-	/// Get current collection limits.
-	///
-	/// @return Array of collection limits
-	/// @dev EVM selector for this function is: 0xf63bc572,
-	///  or in textual repr: collectionLimits()
-	function collectionLimits() external view returns (CollectionLimit[] memory);
-
-	/// Set limits for the collection.
-	/// @dev Throws error if limit not found.
-	/// @param limit Some limit.
-	/// @dev EVM selector for this function is: 0x2316ee74,
-	///  or in textual repr: setCollectionLimit((uint8,(bool,uint256)))
-	function setCollectionLimit(CollectionLimit memory limit) external;
-
-	/// Get contract address.
-	/// @dev EVM selector for this function is: 0xf6b4dfb4,
-	///  or in textual repr: contractAddress()
-	function contractAddress() external view returns (address);
-
-	/// Add collection admin.
-	/// @param newAdmin Cross account administrator address.
-	/// @dev EVM selector for this function is: 0x859aa7d6,
-	///  or in textual repr: addCollectionAdminCross((address,uint256))
-	function addCollectionAdminCross(CrossAddress memory newAdmin) external;
-
-	/// Remove collection admin.
-	/// @param admin Cross account administrator address.
-	/// @dev EVM selector for this function is: 0x6c0cd173,
-	///  or in textual repr: removeCollectionAdminCross((address,uint256))
-	function removeCollectionAdminCross(CrossAddress memory admin) external;
-
-	// /// Add collection admin.
-	// /// @param newAdmin Address of the added administrator.
-	// /// @dev EVM selector for this function is: 0x92e462c7,
-	// ///  or in textual repr: addCollectionAdmin(address)
-	// function addCollectionAdmin(address newAdmin) external;
-
-	// /// Remove collection admin.
-	// ///
-	// /// @param admin Address of the removed administrator.
-	// /// @dev EVM selector for this function is: 0xfafd7b42,
-	// ///  or in textual repr: removeCollectionAdmin(address)
-	// function removeCollectionAdmin(address admin) external;
-
-	/// @dev EVM selector for this function is: 0x0b9f3890,
-	///  or in textual repr: setCollectionNesting((bool,bool,address[]))
-	function setCollectionNesting(CollectionNestingAndPermission memory collectionNestingAndPermissions) external;
-
-	// /// Toggle accessibility of collection nesting.
-	// ///
-	// /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
-	// /// @dev EVM selector for this function is: 0x112d4586,
-	// ///  or in textual repr: setCollectionNesting(bool)
-	// function setCollectionNesting(bool enable) external;
-
-	// /// Toggle accessibility of collection nesting.
-	// ///
-	// /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'
-	// /// @param collections Addresses of collections that will be available for nesting.
-	// /// @dev EVM selector for this function is: 0x64872396,
-	// ///  or in textual repr: setCollectionNesting(bool,address[])
-	// function setCollectionNesting(bool enable, address[] memory collections) external;
-
-	/// @dev EVM selector for this function is: 0x92c660a8,
-	///  or in textual repr: collectionNesting()
-	function collectionNesting() external view returns (CollectionNestingAndPermission memory);
-
-	// /// Returns nesting for a collection
-	// /// @dev EVM selector for this function is: 0x22d25bfe,
-	// ///  or in textual repr: collectionNestingRestrictedCollectionIds()
-	// function collectionNestingRestrictedCollectionIds() external view returns (CollectionNesting memory);
-
-	// /// Returns permissions for a collection
-	// /// @dev EVM selector for this function is: 0x5b2eaf4b,
-	// ///  or in textual repr: collectionNestingPermissions()
-	// function collectionNestingPermissions() external view returns (CollectionNestingPermission[] memory);
-
-	/// Set the collection access method.
-	/// @param mode Access mode
-	/// @dev EVM selector for this function is: 0x41835d4c,
-	///  or in textual repr: setCollectionAccess(uint8)
-	function setCollectionAccess(AccessMode mode) external;
-
-	/// Checks that user allowed to operate with collection.
-	///
-	/// @param user User address to check.
-	/// @dev EVM selector for this function is: 0x91b6df49,
-	///  or in textual repr: allowlistedCross((address,uint256))
-	function allowlistedCross(CrossAddress memory user) external view returns (bool);
-
-	// /// Add the user to the allowed list.
-	// ///
-	// /// @param user Address of a trusted user.
-	// /// @dev EVM selector for this function is: 0x67844fe6,
-	// ///  or in textual repr: addToCollectionAllowList(address)
-	// function addToCollectionAllowList(address user) external;
-
-	/// Add user to allowed list.
-	///
-	/// @param user User cross account address.
-	/// @dev EVM selector for this function is: 0xa0184a3a,
-	///  or in textual repr: addToCollectionAllowListCross((address,uint256))
-	function addToCollectionAllowListCross(CrossAddress memory user) external;
-
-	// /// Remove the user from the allowed list.
-	// ///
-	// /// @param user Address of a removed user.
-	// /// @dev EVM selector for this function is: 0x85c51acb,
-	// ///  or in textual repr: removeFromCollectionAllowList(address)
-	// function removeFromCollectionAllowList(address user) external;
-
-	/// Remove user from allowed list.
-	///
-	/// @param user User cross account address.
-	/// @dev EVM selector for this function is: 0x09ba452a,
-	///  or in textual repr: removeFromCollectionAllowListCross((address,uint256))
-	function removeFromCollectionAllowListCross(CrossAddress memory user) external;
-
-	/// Switch permission for minting.
-	///
-	/// @param mode Enable if "true".
-	/// @dev EVM selector for this function is: 0x00018e84,
-	///  or in textual repr: setCollectionMintMode(bool)
-	function setCollectionMintMode(bool mode) external;
-
-	// /// Check that account is the owner or admin of the collection
-	// ///
-	// /// @param user account to verify
-	// /// @return "true" if account is the owner or admin
-	// /// @dev EVM selector for this function is: 0x9811b0c7,
-	// ///  or in textual repr: isOwnerOrAdmin(address)
-	// function isOwnerOrAdmin(address user) external view returns (bool);
-
-	/// Check that account is the owner or admin of the collection
-	///
-	/// @param user User cross account to verify
-	/// @return "true" if account is the owner or admin
-	/// @dev EVM selector for this function is: 0x3e75a905,
-	///  or in textual repr: isOwnerOrAdminCross((address,uint256))
-	function isOwnerOrAdminCross(CrossAddress memory user) external view returns (bool);
-
-	/// Returns collection type
-	///
-	/// @return `Fungible` or `NFT` or `ReFungible`
-	/// @dev EVM selector for this function is: 0xd34b55b8,
-	///  or in textual repr: uniqueCollectionType()
-	function uniqueCollectionType() external view returns (string memory);
-
-	/// Get collection owner.
-	///
-	/// @return Tuble with sponsor address and his substrate mirror.
-	/// If address is canonical then substrate mirror is zero and vice versa.
-	/// @dev EVM selector for this function is: 0xdf727d3b,
-	///  or in textual repr: collectionOwner()
-	function collectionOwner() external view returns (CrossAddress memory);
-
-	// /// Changes collection owner to another account
-	// ///
-	// /// @dev Owner can be changed only by current owner
-	// /// @param newOwner new owner account
-	// /// @dev EVM selector for this function is: 0x4f53e226,
-	// ///  or in textual repr: changeCollectionOwner(address)
-	// function changeCollectionOwner(address newOwner) external;
-
-	/// Get collection administrators
-	///
-	/// @return Vector of tuples with admins address and his substrate mirror.
-	/// If address is canonical then substrate mirror is zero and vice versa.
-	/// @dev EVM selector for this function is: 0x5813216b,
-	///  or in textual repr: collectionAdmins()
-	function collectionAdmins() external view returns (CrossAddress[] memory);
-
-	/// Changes collection owner to another account
-	///
-	/// @dev Owner can be changed only by current owner
-	/// @param newOwner new owner cross account
-	/// @dev EVM selector for this function is: 0x6496c497,
-	///  or in textual repr: changeCollectionOwnerCross((address,uint256))
-	function changeCollectionOwnerCross(CrossAddress memory newOwner) external;
-}
-
-/// Cross account struct
-struct CrossAddress {
-	address eth;
-	uint256 sub;
-}
-
-/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).
-enum AccessMode {
-	/// Access grant for owner and admins. Used as default.
-	Normal,
-	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.
-	AllowList
-}
-
-/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.
-struct CollectionNestingPermission {
-	CollectionPermissionField field;
-	bool value;
-}
-
-/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.
-enum CollectionPermissionField {
-	/// Owner of token can nest tokens under it.
-	TokenOwner,
-	/// Admin of token collection can nest tokens under token.
-	CollectionAdmin
-}
-
-/// Nested collections.
-struct CollectionNesting {
-	bool token_owner;
-	uint256[] ids;
-}
-
-/// Nested collections and permissions
-struct CollectionNestingAndPermission {
-	/// Owner of token can nest tokens under it.
-	bool token_owner;
-	/// Admin of token collection can nest tokens under token.
-	bool collection_admin;
-	/// If set - only tokens from specified collections can be nested.
-	address[] restricted;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.
-struct CollectionLimit {
-	CollectionLimitField field;
-	OptionUint256 value;
-}
-
-/// Optional value
-struct OptionUint256 {
-	/// Shows the status of accessibility of value
-	bool status;
-	/// Actual value if `status` is true
-	uint256 value;
-}
-
-/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.
-enum CollectionLimitField {
-	/// How many tokens can a user have on one account.
-	AccountTokenOwnership,
-	/// How many bytes of data are available for sponsorship.
-	SponsoredDataSize,
-	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]
-	SponsoredDataRateLimit,
-	/// How many tokens can be mined into this collection.
-	TokenLimit,
-	/// Timeouts for transfer sponsoring.
-	SponsorTransferTimeout,
-	/// Timeout for sponsoring an approval in passed blocks.
-	SponsorApproveTimeout,
-	/// Whether the collection owner of the collection can send tokens (which belong to other users).
-	OwnerCanTransfer,
-	/// Can the collection owner burn other people's tokens.
-	OwnerCanDestroy,
-	/// Is it possible to send tokens from this collection between users.
-	TransferEnabled
-}
-
-/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
-/// @dev See https://eips.ethereum.org/EIPS/eip-721
-/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
-interface ERC721Metadata is Dummy, ERC165 {
-	// /// @notice A descriptive name for a collection of NFTs in this contract
-	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
-	// /// @dev EVM selector for this function is: 0x06fdde03,
-	// ///  or in textual repr: name()
-	// function name() external view returns (string memory);
-
-	// /// @notice An abbreviated name for NFTs in this contract
-	// /// @dev real implementation of this function lies in `ERC721UniqueExtensions`
-	// /// @dev EVM selector for this function is: 0x95d89b41,
-	// ///  or in textual repr: symbol()
-	// function symbol() external view returns (string memory);
-
-	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	///
-	/// @dev If the token has a `url` property and it is not empty, it is returned.
-	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	///
-	/// @return token's const_metadata
-	/// @dev EVM selector for this function is: 0xc87b56dd,
-	///  or in textual repr: tokenURI(uint256)
-	function tokenURI(uint256 tokenId) external view returns (string memory);
-}
-
-/// @title ERC721 Token that can be irreversibly burned (destroyed).
-/// @dev the ERC-165 identifier for this interface is 0x42966c68
-interface ERC721Burnable is Dummy, ERC165 {
-	/// @notice Burns a specific ERC721 token.
-	/// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
-	///  operator of the current owner.
-	/// @param tokenId The RFT to approve
-	/// @dev EVM selector for this function is: 0x42966c68,
-	///  or in textual repr: burn(uint256)
-	function burn(uint256 tokenId) external;
-}
-
-/// @title ERC721 minting logic.
-/// @dev the ERC-165 identifier for this interface is 0x3fd94ea6
-interface ERC721UniqueMintable is Dummy, ERC165 {
-	/// @notice Function to mint a token.
-	/// @param to The new owner
-	/// @return uint256 The id of the newly minted token
-	/// @dev EVM selector for this function is: 0x6a627842,
-	///  or in textual repr: mint(address)
-	function mint(address to) external returns (uint256);
-
-	// /// @notice Function to mint a token.
-	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
-	// ///  unlike standard, you can't specify it manually
-	// /// @param to The new owner
-	// /// @param tokenId ID of the minted RFT
-	// /// @dev EVM selector for this function is: 0x40c10f19,
-	// ///  or in textual repr: mint(address,uint256)
-	// function mint(address to, uint256 tokenId) external returns (bool);
-
-	/// @notice Function to mint token with the given tokenUri.
-	/// @param to The new owner
-	/// @param tokenUri Token URI that would be stored in the NFT properties
-	/// @return uint256 The id of the newly minted token
-	/// @dev EVM selector for this function is: 0x45c17782,
-	///  or in textual repr: mintWithTokenURI(address,string)
-	function mintWithTokenURI(address to, string memory tokenUri) external returns (uint256);
-	// /// @notice Function to mint token with the given tokenUri.
-	// /// @dev `tokenId` should be obtained with `nextTokenId` method,
-	// ///  unlike standard, you can't specify it manually
-	// /// @param to The new owner
-	// /// @param tokenId ID of the minted RFT
-	// /// @param tokenUri Token URI that would be stored in the RFT properties
-	// /// @dev EVM selector for this function is: 0x50bb4e7f,
-	// ///  or in textual repr: mintWithTokenURI(address,uint256,string)
-	// function mintWithTokenURI(address to, uint256 tokenId, string memory tokenUri) external returns (bool);
-
-}
-
-/// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x4abaabdb
-interface ERC721UniqueExtensions is Dummy, ERC165 {
-	/// @notice A descriptive name for a collection of NFTs in this contract
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() external view returns (string memory);
-
-	/// @notice An abbreviated name for NFTs in this contract
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() external view returns (string memory);
-
-	/// @notice A description for the collection.
-	/// @dev EVM selector for this function is: 0x7284e416,
-	///  or in textual repr: description()
-	function description() external view returns (string memory);
-
-	// /// Returns the owner (in cross format) of the token.
-	// ///
-	// /// @param tokenId Id for the token.
-	// /// @dev EVM selector for this function is: 0x2b29dace,
-	// ///  or in textual repr: crossOwnerOf(uint256)
-	// function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
-
-	/// Returns the owner (in cross format) of the token.
-	///
-	/// @param tokenId Id for the token.
-	/// @dev EVM selector for this function is: 0xcaa3a4d0,
-	///  or in textual repr: ownerOfCross(uint256)
-	function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
-
-	/// @notice Count all RFTs assigned to an owner
-	/// @param owner An cross address for whom to query the balance
-	/// @return The number of RFTs owned by `owner`, possibly zero
-	/// @dev EVM selector for this function is: 0xec069398,
-	///  or in textual repr: balanceOfCross((address,uint256))
-	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
-
-	/// Returns the token properties.
-	///
-	/// @param tokenId Id for the token.
-	/// @param keys Properties keys. Empty keys for all propertyes.
-	/// @return Vector of properties key/value pairs.
-	/// @dev EVM selector for this function is: 0xe07ede7e,
-	///  or in textual repr: properties(uint256,string[])
-	function properties(uint256 tokenId, string[] memory keys) external view returns (Property[] memory);
-
-	/// @notice Transfer ownership of an RFT
-	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	///  is the zero address. Throws if `tokenId` is not a valid RFT.
-	///  Throws if RFT pieces have multiple owners.
-	/// @param to The new owner
-	/// @param tokenId The RFT to transfer
-	/// @dev EVM selector for this function is: 0xa9059cbb,
-	///  or in textual repr: transfer(address,uint256)
-	function transfer(address to, uint256 tokenId) external;
-
-	/// @notice Transfer ownership of an RFT
-	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	///  is the zero address. Throws if `tokenId` is not a valid RFT.
-	///  Throws if RFT pieces have multiple owners.
-	/// @param to The new owner
-	/// @param tokenId The RFT to transfer
-	/// @dev EVM selector for this function is: 0x2ada85ff,
-	///  or in textual repr: transferCross((address,uint256),uint256)
-	function transferCross(CrossAddress memory to, uint256 tokenId) external;
-
-	/// @notice Transfer ownership of an RFT
-	/// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	///  is the zero address. Throws if `tokenId` is not a valid RFT.
-	///  Throws if RFT pieces have multiple owners.
-	/// @param to The new owner
-	/// @param tokenId The RFT to transfer
-	/// @dev EVM selector for this function is: 0xd5cf430b,
-	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
-	function transferFromCross(
-		CrossAddress memory from,
-		CrossAddress memory to,
-		uint256 tokenId
-	) external;
-
-	// /// @notice Burns a specific ERC721 token.
-	// /// @dev Throws unless `msg.sender` is the current owner or an authorized
-	// ///  operator for this RFT. Throws if `from` is not the current owner. Throws
-	// ///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	// ///  Throws if RFT pieces have multiple owners.
-	// /// @param from The current owner of the RFT
-	// /// @param tokenId The RFT to transfer
-	// /// @dev EVM selector for this function is: 0x79cc6790,
-	// ///  or in textual repr: burnFrom(address,uint256)
-	// function burnFrom(address from, uint256 tokenId) external;
-
-	/// @notice Burns a specific ERC721 token.
-	/// @dev Throws unless `msg.sender` is the current owner or an authorized
-	///  operator for this RFT. Throws if `from` is not the current owner. Throws
-	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	///  Throws if RFT pieces have multiple owners.
-	/// @param from The current owner of the RFT
-	/// @param tokenId The RFT to transfer
-	/// @dev EVM selector for this function is: 0xbb2f5a58,
-	///  or in textual repr: burnFromCross((address,uint256),uint256)
-	function burnFromCross(CrossAddress memory from, uint256 tokenId) external;
-
-	/// @notice Returns next free RFT ID.
-	/// @dev EVM selector for this function is: 0x75794a3c,
-	///  or in textual repr: nextTokenId()
-	function nextTokenId() external view returns (uint256);
-
-	// /// @notice Function to mint multiple tokens.
-	// /// @dev `tokenIds` should be an array of consecutive numbers and first number
-	// ///  should be obtained with `nextTokenId` method
-	// /// @param to The new owner
-	// /// @param tokenIds IDs of the minted RFTs
-	// /// @dev EVM selector for this function is: 0x44a9945e,
-	// ///  or in textual repr: mintBulk(address,uint256[])
-	// function mintBulk(address to, uint256[] memory tokenIds) external returns (bool);
-
-	/// @notice Function to mint a token.
-	/// @param tokenProperties Properties of minted token
-	/// @dev EVM selector for this function is: 0xdf7a5db7,
-	///  or in textual repr: mintBulkCross((((address,uint256),uint128)[],(string,bytes)[])[])
-	function mintBulkCross(MintTokenData[] memory tokenProperties) external returns (bool);
-
-	// /// @notice Function to mint multiple tokens with the given tokenUris.
-	// /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	// ///  numbers and first number should be obtained with `nextTokenId` method
-	// /// @param to The new owner
-	// /// @param tokens array of pairs of token ID and token URI for minted tokens
-	// /// @dev EVM selector for this function is: 0x36543006,
-	// ///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
-	// function mintBulkWithTokenURI(address to, TokenUri[] memory tokens) external returns (bool);
-
-	/// @notice Function to mint a token.
-	/// @param to The new owner crossAccountId
-	/// @param properties Properties of minted token
-	/// @return uint256 The id of the newly minted token
-	/// @dev EVM selector for this function is: 0xb904db03,
-	///  or in textual repr: mintCross((address,uint256),(string,bytes)[])
-	function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256);
-
-	/// Returns EVM address for refungible token
-	///
-	/// @param token ID of the token
-	/// @dev EVM selector for this function is: 0xab76fac6,
-	///  or in textual repr: tokenContractAddress(uint256)
-	function tokenContractAddress(uint256 token) external view returns (address);
-
-	/// @notice Returns collection helper contract address
-	/// @dev EVM selector for this function is: 0x1896cce6,
-	///  or in textual repr: collectionHelperAddress()
-	function collectionHelperAddress() external view returns (address);
-}
-
-/// Data for creation token with uri.
-struct TokenUri {
-	/// Id of new token.
-	uint256 id;
-	/// Uri of new token.
-	string uri;
-}
-
-/// Token minting parameters
-struct MintTokenData {
-	/// Minted token owner and number of pieces
-	OwnerPieces[] owners;
-	/// Minted token properties
-	Property[] properties;
-}
-
-/// Token minting parameters
-struct OwnerPieces {
-	/// Minted token owner
-	CrossAddress owner;
-	/// Number of token pieces
-	uint128 pieces;
-}
-
-/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
-/// @dev See https://eips.ethereum.org/EIPS/eip-721
-/// @dev the ERC-165 identifier for this interface is 0x780e9d63
-interface ERC721Enumerable is Dummy, ERC165 {
-	/// @notice Enumerate valid RFTs
-	/// @param index A counter less than `totalSupply()`
-	/// @return The token identifier for the `index`th NFT,
-	///  (sort order not specified)
-	/// @dev EVM selector for this function is: 0x4f6ccce7,
-	///  or in textual repr: tokenByIndex(uint256)
-	function tokenByIndex(uint256 index) external view returns (uint256);
-
-	/// Not implemented
-	/// @dev EVM selector for this function is: 0x2f745c59,
-	///  or in textual repr: tokenOfOwnerByIndex(address,uint256)
-	function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
-
-	/// @notice Count RFTs tracked by this contract
-	/// @return A count of valid RFTs tracked by this contract, where each one of
-	///  them has an assigned and queryable owner not equal to the zero address
-	/// @dev EVM selector for this function is: 0x18160ddd,
-	///  or in textual repr: totalSupply()
-	function totalSupply() external view returns (uint256);
-}
-
-/// @dev inlined interface
-interface ERC721Events {
-	event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
-	event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
-	event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
-}
-
-/// @title ERC-721 Non-Fungible Token Standard
-/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
-interface ERC721 is Dummy, ERC165, ERC721Events {
-	/// @notice Count all RFTs assigned to an owner
-	/// @dev RFTs assigned to the zero address are considered invalid, and this
-	///  function throws for queries about the zero address.
-	/// @param owner An address for whom to query the balance
-	/// @return The number of RFTs owned by `owner`, possibly zero
-	/// @dev EVM selector for this function is: 0x70a08231,
-	///  or in textual repr: balanceOf(address)
-	function balanceOf(address owner) external view returns (uint256);
-
-	/// @notice Find the owner of an RFT
-	/// @dev RFTs assigned to zero address are considered invalid, and queries
-	///  about them do throw.
-	///  Returns special 0xffffffffffffffffffffffffffffffffffffffff address for
-	///  the tokens that are partially owned.
-	/// @param tokenId The identifier for an RFT
-	/// @return The address of the owner of the RFT
-	/// @dev EVM selector for this function is: 0x6352211e,
-	///  or in textual repr: ownerOf(uint256)
-	function ownerOf(uint256 tokenId) external view returns (address);
-
-	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0xb88d4fde,
-	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId,
-		bytes memory data
-	) external;
-
-	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0x42842e0e,
-	///  or in textual repr: safeTransferFrom(address,address,uint256)
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
-
-	/// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
-	///  TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
-	///  THEY MAY BE PERMANENTLY LOST
-	/// @dev Throws unless `msg.sender` is the current owner or an authorized
-	///  operator for this RFT. Throws if `from` is not the current owner. Throws
-	///  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	///  Throws if RFT pieces have multiple owners.
-	/// @param from The current owner of the NFT
-	/// @param to The new owner
-	/// @param tokenId The NFT to transfer
-	/// @dev EVM selector for this function is: 0x23b872dd,
-	///  or in textual repr: transferFrom(address,address,uint256)
-	function transferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) external;
-
-	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0x095ea7b3,
-	///  or in textual repr: approve(address,uint256)
-	function approve(address approved, uint256 tokenId) external;
-
-	/// @notice Sets or unsets the approval of a given operator.
-	///  The `operator` is allowed to transfer all token pieces of the `caller` on their behalf.
-	/// @param operator Operator
-	/// @param approved Should operator status be granted or revoked?
-	/// @dev EVM selector for this function is: 0xa22cb465,
-	///  or in textual repr: setApprovalForAll(address,bool)
-	function setApprovalForAll(address operator, bool approved) external;
-
-	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0x081812fc,
-	///  or in textual repr: getApproved(uint256)
-	function getApproved(uint256 tokenId) external view returns (address);
-
-	/// @notice Tells whether the given `owner` approves the `operator`.
-	/// @dev EVM selector for this function is: 0xe985e9c5,
-	///  or in textual repr: isApprovedForAll(address,address)
-	function isApprovedForAll(address owner, address operator) external view returns (bool);
-}
-
-interface UniqueRefungible is
-	Dummy,
-	ERC165,
-	ERC721,
-	ERC721Enumerable,
-	ERC721UniqueExtensions,
-	ERC721UniqueMintable,
-	ERC721Burnable,
-	ERC721Metadata,
-	Collection,
-	TokenProperties
-{}
deletedjs-packages/tests/src/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/api/UniqueRefungibleToken.sol
+++ /dev/null
@@ -1,181 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-/// @dev common stubs holder
-interface Dummy {
-
-}
-
-interface ERC165 is Dummy {
-	function supportsInterface(bytes4 interfaceID) external view returns (bool);
-}
-
-/// @dev the ERC-165 identifier for this interface is 0x5755c3f2
-interface ERC1633 is Dummy, ERC165 {
-	/// @dev EVM selector for this function is: 0x80a54001,
-	///  or in textual repr: parentToken()
-	function parentToken() external view returns (address);
-
-	/// @dev EVM selector for this function is: 0xd7f083f3,
-	///  or in textual repr: parentTokenId()
-	function parentTokenId() external view returns (uint256);
-}
-
-/// @dev the ERC-165 identifier for this interface is 0xedd3a564
-interface ERC20UniqueExtensions is Dummy, ERC165 {
-	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
-	/// @param owner crossAddress The address which owns the funds.
-	/// @param spender crossAddress The address which will spend the funds.
-	/// @return A uint256 specifying the amount of tokens still available for the spender.
-	/// @dev EVM selector for this function is: 0xe0af4bd7,
-	///  or in textual repr: allowanceCross((address,uint256),(address,uint256))
-	function allowanceCross(CrossAddress memory owner, CrossAddress memory spender) external view returns (uint256);
-
-	// /// @dev Function that burns an amount of the token of a given account,
-	// /// deducting from the sender's allowance for said account.
-	// /// @param from The account whose tokens will be burnt.
-	// /// @param amount The amount that will be burnt.
-	// /// @dev EVM selector for this function is: 0x79cc6790,
-	// ///  or in textual repr: burnFrom(address,uint256)
-	// function burnFrom(address from, uint256 amount) external returns (bool);
-
-	/// @dev Function that burns an amount of the token of a given account,
-	/// deducting from the sender's allowance for said account.
-	/// @param from The account whose tokens will be burnt.
-	/// @param amount The amount that will be burnt.
-	/// @dev EVM selector for this function is: 0xbb2f5a58,
-	///  or in textual repr: burnFromCross((address,uint256),uint256)
-	function burnFromCross(CrossAddress memory from, uint256 amount) external returns (bool);
-
-	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
-	/// Beware that changing an allowance with this method brings the risk that someone may use both the old
-	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
-	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
-	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
-	/// @param spender The crossaccount which will spend the funds.
-	/// @param amount The amount of tokens to be spent.
-	/// @dev EVM selector for this function is: 0x0ecd0ab0,
-	///  or in textual repr: approveCross((address,uint256),uint256)
-	function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool);
-
-	/// @notice Balance of account
-	/// @param owner An cross address for whom to query the balance
-	/// @return The number of fingibles owned by `owner`, possibly zero
-	/// @dev EVM selector for this function is: 0xec069398,
-	///  or in textual repr: balanceOfCross((address,uint256))
-	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
-
-	/// @dev Function that changes total amount of the tokens.
-	///  Throws if `msg.sender` doesn't owns all of the tokens.
-	/// @param amount New total amount of the tokens.
-	/// @dev EVM selector for this function is: 0xd2418ca7,
-	///  or in textual repr: repartition(uint256)
-	function repartition(uint256 amount) external returns (bool);
-
-	/// @dev Transfer token for a specified address
-	/// @param to The crossaccount to transfer to.
-	/// @param amount The amount to be transferred.
-	/// @dev EVM selector for this function is: 0x2ada85ff,
-	///  or in textual repr: transferCross((address,uint256),uint256)
-	function transferCross(CrossAddress memory to, uint256 amount) external returns (bool);
-
-	/// @dev Transfer tokens from one address to another
-	/// @param from The address which you want to send tokens from
-	/// @param to The address which you want to transfer to
-	/// @param amount the amount of tokens to be transferred
-	/// @dev EVM selector for this function is: 0xd5cf430b,
-	///  or in textual repr: transferFromCross((address,uint256),(address,uint256),uint256)
-	function transferFromCross(
-		CrossAddress memory from,
-		CrossAddress memory to,
-		uint256 amount
-	) external returns (bool);
-}
-
-/// Cross account struct
-struct CrossAddress {
-	address eth;
-	uint256 sub;
-}
-
-/// @dev inlined interface
-interface ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(address indexed owner, address indexed spender, uint256 value);
-}
-
-/// @title Standard ERC20 token
-///
-/// @dev Implementation of the basic standard token.
-/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
-/// @dev the ERC-165 identifier for this interface is 0x942e8b22
-interface ERC20 is Dummy, ERC165, ERC20Events {
-	/// @return the name of the token.
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() external view returns (string memory);
-
-	/// @return the symbol of the token.
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() external view returns (string memory);
-
-	/// @dev Total number of tokens in existence
-	/// @dev EVM selector for this function is: 0x18160ddd,
-	///  or in textual repr: totalSupply()
-	function totalSupply() external view returns (uint256);
-
-	/// @dev Not supported
-	/// @dev EVM selector for this function is: 0x313ce567,
-	///  or in textual repr: decimals()
-	function decimals() external view returns (uint8);
-
-	/// @dev Gets the balance of the specified address.
-	/// @param owner The address to query the balance of.
-	/// @return An uint256 representing the amount owned by the passed address.
-	/// @dev EVM selector for this function is: 0x70a08231,
-	///  or in textual repr: balanceOf(address)
-	function balanceOf(address owner) external view returns (uint256);
-
-	/// @dev Transfer token for a specified address
-	/// @param to The address to transfer to.
-	/// @param amount The amount to be transferred.
-	/// @dev EVM selector for this function is: 0xa9059cbb,
-	///  or in textual repr: transfer(address,uint256)
-	function transfer(address to, uint256 amount) external returns (bool);
-
-	/// @dev Transfer tokens from one address to another
-	/// @param from address The address which you want to send tokens from
-	/// @param to address The address which you want to transfer to
-	/// @param amount uint256 the amount of tokens to be transferred
-	/// @dev EVM selector for this function is: 0x23b872dd,
-	///  or in textual repr: transferFrom(address,address,uint256)
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) external returns (bool);
-
-	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.
-	/// Beware that changing an allowance with this method brings the risk that someone may use both the old
-	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
-	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
-	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
-	/// @param spender The address which will spend the funds.
-	/// @param amount The amount of tokens to be spent.
-	/// @dev EVM selector for this function is: 0x095ea7b3,
-	///  or in textual repr: approve(address,uint256)
-	function approve(address spender, uint256 amount) external returns (bool);
-
-	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
-	/// @param owner address The address which owns the funds.
-	/// @param spender address The address which will spend the funds.
-	/// @return A uint256 specifying the amount of tokens still available for the spender.
-	/// @dev EVM selector for this function is: 0xdd62ed3e,
-	///  or in textual repr: allowance(address,address)
-	function allowance(address owner, address spender) external view returns (uint256);
-}
-
-interface UniqueRefungibleToken is Dummy, ERC165, ERC20, ERC20UniqueExtensions, ERC1633 {}
deletedjs-packages/tests/src/eth/base.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/base.test.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itEth, usingEthPlaygrounds, expect} from './util/index.js';
-import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-
-
-describe('Contract calls', () => {
-  let donor: IKeyringPair;
-
-  before(async function () {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Call of simple contract fee is less than 0.2 UNQ', async ({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const flipper = await helper.eth.deployFlipper(deployer);
-
-    const cost = await helper.eth.calculateFee({Ethereum: deployer}, () => flipper.methods.flip().send({from: deployer}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true;
-  });
-
-  itEth('Balance transfer fee is less than 0.2 UNQ', async ({helper}) => {
-    const userA = await helper.eth.createAccountWithBalance(donor);
-    const userB = helper.eth.createAccount();
-    const cost = await helper.eth.calculateFee({Ethereum: userA}, () => helper.getWeb3().eth.sendTransaction({
-      from: userA,
-      to: userB,
-      value: '1000000',
-      gas: helper.eth.DEFAULT_GAS,
-    }));
-    const balanceB = await helper.balance.getEthereum(userB);
-    expect(cost - balanceB < BigInt(0.2 * Number(helper.balance.getOneTokenNominal()))).to.be.true;
-  });
-
-  itEth('NFT transfer is close to 0.15 UNQ', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const [alice] = await helper.arrange.createAccounts([10n], donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
-
-    const cost = await helper.eth.calculateFee({Ethereum: caller}, () => contract.methods.transfer(receiver, tokenId).send(caller));
-
-    const fee = Number(cost) / Number(helper.balance.getOneTokenNominal());
-    const expectedFee = 0.15;
-    const tolerance = 0.001;
-
-    expect(Math.abs(fee - expectedFee)).to.be.lessThan(tolerance);
-  });
-});
-
-describe('ERC165 tests', () => {
-  // https://eips.ethereum.org/EIPS/eip-165
-
-  let erc721MetadataCompatibleNftCollectionId: number;
-  let simpleNftCollectionId: number;
-  let minter: string;
-
-  const BASE_URI = 'base/';
-
-  async function checkInterface(helper: EthUniqueHelper, interfaceId: string, simpleResult: boolean, compatibleResult: boolean) {
-    const simple = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(simpleNftCollectionId), 'nft', minter);
-    const compatible = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(erc721MetadataCompatibleNftCollectionId), 'nft', minter);
-
-    expect(await simple.methods.supportsInterface(interfaceId).call()).to.equal(simpleResult, `empty (not ERC721Metadata compatible) NFT collection returns not ${simpleResult}`);
-    expect(await compatible.methods.supportsInterface(interfaceId).call()).to.equal(compatibleResult, `ERC721Metadata compatible NFT collection returns not ${compatibleResult}`);
-  }
-
-  before(async () => {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      const [alice] = await helper.arrange.createAccounts([10n], donor);
-      ({collectionId: simpleNftCollectionId} = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}));
-      minter = await helper.eth.createAccountWithBalance(donor);
-      ({collectionId: erc721MetadataCompatibleNftCollectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(minter, 'n', 'd', 'p', BASE_URI));
-    });
-  });
-
-  itEth('nonexistent interfaceID - 0xffffffff - always false', async ({helper}) => {
-    await checkInterface(helper, '0xffffffff', false, false);
-  });
-
-  itEth('ERC721 - 0x780e9d63 - support', async ({helper}) => {
-    await checkInterface(helper, '0x780e9d63', true, true);
-  });
-
-  itEth('ERC721Metadata - 0x5b5e139f - support', async ({helper}) => {
-    await checkInterface(helper, '0x5b5e139f', false, true);
-  });
-
-  itEth('ERC721Enumerable - 0x780e9d63 - support', async ({helper}) => {
-    await checkInterface(helper, '0x780e9d63', true, true);
-  });
-
-  itEth.skip('ERC721UniqueExtensions support', async ({helper}) => {
-    await checkInterface(helper, '0xb74c26b7', true, true);
-  });
-
-  itEth('ERC721Burnable - 0x42966c68 - support', async ({helper}) => {
-    await checkInterface(helper, '0x42966c68', true, true);
-  });
-
-  itEth('ERC165 - 0x01ffc9a7 - support', async ({helper}) => {
-    await checkInterface(helper, '0x01ffc9a7', true, true);
-  });
-});
deletedjs-packages/tests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/collectionAdmin.test.ts
+++ /dev/null
@@ -1,469 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect} from 'chai';
-import {Pallets} from '../util/index.js';
-import type {IEthCrossAccountId} from '@unique/playgrounds/src/types.js';
-import {usingEthPlaygrounds, itEth} from './util/index.js';
-import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-import {CreateCollectionData} from './util/playgrounds/types.js';
-
-async function recordEthFee(helper: EthUniqueHelper, userAddress: string, call: () => Promise<any>) {
-  const before = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress));
-  await call();
-  await helper.wait.newBlocks(1);
-  const after = await helper.balance.getSubstrate(helper.address.ethToSubstrate(userAddress));
-
-  expect(after < before).to.be.true;
-
-  return before - after;
-}
-
-describe('Add collection admins', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'ft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {
-      // arrange
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const adminSub = await privateKey('//admin2');
-      const adminEth = helper.eth.createAccount().toLowerCase();
-
-      const adminDeprecated = helper.eth.createAccount().toLowerCase();
-      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
-      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
-
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
-
-      // Check isOwnerOrAdminCross returns false:
-      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.false;
-      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.false;
-      expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.false;
-
-      // Soft-deprecated: can addCollectionAdmin
-      await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();
-      // Can addCollectionAdminCross for substrate and ethereum address
-      await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
-      await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
-
-      // 1. Expect api.rpc.unique.adminlist returns admins:
-      const adminListRpc = await helper.collection.getAdmins(collectionId);
-      expect(adminListRpc).to.has.length(3);
-      expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}, {Ethereum: adminDeprecated}]);
-
-      // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist
-      let adminListEth = await collectionEvm.methods.collectionAdmins().call();
-      adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));
-      expect(adminListRpc).to.be.like(adminListEth);
-
-      // 3. check isOwnerOrAdminCross returns true:
-      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;
-      expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;
-      expect(await collectionEvm.methods.isOwnerOrAdminCross(helper.ethCrossAccount.fromAddress(adminDeprecated)).call()).to.be.true;
-    });
-  });
-
-  itEth('cross account admin can mint', async ({helper}) => {
-    // arrange: create collection and accounts
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', 'uri');
-    const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
-    const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
-    const [adminSub] = await helper.arrange.createAccounts([100n], donor);
-    const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    // cannot mint while not admin
-    await expect(collectionEvm.methods.mint(owner).send({from: adminEth})).to.be.rejected;
-    await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);
-
-    // admin (sub and eth) can mint token:
-    await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
-    await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
-    await collectionEvm.methods.mint(owner).send({from: adminEth});
-    await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});
-
-    expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);
-  });
-
-  itEth('cannot add invalid cross account admin', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);
-
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    const adminCross = {
-      eth: helper.address.substrateToEth(admin.address),
-      sub: admin.addressRaw,
-    };
-    await expect(collectionEvm.methods.addCollectionAdminCross(adminCross).send()).to.be.rejected;
-  });
-
-  itEth('can verify owner with methods.isOwnerOrAdmin[Cross]', async ({helper, privateKey}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const adminDeprecated = helper.eth.createAccount();
-    const admin1Cross = helper.ethCrossAccount.fromKeyringPair(await privateKey('admin'));
-    const admin2Cross = helper.ethCrossAccount.fromAddress(helper.address.substrateToEth((await privateKey('admin3')).address));
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    // Soft-deprecated:
-    expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.false;
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.false;
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.false;
-
-    await collectionEvm.methods.addCollectionAdmin(adminDeprecated).send();
-    await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send();
-    await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send();
-
-    // Soft-deprecated: isOwnerOrAdmin returns true
-    expect(await collectionEvm.methods.isOwnerOrAdmin(adminDeprecated).call()).to.be.true;
-    // Expect isOwnerOrAdminCross return true
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin1Cross).call()).to.be.true;
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(admin2Cross).call()).to.be.true;
-  });
-
-  // Soft-deprecated
-  itEth('(!negative tests!) Add admin by ADMIN is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const admin = await helper.eth.createAccountWithBalance(donor);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-    await collectionEvm.methods.addCollectionAdmin(admin).send();
-
-    const user = helper.eth.createAccount();
-    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin}))
-      .to.be.rejectedWith('NoPermission');
-
-    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-    expect(adminList.length).to.be.eq(1);
-    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
-      .to.be.eq(admin.toLocaleLowerCase());
-  });
-
-  // Soft-deprecated
-  itEth('(!negative tests!) Add admin by USER is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const notAdmin = await helper.eth.createAccountWithBalance(donor);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    const user = helper.eth.createAccount();
-    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin}))
-      .to.be.rejectedWith('NoPermission');
-
-    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-    expect(adminList.length).to.be.eq(0);
-  });
-
-  itEth('(!negative tests!) Add [cross] admin by ADMIN is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const [admin, notAdmin] = await helper.arrange.createAccounts([10n, 10n], donor);
-    const adminCross = helper.ethCrossAccount.fromKeyringPair(admin);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-    await collectionEvm.methods.addCollectionAdminCross(adminCross).send();
-
-    const notAdminCross = helper.ethCrossAccount.fromKeyringPair(notAdmin);
-    await expect(collectionEvm.methods.addCollectionAdminCross(notAdminCross).call({from: adminCross.eth}))
-      .to.be.rejectedWith('NoPermission');
-
-    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-    expect(adminList.length).to.be.eq(1);
-
-    const admin0Cross = helper.ethCrossAccount.fromKeyringPair(adminList[0]);
-    expect(admin0Cross.eth.toLocaleLowerCase())
-      .to.be.eq(adminCross.eth.toLocaleLowerCase());
-  });
-
-  itEth('(!negative tests!) Add [cross] admin by USER is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const notAdmin0 = await helper.eth.createAccountWithBalance(donor);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-    const [notAdmin1] = await helper.arrange.createAccounts([10n], donor);
-    const notAdmin1Cross = helper.ethCrossAccount.fromKeyringPair(notAdmin1);
-    await expect(collectionEvm.methods.addCollectionAdminCross(notAdmin1Cross).call({from: notAdmin0}))
-      .to.be.rejectedWith('NoPermission');
-
-    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-    expect(adminList.length).to.be.eq(0);
-  });
-});
-
-describe('Remove collection admins', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  // Soft-deprecated
-  itEth('Remove admin by owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const newAdmin = helper.eth.createAccount();
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();
-
-    {
-      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-      expect(adminList.length).to.be.eq(1);
-      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
-        .to.be.eq(newAdmin.toLocaleLowerCase());
-    }
-
-    await collectionEvm.methods.removeCollectionAdmin(newAdmin).send();
-    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-    expect(adminList.length).to.be.eq(0);
-  });
-
-  itEth('Remove [cross] admin by owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const [adminSub] = await helper.arrange.createAccounts([10n], donor);
-    const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
-    const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
-    const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
-
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-    await collectionEvm.methods.addCollectionAdminCross(adminCrossSub).send();
-    await collectionEvm.methods.addCollectionAdminCross(adminCrossEth).send();
-
-    {
-      const adminList = await helper.collection.getAdmins(collectionId);
-      expect(adminList).to.deep.include({Substrate: adminSub.address});
-      expect(adminList).to.deep.include({Ethereum: adminEth});
-    }
-
-    await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();
-    await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();
-    const adminList = await helper.collection.getAdmins(collectionId);
-    expect(adminList.length).to.be.eq(0);
-
-    // Non admin cannot mint:
-    await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);
-    await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;
-  });
-
-  // Soft-deprecated
-  itEth('(!negative tests!) Remove admin by ADMIN is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    const admin0 = await helper.eth.createAccountWithBalance(donor);
-    await collectionEvm.methods.addCollectionAdmin(admin0).send();
-    const admin1 = await helper.eth.createAccountWithBalance(donor);
-    await collectionEvm.methods.addCollectionAdmin(admin1).send();
-
-    await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0}))
-      .to.be.rejectedWith('NoPermission');
-    {
-      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-      expect(adminList.length).to.be.eq(2);
-      expect(adminList.toString().toLocaleLowerCase())
-        .to.be.deep.contains(admin0.toLocaleLowerCase())
-        .to.be.deep.contains(admin1.toLocaleLowerCase());
-    }
-  });
-
-  // Soft-deprecated
-  itEth('(!negative tests!) Remove admin by USER is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    const admin = await helper.eth.createAccountWithBalance(donor);
-    await collectionEvm.methods.addCollectionAdmin(admin).send();
-    const notAdmin = helper.eth.createAccount();
-
-    await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin}))
-      .to.be.rejectedWith('NoPermission');
-    {
-      const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())
-        .to.be.eq(admin.toLocaleLowerCase());
-      expect(adminList.length).to.be.eq(1);
-    }
-  });
-
-  itEth('(!negative tests!) Remove [cross] admin by ADMIN is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const [admin1] = await helper.arrange.createAccounts([10n], donor);
-    const admin1Cross = helper.ethCrossAccount.fromKeyringPair(admin1);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-    await collectionEvm.methods.addCollectionAdminCross(admin1Cross).send();
-
-    const [admin2] = await helper.arrange.createAccounts([10n], donor);
-    const admin2Cross = helper.ethCrossAccount.fromKeyringPair(admin2);
-    await collectionEvm.methods.addCollectionAdminCross(admin2Cross).send();
-
-    await expect(collectionEvm.methods.removeCollectionAdminCross(admin1Cross).call({from: admin2Cross.eth}))
-      .to.be.rejectedWith('NoPermission');
-
-    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-    expect(adminList.length).to.be.eq(2);
-    expect(adminList.toString().toLocaleLowerCase())
-      .to.be.deep.contains(admin1.address.toLocaleLowerCase())
-      .to.be.deep.contains(admin2.address.toLocaleLowerCase());
-  });
-
-  itEth('(!negative tests!) Remove [cross] admin by USER is not allowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-    const [adminSub] = await helper.arrange.createAccounts([10n], donor);
-    const adminSubCross = helper.ethCrossAccount.fromKeyringPair(adminSub);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-    await collectionEvm.methods.addCollectionAdminCross(adminSubCross).send();
-    const notAdminEth = await helper.eth.createAccountWithBalance(donor);
-
-    await expect(collectionEvm.methods.removeCollectionAdminCross(adminSubCross).call({from: notAdminEth}))
-      .to.be.rejectedWith('NoPermission');
-
-    const adminList = await helper.callRpc('api.rpc.unique.adminlist', [collectionId]);
-    expect(adminList.length).to.be.eq(1);
-    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())
-      .to.be.eq(adminSub.address.toLocaleLowerCase());
-  });
-});
-
-// Soft-deprecated
-describe('Change owner tests', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Change owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const newOwner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    await collectionEvm.methods.changeCollectionOwner(newOwner).send();
-
-    expect(await collectionEvm.methods.isOwnerOrAdmin(owner).call()).to.be.false;
-    expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.true;
-  });
-
-  itEth('change owner call fee', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const newOwner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-    const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.changeCollectionOwner(newOwner).send());
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-    expect(cost > 0);
-  });
-
-  itEth('(!negative tests!) call setOwner by non owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const newOwner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-    await expect(collectionEvm.methods.changeCollectionOwner(newOwner).send({from: newOwner})).to.be.rejected;
-    expect(await collectionEvm.methods.isOwnerOrAdmin(newOwner).call()).to.be.false;
-  });
-});
-
-describe('Change substrate owner tests', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Change owner [cross]', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const ownerEth = await helper.eth.createAccountWithBalance(donor);
-    const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);
-    const [ownerSub] = await helper.arrange.createAccounts([10n], donor);
-    const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);
-
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.false;
-
-    // Can set ethereum owner:
-    await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossEth).send({from: owner});
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossEth).call()).to.be.true;
-    expect(await helper.collection.getData(collectionId))
-      .to.have.property('normalizedOwner').that.is.eq(helper.address.ethToSubstrate(ownerEth));
-
-    // Can set Substrate owner:
-    await collectionEvm.methods.changeCollectionOwnerCross(ownerCrossSub).send({from: ownerEth});
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(ownerCrossSub).call()).to.be.true;
-    expect(await helper.collection.getData(collectionId))
-      .to.have.property('normalizedOwner').that.is.eq(helper.address.normalizeSubstrate(ownerSub.address));
-  });
-
-  itEth.skip('change owner call fee', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const [newOwner] = await helper.arrange.createAccounts([10n], donor);
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    const cost = await recordEthFee(helper, owner, () => collectionEvm.methods.setOwnerSubstrate(newOwner.addressRaw).send());
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-    expect(cost > 0);
-  });
-
-  itEth('(!negative tests!) call setOwner by non owner [cross]', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const otherReceiver = await helper.eth.createAccountWithBalance(donor);
-    const [newOwner] = await helper.arrange.createAccounts([10n], donor);
-    const newOwnerCross = helper.ethCrossAccount.fromKeyringPair(newOwner);
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    await expect(collectionEvm.methods.changeCollectionOwnerCross(newOwnerCross).send({from: otherReceiver})).to.be.rejected;
-    expect(await collectionEvm.methods.isOwnerOrAdminCross(newOwnerCross).call()).to.be.false;
-  });
-});
deletedjs-packages/tests/src/eth/collectionHelperAddress.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/collectionHelperAddress.test.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {itEth, usingEthPlaygrounds, expect} from './util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {COLLECTION_HELPER, Pallets} from '../util/index.js';
-
-describe('[eth]CollectionHelperAddress test: ERC20/ERC721 ', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('NFT', async ({helper}) => {
-    const owner =  await helper.eth.createAccountWithBalance(donor);
-
-    const {collectionAddress: nftCollectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
-    const nftCollection = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner);
-
-    expect((await nftCollection.methods.collectionHelperAddress().call())
-      .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER);
-  });
-
-  itEth.ifWithPallets('RFT ', [Pallets.ReFungible], async ({helper}) => {
-    const owner =  await helper.eth.createAccountWithBalance(donor);
-
-    const {collectionAddress: rftCollectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
-
-    const rftCollection = await helper.ethNativeContract.collection(rftCollectionAddress, 'rft', owner);
-    expect((await rftCollection.methods.collectionHelperAddress().call())
-      .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER);
-  });
-
-  itEth('FT', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', 18, 'absolutely anything', 'ROC');
-    const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    expect((await collection.methods.collectionHelperAddress().call())
-      .toString().toLowerCase()).to.be.equal(COLLECTION_HELPER);
-  });
-
-  itEth('[collectionHelpers] convert collectionId into address', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionId = 7;
-    const collectionAddress = helper.ethAddress.fromCollectionId(collectionId);
-    const helperContract = await helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await helperContract.methods.collectionAddress(collectionId).call()).to.be.equal(collectionAddress);
-    expect(parseInt(await helperContract.methods.collectionId(collectionAddress).call())).to.be.equal(collectionId);
-  });
-
-});
deletedjs-packages/tests/src/eth/collectionLimits.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/collectionLimits.test.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../util/index.js';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import {CollectionLimitField, CreateCollectionData} from './util/playgrounds/types.js';
-
-
-describe('Can set collection limits', () => {
-  let donor: IKeyringPair;
-
-  before(async () => {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  [
-    {case: 'nft' as const},
-    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {case: 'ft' as const},
-  ].map(testCase =>
-    itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send();
-      const limits = {
-        accountTokenOwnershipLimit: 1000,
-        sponsoredDataSize: 1024,
-        sponsoredDataRateLimit: 30,
-        tokenLimit: 1000000,
-        sponsorTransferTimeout: 6,
-        sponsorApproveTimeout: 6,
-        ownerCanTransfer: 1,
-        ownerCanDestroy: 0,
-        transfersEnabled: 0,
-      };
-
-      const expectedLimits = {
-        accountTokenOwnershipLimit: 1000,
-        sponsoredDataSize: 1024,
-        sponsoredDataRateLimit: {blocks: 30},
-        tokenLimit: 1000000,
-        sponsorTransferTimeout: 6,
-        sponsorApproveTimeout: 6,
-        ownerCanTransfer: true,
-        ownerCanDestroy: false,
-        transfersEnabled: false,
-      };
-
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: limits.accountTokenOwnershipLimit}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: limits.sponsoredDataSize}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: limits.sponsoredDataRateLimit}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TokenLimit, value: {status: true, value: limits.tokenLimit}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorTransferTimeout, value: {status: true, value: limits.sponsorTransferTimeout}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsorApproveTimeout, value: {status: true, value: limits.sponsorApproveTimeout}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: limits.ownerCanTransfer}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanDestroy, value: {status: true, value: limits.ownerCanDestroy}}).send();
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: limits.transfersEnabled}}).send();
-
-      // Check limits from sub:
-      const data = (await helper.rft.getData(collectionId))!;
-      expect(data.raw.limits).to.deep.eq(expectedLimits);
-      expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits);
-      // Check limits from eth:
-      const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner});
-      expect(limitsEvm).to.have.length(9);
-      expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]);
-      expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]);
-      expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]);
-      expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]);
-      expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]);
-      expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]);
-      expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]);
-      expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]);
-      expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]);
-    }));
-});
-
-describe('Cannot set invalid collection limits', () => {
-  let donor: IKeyringPair;
-
-  before(async () => {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  [
-    {case: 'nft' as const},
-    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {case: 'ft' as const},
-  ].map(testCase =>
-    itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
-      const invalidLimits = {
-        accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER),
-        transfersEnabled: 3,
-      };
-
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'ISNI', testCase.case, 18)).send();
-      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
-
-      // Cannot set non-existing limit
-      await expect(collectionEvm.methods
-        .setCollectionLimit({field: 9, value: {status: true, value: 1}})
-        .call()).to.be.rejectedWith('Returned error: VM Exception while processing transaction: revert value not convertible into enum "CollectionLimitField"');
-
-      // Cannot disable limits
-      await expect(collectionEvm.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: false, value: 0}})
-        .call()).to.be.rejectedWith('user can\'t disable limits');
-
-      await expect(collectionEvm.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: invalidLimits.accountTokenOwnershipLimit}})
-        .call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`);
-
-      await expect(collectionEvm.methods
-        .setCollectionLimit({field: CollectionLimitField.TransferEnabled, value: {status: true, value: 3}})
-        .call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`);
-
-      expect(() => collectionEvm.methods
-        .setCollectionLimit({field: CollectionLimitField.SponsoredDataSize, value: {status: true, value: -1}}).send()).to.throw('value out-of-bounds');
-    }));
-
-  [
-    {case: 'nft' as const, requiredPallets: []},
-    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {case: 'ft' as const, requiredPallets: []},
-  ].map(testCase =>
-    itEth.ifWithPallets(`Non-owner and non-admin cannot set collection limits for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const nonOwner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'FLO', testCase.case, 18)).send();
-
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
-      await expect(collectionEvm.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .call({from: nonOwner}))
-        .to.be.rejectedWith('NoPermission');
-
-      await expect(collectionEvm.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .send({from: nonOwner}))
-        .to.be.rejected;
-    }));
-});
deletedjs-packages/tests/src/eth/collectionProperties.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/collectionProperties.test.ts
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {itEth, usingEthPlaygrounds, expect} from './util/index.js';
-import {Pallets} from '../util/index.js';
-import type {IProperty, ITokenPropertyPermission} from '@unique/playgrounds/src/types.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-describe('EVM collection properties', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await _helper.arrange.createAccounts([50n], donor);
-    });
-  });
-
-  // Soft-deprecated: setCollectionProperty
-  [
-    {method: 'setCollectionProperties', mode: 'nft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
-    {method: 'setCollectionProperties', mode: 'rft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
-    {method: 'setCollectionProperties', mode: 'ft' as const, methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
-    {method: 'setCollectionProperty', mode: 'nft' as const, methodParams: ['testKey', Buffer.from('testValue')], expectedProps: [{key: 'testKey', value: 'testValue'}]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`Collection properties can be set: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});
-      await collection.addAdmin(alice, {Ethereum: caller});
-
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'setCollectionProperty');
-
-      // collectionProperties returns an empty array if no properties:
-      expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like([]);
-      expect(await collectionEvm.methods.collectionProperties(['NonExistingKey']).call()).to.be.like([]);
-
-      await collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller});
-
-      const raw = (await collection.getData())?.raw;
-      expect(raw.properties).to.deep.equal(testCase.expectedProps);
-
-      // collectionProperties returns properties:
-      expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));
-    }));
-
-  itEth('Cannot set invalid properties', async({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: []});
-    await collection.addAdmin(alice, {Ethereum: caller});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
-
-    await expect(contract.methods.setCollectionProperties([{key: '', value: Buffer.from('val1')}]).send({from: caller})).to.be.rejected;
-    await expect(contract.methods.setCollectionProperties([{key: 'déjà vu', value: Buffer.from('hmm...')}]).send({from: caller})).to.be.rejected;
-    await expect(contract.methods.setCollectionProperties([{key: 'a'.repeat(257), value: Buffer.from('val3')}]).send({from: caller})).to.be.rejected;
-    // TODO add more expects
-    const raw = (await collection.getData())?.raw;
-    expect(raw.properties).to.deep.equal([]);
-  });
-
-
-  // Soft-deprecated: deleteCollectionProperty
-  [
-    {method: 'deleteCollectionProperties', mode: 'nft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
-    {method: 'deleteCollectionProperties', mode: 'rft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
-    {method: 'deleteCollectionProperties', mode: 'ft' as const, methodParams: [['testKey1', 'testKey2']], expectedProps: [{key: 'testKey3', value: 'testValue3'}]},
-    {method: 'deleteCollectionProperty', mode: 'nft' as const, methodParams: ['testKey1'], expectedProps: [{key: 'testKey2', value: 'testValue2'}, {key: 'testKey3', value: 'testValue3'}]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`Collection properties can be deleted: ${testCase.method}() for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
-      const properties = [
-        {key: 'testKey1', value: 'testValue1'},
-        {key: 'testKey2', value: 'testValue2'},
-        {key: 'testKey3', value: 'testValue3'}];
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const collection = await helper[testCase.mode].mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});
-
-      await collection.addAdmin(alice, {Ethereum: caller});
-
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty');
-
-      await contract.methods[testCase.method](...testCase.methodParams).send({from: caller});
-
-      const raw = (await collection.getData())?.raw;
-
-      expect(raw.properties.length).to.equal(testCase.expectedProps.length);
-      expect(raw.properties).to.deep.equal(testCase.expectedProps);
-    }));
-
-  [
-    {method: 'deleteCollectionProperties', methodParams: [['testKey2']]},
-    {method: 'deleteCollectionProperty', methodParams: ['testKey2']},
-  ].map(testCase =>
-    itEth(`cannot ${testCase.method}() of non-owned collections`, async ({helper}) => {
-      const properties = [
-        {key: 'testKey1', value: 'testValue1'},
-        {key: 'testKey2', value: 'testValue2'},
-      ];
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties});
-
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(address, 'nft', caller, testCase.method === 'deleteCollectionProperty');
-
-      await expect(collectionEvm.methods[testCase.method](...testCase.methodParams).send({from: caller})).to.be.rejected;
-      expect(await collection.getProperties()).to.deep.eq(properties);
-    }));
-
-  itEth('Can be read', async({helper}) => {
-    const caller = helper.eth.createAccount();
-    const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'test', tokenPrefix: 'test', properties: [{key: 'testKey', value: 'testValue'}]});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
-
-    const value = await contract.methods.collectionProperty('testKey').call();
-    expect(value).to.equal(helper.getWeb3().utils.toHex('testValue'));
-  });
-});
-
-describe('Supports ERC721Metadata', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  [
-    {case: 'nft' as const},
-    {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`ERC721Metadata property can be set for ${testCase.case} collection`, testCase.requiredPallets || [], async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const bruh = await helper.eth.createAccountWithBalance(donor);
-
-      const BASE_URI = 'base/';
-      const SUFFIX = 'suffix1';
-      const URI = 'uri1';
-
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(caller);
-      const creatorMethod = testCase.case === 'rft' ? 'createRFTCollection' : 'createNFTCollection';
-
-      const {collectionId, collectionAddress} = await helper.eth[creatorMethod](caller, 'n', 'd', 'p');
-      const bruhCross = helper.ethCrossAccount.fromAddress(bruh);
-
-      const contract = await helper.ethNativeContract.collectionById(collectionId, testCase.case, caller);
-      await contract.methods.addCollectionAdminCross(bruhCross).send(); // to check that admin will work too
-
-      const collection1 = helper.nft.getCollectionObject(collectionId);
-      const data1 = await collection1.getData();
-      expect(data1?.raw.flags.erc721metadata).to.be.false;
-      expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false;
-
-      await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, BASE_URI)
-        .send({from: bruh});
-
-      expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.true;
-
-      const collection2 = helper.nft.getCollectionObject(collectionId);
-      const data2 = await collection2.getData();
-      expect(data2?.raw.flags.erc721metadata).to.be.true;
-
-      const propertyPermissions = data2?.raw.tokenPropertyPermissions;
-      expect(propertyPermissions?.length).to.equal(2);
-
-      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URI' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null;
-
-      expect(propertyPermissions.find((tpp: ITokenPropertyPermission) => tpp.key === 'URISuffix' && tpp.permission.mutable && tpp.permission.collectionAdmin && !tpp.permission.tokenOwner)).to.be.not.null;
-
-      expect(data2?.raw.properties?.find((property: IProperty) => property.key === 'baseURI' && property.value === BASE_URI)).to.be.not.null;
-
-      const token1Result = await contract.methods.mint(bruh).send();
-      const tokenId1 = token1Result.events.Transfer.returnValues.tokenId;
-
-      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI);
-
-      await contract.methods.setProperties(tokenId1, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send();
-      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);
-
-      await contract.methods.setProperties(tokenId1, [{key: 'URI', value: Buffer.from(URI)}]).send();
-      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(URI);
-
-      await contract.methods.deleteProperties(tokenId1, ['URI']).send();
-      expect(await contract.methods.tokenURI(tokenId1).call()).to.equal(BASE_URI + SUFFIX);
-
-      const token2Result = await contract.methods.mintWithTokenURI(bruh, URI).send();
-      const tokenId2 = token2Result.events.Transfer.returnValues.tokenId;
-
-      expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(URI);
-
-      await contract.methods.deleteProperties(tokenId2, ['URI']).send();
-      expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI);
-
-      await contract.methods.setProperties(tokenId2, [{key: 'URISuffix', value: Buffer.from(SUFFIX)}]).send();
-      expect(await contract.methods.tokenURI(tokenId2).call()).to.equal(BASE_URI + SUFFIX);
-    }));
-});
deletedjs-packages/tests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/collectionSponsoring.test.ts
+++ /dev/null
@@ -1,955 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index.js';
-import {itEth, expect} from './util/index.js';
-import {CollectionLimitField, TokenPermissionField} from './util/playgrounds/types.js';
-
-describe('evm nft collection sponsoring', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let nominal: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  // TODO: move to substrate tests
-  itEth('sponsors mint transactions', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}});
-    await collection.setSponsor(alice, alice.address);
-    await collection.confirmSponsorship(alice);
-
-    const minter = helper.eth.createAccount();
-    expect(await helper.balance.getEthereum(minter)).to.equal(0n);
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', minter);
-
-    await collection.addToAllowList(alice, {Ethereum: minter});
-
-    const result = await contract.methods.mint(minter).send();
-
-    const events = helper.eth.normalizeEvents(result.events);
-    expect(events).to.be.deep.equal([
-      {
-        address: collectionAddress,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: minter,
-          tokenId: '1',
-        },
-      },
-    ]);
-  });
-
-  // TODO: Temprorary off. Need refactor
-  // itWeb3('Set substrate sponsor', async ({api, web3, privateKeyWrapper}) => {
-  //   const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-  //   const collectionHelpers = evmCollectionHelpers(web3, owner);
-  //   let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send();
-  //   const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-  //   const sponsor = privateKeyWrapper('//Alice');
-  //   const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-
-  //   expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
-  //   result = await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
-  //   expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
-
-  //   const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
-  //   await submitTransactionAsync(sponsor, confirmTx);
-  //   expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
-
-  //   const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
-  //   expect(bigIntToSub(api, BigInt(sponsorTuple[1]))).to.be.eq(sponsor.address);
-  // });
-
-  [
-    'setCollectionSponsorCross',
-    'setCollectionSponsor', // Soft-deprecated
-  ].map(testCase =>
-    itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
-
-      let result = await collectionHelpers.methods.createNFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
-      const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'nft', owner, testCase === 'setCollectionSponsor');
-
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
-      result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
-
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-      let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
-      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
-
-      await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
-
-      sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
-      expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');
-    }));
-
-  [
-    'setCollectionSponsorCross',
-    'setCollectionSponsor', // Soft-deprecated
-  ].map(testCase =>
-    itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
-
-      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');
-
-      const collectionSub = helper.nft.getCollectionObject(collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');
-
-      // Set collection sponsor:
-      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});
-      let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
-      expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
-      // Account cannot confirm sponsorship if it is not set as a sponsor
-      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-      // Sponsor can confirm sponsorship:
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
-      sponsorship = (await collectionSub.getData())!.raw.sponsorship;
-      expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
-
-      // Create user with no balance:
-      const user = helper.ethCrossAccount.createAccount();
-      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-
-      // Set collection permissions:
-      const oldPermissions = (await collectionSub.getData())!.raw.permissions;
-      expect(oldPermissions.mintMode).to.be.false;
-      expect(oldPermissions.access).to.be.equal('Normal');
-
-      await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-      await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});
-      await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
-      await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send();
-
-      const newPermissions = (await collectionSub.getData())!.raw.permissions;
-      expect(newPermissions.mintMode).to.be.true;
-      expect(newPermissions.access).to.be.equal('AllowList');
-
-      // Set token permissions
-      await collectionEvm.methods.setTokenPropertyPermissions([
-        ['key', [
-          [TokenPermissionField.TokenOwner, true],
-        ],
-        ],
-      ]).send({from: owner});
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
-
-      // User can mint token without balance:
-      {
-        const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});
-        const event = helper.eth.normalizeEvents(result.events)
-          .find(event => event.event === 'Transfer');
-
-        expect(event).to.be.deep.equal({
-          address: collectionAddress,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: user.eth,
-            tokenId: '1',
-          },
-        });
-
-        // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth});
-
-        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-        const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
-
-        expect(await collectionEvm.methods.properties(nextTokenId, []).call())
-          .to.be.like([
-            [
-              'key',
-              '0x' + Buffer.from('Value').toString('hex'),
-            ],
-          ]);
-        expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-        expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
-      }
-    }));
-
-  itEth('Can sponsor [set token properties] via access list', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsorEth = await helper.eth.createAccountWithBalance(donor);
-    const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
-
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Sponsor collection', '1', '1', '');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, false);
-
-    // Set collection sponsor:
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});
-
-    // Sponsor can confirm sponsorship:
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
-
-    // Create user with no balance:
-    const user = helper.ethCrossAccount.createAccount();
-    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-
-    // Set collection permissions:
-    await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-    await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});
-    await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
-    await collectionEvm.methods.setCollectionLimit({field: CollectionLimitField.SponsoredDataRateLimit, value: {status: true, value: 30}}).send();
-
-    // Set token permissions
-    await collectionEvm.methods.setTokenPropertyPermissions([
-      ['key', [
-        [TokenPermissionField.TokenOwner, true],
-      ],
-      ],
-    ]).send({from: owner});
-
-    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-    const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
-
-    // User can mint token without balance:
-    {
-      const result = await collectionEvm.methods.mintCross(user, []).send({from: user.eth});
-      const event = helper.eth.normalizeEvents(result.events)
-        .find(event => event.event === 'Transfer');
-
-      expect(event).to.be.deep.equal({
-        address: collectionAddress,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user.eth,
-          tokenId: '1',
-        },
-      });
-
-      await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-      const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
-
-      expect(await collectionEvm.methods.properties(nextTokenId, []).call())
-        .to.be.like([
-          [
-            'key',
-            '0x' + Buffer.from('Value').toString('hex'),
-          ],
-        ]);
-      expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-      expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
-    }
-  });
-
-  // TODO: Temprorary off. Need refactor
-  // itWeb3('Sponsoring collection from substrate address via access list', async ({api, web3, privateKeyWrapper}) => {
-  //   const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-  //   const collectionHelpers = evmCollectionHelpers(web3, owner);
-  //   const result = await collectionHelpers.methods.createERC721MetadataCompatibleNFTCollection('Sponsor collection', '1', '1', '').send();
-  //   const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-  //   const sponsor = privateKeyWrapper('//Alice');
-  //   const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-
-  //   await collectionEvm.methods.setCollectionSponsorSubstrate(sponsor.addressRaw).send({from: owner});
-
-  //   const confirmTx = await api.tx.unique.confirmSponsorship(collectionId);
-  //   await submitTransactionAsync(sponsor, confirmTx);
-
-  //   const user = createEthAccount(web3);
-  //   const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-  //   expect(nextTokenId).to.be.equal('1');
-
-  //   await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-  //   await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
-  //   await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
-
-  //   const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
-  //   const sponsorBalanceBefore = (await getBalance(api, [sponsor.address]))[0];
-
-  //   {
-  //     const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-  //     expect(nextTokenId).to.be.equal('1');
-  //     const result = await collectionEvm.methods.mintWithTokenURI(
-  //       user,
-  //       nextTokenId,
-  //       'Test URI',
-  //     ).send({from: user});
-  //     const events = normalizeEvents(result.events);
-
-  //     expect(events).to.be.deep.equal([
-  //       {
-  //         address: collectionIdAddress,
-  //         event: 'Transfer',
-  //         args: {
-  //           from: '0x0000000000000000000000000000000000000000',
-  //           to: user,
-  //           tokenId: nextTokenId,
-  //         },
-  //       },
-  //     ]);
-
-  //     const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
-  //     const sponsorBalanceAfter = (await getBalance(api, [sponsor.address]))[0];
-
-  //     expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
-  //     expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-  //     expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
-  //   }
-  // });
-
-  [
-    'setCollectionSponsorCross',
-    'setCollectionSponsor', // Soft-deprecated
-  ].map(testCase =>
-    itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-
-      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');
-
-      const collectionSub = helper.nft.getCollectionObject(collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, testCase === 'setCollectionSponsor');
-      // Set collection sponsor:
-      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();
-      let collectionData = (await collectionSub.getData())!;
-      expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-      collectionData = (await collectionSub.getData())!;
-      expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-
-      const user = helper.eth.createAccount();
-      const userCross = helper.ethCrossAccount.fromAddress(user);
-      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-
-      const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-      const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
-
-      const event = helper.eth.normalizeEvents(mintingResult.events)
-        .find(event => event.event === 'Transfer');
-      const address = helper.ethAddress.fromCollectionId(collectionId);
-
-      expect(event).to.be.deep.equal({
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user,
-          tokenId: '1',
-        },
-      });
-      expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-    }));
-
-  itEth('Can reassign collection sponsor', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsorEth = await helper.eth.createAccountWithBalance(donor);
-    const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
-    const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);
-    const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);
-
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner,'Sponsor collection', '1', '1', '');
-    const collectionSub = helper.nft.getCollectionObject(collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    // Set and confirm sponsor:
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
-
-    // Can reassign sponsor:
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});
-    const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
-    expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
-  });
-});
-
-describe('evm RFT collection sponsoring', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let nominal: bigint;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  [
-    'mintCross',
-    'mintWithTokenURI',
-  ].map(testCase =>
-    itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => {
-      const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [
-        {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
-      ]});
-
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      await collection.setSponsor(alice, alice.address);
-      await collection.confirmSponsorship(alice);
-
-      const minter = helper.eth.createAccount();
-      const minterCross = helper.ethCrossAccount.fromAddress(minter);
-      expect(await helper.balance.getEthereum(minter)).to.equal(0n);
-
-      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true);
-
-      await collection.addToAllowList(alice, {Ethereum: minter});
-      await collection.addAdmin(alice, {Ethereum: owner});
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
-      await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/')
-        .send();
-
-      let mintingResult;
-      let tokenId;
-      switch (testCase) {
-        case 'mintCross':
-          mintingResult = await contract.methods.mintCross(minterCross, []).send();
-          break;
-        case 'mintWithTokenURI':
-          mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send();
-          tokenId = mintingResult.events.Transfer.returnValues.tokenId;
-          expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
-          break;
-      }
-
-      const events = helper.eth.normalizeEvents(mintingResult.events);
-      expect(events).to.deep.include({
-        address: collectionAddress,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: minter,
-          tokenId: '1',
-        },
-      });
-    }));
-
-  [
-    'setCollectionSponsorCross',
-    'setCollectionSponsor', // Soft-deprecated
-  ].map(testCase =>
-    itEth(`[${testCase}] can remove collection sponsor`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
-
-      let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send({value: Number(2n * nominal)});
-      const collectionIdAddress = helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionIdAddress, 'rft', owner, testCase === 'setCollectionSponsor');
-
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
-      result = await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send({from: owner});
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
-
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
-
-      await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
-
-      const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
-      expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');
-    }));
-
-  [
-    'setCollectionSponsorCross',
-    'setCollectionSponsor', // Soft-deprecated
-  ].map(testCase =>
-    itEth(`[${testCase}] Can sponsor from evm address via access list`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
-
-      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Sponsor collection', '1', '1', '');
-
-      const collectionSub = helper.rft.getCollectionObject(collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');
-
-      // Set collection sponsor:
-      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsorEth : sponsorCrossEth).send({from: owner});
-      let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
-      expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
-      // Account cannot confirm sponsorship if it is not set as a sponsor
-      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-      // Sponsor can confirm sponsorship:
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
-      sponsorship = (await collectionSub.getData())!.raw.sponsorship;
-      expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
-
-      // Create user with no balance:
-      const user = helper.eth.createAccount();
-      const userCross = helper.ethCrossAccount.fromAddress(user);
-      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-
-      // Set collection permissions:
-      const oldPermissions = (await collectionSub.getData())!.raw.permissions;
-      expect(oldPermissions.mintMode).to.be.false;
-      expect(oldPermissions.access).to.be.equal('Normal');
-
-      await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-      await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
-      await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
-
-      const newPermissions = (await collectionSub.getData())!.raw.permissions;
-      expect(newPermissions.mintMode).to.be.true;
-      expect(newPermissions.access).to.be.equal('AllowList');
-
-      // Set token permissions
-      await collectionEvm.methods.setTokenPropertyPermissions([
-        ['URI', [
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true],
-        ],
-        ],
-      ]).send({from: owner});
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-      // User can mint token without balance:
-      {
-        const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-        const events = helper.eth.normalizeEvents(result.events);
-
-        expect(events).to.deep.include({
-          address: collectionAddress,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: user,
-            tokenId: '1',
-          },
-        });
-
-        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-        const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-        expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
-        expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-        expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
-      }
-    }));
-
-  [
-    'setCollectionSponsorCross',
-    'setCollectionSponsor', // Soft-deprecated
-  ].map(testCase =>
-    itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-
-      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-
-      const collectionSub = helper.rft.getCollectionObject(collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');
-      // Set collection sponsor:
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
-      await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
-      let collectionData = (await collectionSub.getData())!;
-      expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
-
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-      collectionData = (await collectionSub.getData())!;
-      expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
-      const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
-      const sponsorSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.ethToSubstrate(sponsor));
-      const actualSubAddress = helper.address.normalizeSubstrateToChainFormat(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub)));
-      expect(actualSubAddress).to.be.equal(sponsorSubAddress);
-
-      const user = helper.eth.createAccount();
-      const userCross = helper.ethCrossAccount.fromAddress(user);
-      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-
-      const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-      const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
-
-      const events = helper.eth.normalizeEvents(mintingResult.events);
-      const address = helper.ethAddress.fromCollectionId(collectionId);
-
-      expect(events).to.deep.include({
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user,
-          tokenId: '1',
-        },
-      });
-      expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-    }));
-
-  itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = alice;
-    const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);
-
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-
-    const collectionSub = helper.rft.getCollectionObject(collectionId);
-    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);
-    // Set collection sponsor:
-    expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
-    expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
-    let collectionData = (await collectionSub.getData())!;
-    expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-    await collectionSub.confirmSponsorship(sponsor);
-    collectionData = (await collectionSub.getData())!;
-    expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);
-    expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
-    const sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
-    expect(BigInt(sponsorStruct.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));
-
-    const user = helper.eth.createAccount();
-    const userCross = helper.ethCrossAccount.fromAddress(user);
-    await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
-    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);
-
-    const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-    const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
-
-    const events = helper.eth.normalizeEvents(mintingResult.events);
-
-    expect(events).to.deep.include({
-      address: collectionAddress,
-      event: 'Transfer',
-      args: {
-        from: '0x0000000000000000000000000000000000000000',
-        to: user,
-        tokenId: '1',
-      },
-    });
-    expect(await collectionEvm.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
-
-    const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-    const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-  });
-
-  itEth('Sponsoring collection from substrate address via access list', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const user = helper.eth.createAccount();
-    const userCross =  helper.ethCrossAccount.fromAddress(user);
-    const sponsor = alice;
-    const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);
-
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, false);
-
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send({from: owner});
-
-    const collectionSub = helper.rft.getCollectionObject(collectionId);
-    await collectionSub.confirmSponsorship(sponsor);
-
-    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-
-    await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-
-    await collectionEvm.methods.addToCollectionAllowListCross(userCross).send({from: owner});
-    await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
-
-    // Set token permissions
-    await collectionEvm.methods.setTokenPropertyPermissions([
-      ['URI', [
-        [TokenPermissionField.TokenOwner, true],
-        [TokenPermissionField.CollectionAdmin, true],
-      ],
-      ],
-    ]).send({from: owner});
-
-    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);
-    const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-    {
-      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-      const mintingResult = await collectionEvm.methods.mintWithTokenURI(
-        user,
-        'Test URI',
-      ).send({from: user});
-
-      const events = helper.eth.normalizeEvents(mintingResult.events);
-
-
-      expect(events).to.deep.include({
-        address: collectionAddress,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user,
-          tokenId: nextTokenId,
-        },
-      });
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);
-      const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-      expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
-      expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-      expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
-    }
-  });
-
-  itEth('Can reassign collection sponsor', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsorEth = await helper.eth.createAccountWithBalance(donor);
-    const sponsorCrossEth = helper.ethCrossAccount.fromAddress(sponsorEth);
-    const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);
-    const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);
-
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-    const collectionSub = helper.rft.getCollectionObject(collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-    // Set and confirm sponsor:
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossEth).send({from: owner});
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
-
-    // Can reassign sponsor:
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});
-    const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
-    expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
-  });
-
-  [
-    'transfer',
-    'transferCross',
-    'transferFrom',
-    'transferFromCross',
-  ].map(testCase =>
-    itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      const receiver = await helper.eth.createAccountWithBalance(donor);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-
-      const user = await helper.eth.createAccountWithBalance(donor);
-      const userCross = helper.ethCrossAccount.fromAddress(user);
-      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
-      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-      switch (testCase) {
-        case 'transfer':
-          await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});
-          break;
-        case 'transferCross':
-          await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
-          break;
-        case 'transferFrom':
-          await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});
-          break;
-        case 'transferFromCross':
-          await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
-          break;
-      }
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-    }));
-});
-
-describe('evm RFT token sponsoring', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  [
-    'transfer',
-    'transferCross',
-    'transferFrom',
-    'transferFromCross',
-  ].map(testCase =>
-    itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      const receiver = await helper.eth.createAccountWithBalance(donor);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-
-      const user = await helper.eth.createAccountWithBalance(donor);
-      const userCross = helper.ethCrossAccount.fromAddress(user);
-      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
-      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-
-      const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
-      await tokenContract.methods.repartition(2).send();
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-      switch (testCase) {
-        case 'transfer':
-          await tokenContract.methods.transfer(receiver, 1).send();
-          break;
-        case 'transferCross':
-          await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
-          break;
-        case 'transferFrom':
-          await tokenContract.methods.transferFrom(user, receiver, 1).send();
-          break;
-        case 'transferFromCross':
-          await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();
-          break;
-      }
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-    }));
-
-  [
-    'approve',
-    'approveCross',
-  ].map(testCase =>
-    itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      const receiver = await helper.eth.createAccountWithBalance(donor);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-
-      const user = await helper.eth.createAccountWithBalance(donor);
-      const userCross = helper.ethCrossAccount.fromAddress(user);
-      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
-      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-
-      const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);
-      await tokenContract.methods.repartition(2).send();
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-      switch (testCase) {
-        case 'approve':
-          await tokenContract.methods.approve(receiver, 1).send();
-          break;
-        case 'approveCross':
-          await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
-          break;
-      }
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-    }));
-});
-
deletedjs-packages/tests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/contractSponsoring.test.ts
+++ /dev/null
@@ -1,579 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 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 type {CompiledContract} from './util/playgrounds/types.js';
-
-describe('Sponsoring EVM contracts', () => {
-  let donor: IKeyringPair;
-  let nominal: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  itEth('Self sponsoring can be set by the address that deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const flipper = await helper.eth.deployFlipper(owner);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-
-    // 1. owner can set selfSponsoring:
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-    const result = await helpers.methods.selfSponsoredEnable(flipper.options.address).send({from: owner});
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-
-    // 1.1 Can get sponsor using methods.sponsor:
-    const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();
-    expect(actualSponsorOpt.status).to.be.true;
-    const actualSponsor = actualSponsorOpt.value;
-    expect(actualSponsor.eth).to.eq(flipper.options.address);
-    expect(actualSponsor.sub).to.eq('0');
-
-    // 2. Events should be:
-    const ethEvents = helper.eth.helper.eth.normalizeEvents(result.events);
-    expect(ethEvents).to.be.deep.equal([
-      {
-        address: flipper.options.address,
-        event: 'ContractSponsorSet',
-        args: {
-          contractAddress: flipper.options.address,
-          sponsor: flipper.options.address,
-        },
-      },
-      {
-        address: flipper.options.address,
-        event: 'ContractSponsorshipConfirmed',
-        args: {
-          contractAddress: flipper.options.address,
-          sponsor: flipper.options.address,
-        },
-      },
-    ]);
-  });
-
-  itEth('Self sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const notOwner = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-    await expect(helpers.methods.selfSponsoredEnable(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-  });
-
-  itEth('Sponsoring can be set by the address that has deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
-    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
-    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
-  });
-
-  itEth('Sponsoring cannot be set by the address that did not deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const notOwner = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
-    await expect(helpers.methods.setSponsoringMode(notOwner, SponsoringMode.Allowlisted).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
-  });
-
-  itEth('Sponsor can be set by the address that deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    // 1. owner can set a sponsor:
-    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
-    const result = await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.true;
-
-    // 2. Events should be:
-    const events = helper.eth.normalizeEvents(result.events);
-    expect(events).to.be.deep.equal([
-      {
-        address: flipper.options.address,
-        event: 'ContractSponsorSet',
-        args: {
-          contractAddress: flipper.options.address,
-          sponsor: sponsor,
-        },
-      },
-    ]);
-  });
-
-  itEth('Sponsor cannot be set by the address that did not deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const notOwner = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
-    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-    expect(await helpers.methods.hasPendingSponsor(flipper.options.address).call()).to.be.false;
-  });
-
-  itEth('Sponsorship can be confirmed by the address that pending as sponsor', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-
-    // 1. sponsor can confirm sponsorship:
-    const result = await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-
-    // 1.1 Can get sponsor using methods.sponsor:
-    const actualSponsorOpt = await helpers.methods.sponsor(flipper.options.address).call();
-    expect(actualSponsorOpt.status).to.be.true;
-    const actualSponsor = actualSponsorOpt.value;
-    expect(actualSponsor.eth).to.eq(sponsor);
-    expect(actualSponsor.sub).to.eq('0');
-
-    // 2. Events should be:
-    const events = helper.eth.normalizeEvents(result.events);
-    expect(events).to.be.deep.equal([
-      {
-        address: flipper.options.address,
-        event: 'ContractSponsorshipConfirmed',
-        args: {
-          contractAddress: flipper.options.address,
-          sponsor: sponsor,
-        },
-      },
-    ]);
-  });
-
-  itEth('Sponsorship can not be confirmed by the address that not pending as sponsor', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const notSponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-    await expect(helpers.methods.setSponsor(flipper.options.address, sponsor).send()).to.be.not.rejected;
-    await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPermission');
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-  });
-
-  itEth('Sponsorship can not be confirmed by the address that not set as sponsor', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const notSponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-    await expect(helpers.methods.confirmSponsorship(flipper.options.address).call({from: notSponsor})).to.be.rejectedWith('NoPendingSponsor');
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-  });
-
-  itEth('Sponsor can be removed by the address that deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-    // 1. Can remove sponsor:
-    const result = await helpers.methods.removeSponsor(flipper.options.address).send();
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-
-    // 2. Events should be:
-    const events = helper.eth.normalizeEvents(result.events);
-    expect(events).to.be.deep.equal([
-      {
-        address: flipper.options.address,
-        event: 'ContractSponsorRemoved',
-        args: {
-          contractAddress: flipper.options.address,
-        },
-      },
-    ]);
-
-    // TODO: why call method reverts?
-    // const actualSponsor = await helpers.methods.sponsor(flipper.options.address).call();
-    // expect(actualSponsor.eth).to.eq(sponsor);
-    // expect(actualSponsor.sub).to.eq('0');
-  });
-
-  itEth('Sponsor can not be removed by the address that did not deployed the contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const notOwner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-
-    await expect(helpers.methods.removeSponsor(flipper.options.address).call({from: notOwner})).to.be.rejectedWith('NoPermission');
-    await expect(helpers.methods.removeSponsor(flipper.options.address).send({from: notOwner})).to.be.rejected;
-    expect(await helpers.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-  });
-
-  itEth('In generous mode, non-allowlisted user transaction will be sponsored', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
-    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    const callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
-
-    await flipper.methods.flip().send({from: caller});
-    expect(await flipper.methods.getValue().call()).to.be.true;
-
-    // Balance should be taken from sponsor instead of caller
-    const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-    expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
-  });
-
-  itEth('In generous mode, non-allowlisted user transaction will be self sponsored', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    await helpers.methods.selfSponsoredEnable(flipper.options.address).send();
-
-    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-
-    await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
-
-    const contractBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));
-    const callerBalanceBefore = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
-
-    await flipper.methods.flip().send({from: caller});
-    expect(await flipper.methods.getValue().call()).to.be.true;
-
-    // Balance should be taken from sponsor instead of caller
-    const contractBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(flipper.options.address));
-    const callerBalanceAfter = await helper.balance.getSubstrate(await helper.address.ethToSubstrate(caller));
-    expect(contractBalanceAfter < contractBalanceBefore).to.be.true;
-    expect(callerBalanceAfter).to.be.eq(callerBalanceBefore);
-  });
-
-  [
-    {balance: 0n, label: '0'},
-    {balance: 10n, label: '10'},
-  ].map(testCase => {
-    itEth(`Allow-listed address that has ${testCase.label} UNQ can call a contract. Sponsor balance should decrease`, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const caller = helper.eth.createAccount();
-      await helper.eth.transferBalanceFromSubstrate(donor, caller, testCase.balance);
-      const helpers = await helper.ethNativeContract.contractHelpers(owner);
-      const flipper = await helper.eth.deployFlipper(owner);
-
-      await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
-      await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
-
-      await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
-      await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-
-      await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-      await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceBefore > 0n).to.be.true;
-
-      await flipper.methods.flip().send({from: caller});
-      expect(await flipper.methods.getValue().call()).to.be.true;
-
-      // Balance should be taken from flipper instead of caller
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-      // Caller's balance does not change:
-      const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
-      expect(callerBalanceAfter).to.eq(testCase.balance * nominal);
-    });
-  });
-
-  itEth('Non-allow-listed address can call a contract. Sponsor balance should not decrease', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const caller = helper.eth.createAccount();
-    const contractHelpers = await helper.ethNativeContract.contractHelpers(owner);
-
-    // Deploy flipper and send some tokens:
-    const flipper = await helper.eth.deployFlipper(owner);
-    await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
-    expect(await flipper.methods.getValue().call()).to.be.false;
-    // flipper address has some tokens:
-    const originalFlipperBalance = await helper.balance.getEthereum(flipper.options.address);
-    expect(originalFlipperBalance > 0n).to.be.true;
-
-    // Set Allowlisted sponsoring mode. caller is not in allow list:
-    await contractHelpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
-    await contractHelpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
-    await contractHelpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-
-    // 1. Caller has no UNQ and is not in allow list. So he cannot flip:
-    await expect(flipper.methods.flip().send({from: caller})).to.be.rejectedWith(/Returned error: insufficient funds for gas \* price \+ value/);
-    expect(await flipper.methods.getValue().call()).to.be.false;
-
-    // Flipper's balance does not change:
-    const balanceAfter = await helper.balance.getEthereum(flipper.options.address);
-    expect(balanceAfter).to.be.equal(originalFlipperBalance);
-  });
-
-  itEth('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    const originalCallerBalance = await helper.balance.getEthereum(caller);
-    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
-    await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
-
-    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 10).send({from: owner});
-
-    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
-    const originalFlipperBalance = await helper.balance.getEthereum(sponsor);
-    expect(originalFlipperBalance > 0n).to.be.true;
-
-    await flipper.methods.flip().send({from: caller});
-    expect(await flipper.methods.getValue().call()).to.be.true;
-    expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance);
-
-    const newFlipperBalance = await helper.balance.getEthereum(sponsor);
-    expect(newFlipperBalance).to.be.not.equal(originalFlipperBalance);
-
-    await flipper.methods.flip().send({from: caller});
-    // todo:playgrounds fails rarely (expected 99893341659775672580n to equal 99912598679356033129n) (again, 99893341659775672580n)
-    expect(await helper.balance.getEthereum(sponsor)).to.be.equal(newFlipperBalance);
-    expect(await helper.balance.getEthereum(caller)).to.be.not.equal(originalCallerBalance);
-  });
-
-  // TODO: Find a way to calculate default rate limit
-  itEth('Default rate limit equal 7200', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.sponsoringRateLimit(flipper.options.address).call()).to.be.equal('7200');
-  });
-
-  itEth('Gas price boundaries', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    await helpers.methods.setSponsor(flipper.options.address, sponsor).send();
-    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
-    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-
-    let sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    let callerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
-
-    let expectValue = await flipper.methods.getValue().call();
-
-    const flip = async (gasPrice: bigint, shouldPass = true) => {
-      await flipper.methods.flip().send({from: caller, gasPrice: gasPrice});
-      expectValue = !expectValue;
-      expect(await flipper.methods.getValue().call()).to.be.eq(expectValue);
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      const callerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(caller));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.eq(shouldPass);
-      expect(callerBalanceAfter === callerBalanceBefore).to.be.eq(shouldPass);
-      sponsorBalanceBefore = sponsorBalanceAfter;
-      callerBalanceBefore = callerBalanceAfter;
-    };
-
-    const gasPrice = BigInt((await helper.eth.getGasPrice())!);
-    await flip(gasPrice);
-    await flip(gasPrice * 2n);
-    await flip(gasPrice * 21n / 10n);
-    await flip(gasPrice * 22n / 10n, false);
-  });
-});
-
-describe('Sponsoring Fee Limit', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let testContract: CompiledContract;
-
-  async function compileTestContract(helper: EthUniqueHelper) {
-    if(!testContract) {
-      testContract = await helper.ethContract.compile(
-        'TestContract',
-        `
-        // SPDX-License-Identifier: MIT
-        pragma solidity ^0.8.0;
-
-        contract TestContract {
-          event Result(bool);
-
-          function test(uint32 cycles) public {
-            uint256 counter = 0;
-            while(true) {
-              counter ++;
-              if (counter > cycles){
-                break;
-              }
-            }
-            emit Result(true);
-          }
-        }
-      `,
-      );
-    }
-    return testContract;
-  }
-
-  async function deployTestContract(helper: EthUniqueHelper, owner: string) {
-    const compiled = await compileTestContract(helper);
-    return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object);
-  }
-
-  before(async () => {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  itEth('Default fee limit', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('115792089237316195423570985008687907853269984665640564039457584007913129639935');
-  });
-
-  itEth('Set fee limit', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    await helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send();
-    expect(await helpers.methods.sponsoringFeeLimit(flipper.options.address).call()).to.be.equal('100');
-  });
-
-  itEth('Negative test - set fee limit by non-owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const stranger = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    await expect(helpers.methods.setSponsoringFeeLimit(flipper.options.address, 100).send({from: stranger})).to.be.rejected;
-  });
-
-  itEth('Negative test - check that eth transactions exceeding fee limit are not executed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const user = await helper.eth.createAccountWithBalance(donor);
-    const helpers = helper.ethNativeContract.contractHelpers(owner);
-
-    const testContract = await deployTestContract(helper, owner);
-
-    await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});
-
-    await helpers.methods.setSponsor(testContract.options.address, sponsor).send();
-    await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});
-
-    const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());
-
-    await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();
-
-    const originalUserBalance = await helper.balance.getEthereum(user);
-    await testContract.methods.test(100).send({from: user, gas: 2_000_000, maxFeePerGas: gasPrice.toString()});
-    expect(await helper.balance.getEthereum(user)).to.be.equal(originalUserBalance);
-
-    await testContract.methods.test(100).send({from: user, gas: 2_100_000, maxFeePerGas: gasPrice.toString()});
-    expect(await helper.balance.getEthereum(user)).to.not.be.equal(originalUserBalance);
-  });
-
-  itEth('Negative test - check that evm.call transactions exceeding fee limit are not executed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-
-    const testContract = await deployTestContract(helper, owner);
-
-    await helpers.methods.setSponsoringMode(testContract.options.address, SponsoringMode.Generous).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(testContract.options.address, 0).send({from: owner});
-
-    await helpers.methods.setSponsor(testContract.options.address, sponsor).send();
-    await helpers.methods.confirmSponsorship(testContract.options.address).send({from: sponsor});
-
-    const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());
-
-    await helpers.methods.setSponsoringFeeLimit(testContract.options.address, 2_000_000n * gasPrice).send();
-
-    const originalAliceBalance = await helper.balance.getSubstrate(alice.address);
-
-    await helper.eth.sendEVM(
-      alice,
-      testContract.options.address,
-      testContract.methods.test(100).encodeABI(),
-      '0',
-      2_000_000,
-    );
-    // expect((await api.query.system.account(alice.address)).data.free.toBigInt()).to.be.equal(originalAliceBalance);
-    expect(await helper.balance.getSubstrate(alice.address)).to.be.equal(originalAliceBalance);
-
-    await helper.eth.sendEVM(
-      alice,
-      testContract.options.address,
-      testContract.methods.test(100).encodeABI(),
-      '0',
-      2_100_000,
-    );
-    expect(await helper.balance.getSubstrate(alice.address)).to.not.be.equal(originalAliceBalance);
-  });
-});
deletedjs-packages/tests/src/eth/createCollection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/createCollection.test.ts
+++ /dev/null
@@ -1,1449 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {evmToAddress} from '@polkadot/util-crypto';
-import {Pallets, requirePalletsOrSkip} from '../util/index.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/src/types.js';
-import type {IEthCrossAccountId, TCollectionMode} from '@unique/playgrounds/src/types.js';
-
-const DECIMALS = 18;
-const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [
-  [],  // properties
-  [],  // tokenPropertyPermissions
-  [],  // adminList
-  [false, false, []],  // nestingSettings
-  [],  // limits
-  emptyAddress,  // pendingSponsor
-  [0],  // flags
-];
-
-type ElementOf<A> = A extends readonly (infer T)[] ? T : never;
-function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {
-  if(args.length === 0) {
-    yield internalRest as any;
-    return;
-  }
-  for(const value of args[0]) {
-    yield* cartesian([...internalRest, value], ...args.slice(1)) as any;
-  }
-}
-
-describe('Create collection from EVM', () => {
-  let donor: IKeyringPair;
-  let nominal: bigint;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  describe('Fungible collection', () => {
-    before(async function() {
-      await usingEthPlaygrounds((helper) => {
-        requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
-        return Promise.resolve();
-      });
-    });
-
-    itEth('Collection address exist', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-      expect(await collectionHelpers
-        .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
-        .to.be.false;
-
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();
-      expect(await collectionHelpers
-        .methods.isCollectionExist(collectionAddress).call())
-        .to.be.true;
-
-      // check collectionOwner:
-      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
-      const collectionOwner = await collectionEvm.methods.collectionOwner().call();
-      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
-    });
-
-    itEth('destroyCollection', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();
-      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-
-      const result = await collectionHelper.methods
-        .destroyCollection(collectionAddress)
-        .send({from: owner});
-
-      const events = helper.eth.normalizeEvents(result.events);
-
-      expect(events).to.be.deep.equal([
-        {
-          address: collectionHelper.options.address,
-          event: 'CollectionDestroyed',
-          args: {
-            collectionId: collectionAddress,
-          },
-        },
-      ]);
-
-      expect(await collectionHelper.methods
-        .isCollectionExist(collectionAddress)
-        .call()).to.be.false;
-      expect(await helper.collection.getData(collectionId)).to.be.null;
-    });
-
-    itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-      {
-        const MAX_NAME_LENGTH = 64;
-        const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
-        const description = 'A';
-        const tokenPrefix = 'A';
-
-        await expect(collectionHelper.methods
-          .createCollection([
-            collectionName,
-            description,
-            tokenPrefix,
-            CollectionMode.Fungible,
-            DECIMALS,
-            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-          ])
-          .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
-      }
-      {
-        const MAX_DESCRIPTION_LENGTH = 256;
-        const collectionName = 'A';
-        const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
-        const tokenPrefix = 'A';
-        await expect(collectionHelper.methods
-          .createCollection([
-            collectionName,
-            description,
-            tokenPrefix,
-            CollectionMode.Fungible,
-            DECIMALS,
-            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-          ])
-          .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
-      }
-      {
-        const MAX_TOKEN_PREFIX_LENGTH = 16;
-        const collectionName = 'A';
-        const description = 'A';
-        const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
-        await expect(collectionHelper.methods
-          .createCollection([
-            collectionName,
-            description,
-            tokenPrefix,
-            CollectionMode.Fungible,
-            DECIMALS,
-            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-          ])
-          .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
-      }
-    });
-
-    itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-      const expects = [0n, 1n, 30n].map(async value => {
-        await expect(collectionHelper.methods
-          .createCollection([
-            'Peasantry',
-            'absolutely anything',
-            'TWIW',
-            CollectionMode.Fungible,
-            DECIMALS,
-            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-          ])
-          .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-      });
-      await Promise.all(expects);
-    });
-  });
-
-  describe('Nonfungible collection', () => {
-    before(async function() {
-      await usingEthPlaygrounds((helper) => {
-        requirePalletsOrSkip(this, helper, [Pallets.NFT]);
-        return Promise.resolve();
-      });
-    });
-
-    itEth('Create collection', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const name = 'CollectionEVM';
-      const description = 'Some description';
-      const prefix = 'token prefix';
-
-      // todo:playgrounds this might fail when in async environment.
-      const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-      const {collectionId, collectionAddress, events} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'nft')).send();
-
-      expect(events).to.be.deep.equal([
-        {
-          address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
-          event: 'CollectionCreated',
-          args: {
-            owner: owner,
-            collectionId: collectionAddress,
-          },
-        },
-      ]);
-
-      const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-
-      const collection = helper.nft.getCollectionObject(collectionId);
-      const data = (await collection.getData())!;
-
-      // Parallel test safety
-      expect(collectionCountAfter - collectionCountBefore).to.be.gte(1);
-      expect(collectionId).to.be.eq(collectionCountAfter);
-      expect(data.name).to.be.eq(name);
-      expect(data.description).to.be.eq(description);
-      expect(data.raw.tokenPrefix).to.be.eq(prefix);
-      expect(data.raw.mode).to.be.eq('NFT');
-
-      const options = await collection.getOptions();
-
-      expect(options.tokenPropertyPermissions).to.be.empty;
-    });
-
-    // this test will occasionally fail when in async environment.
-    itEth('Check collection address exist', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
-      const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-      expect(await collectionHelpers.methods
-        .isCollectionExist(expectedCollectionAddress)
-        .call()).to.be.false;
-
-      await collectionHelpers.methods
-        .createCollection([
-          'A',
-          'A',
-          'A',
-          CollectionMode.Nonfungible,
-          0,
-          ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-        ])
-        .send({value: Number(2n * helper.balance.getOneTokenNominal())});
-
-      expect(await collectionHelpers.methods
-        .isCollectionExist(expectedCollectionAddress)
-        .call()).to.be.true;
-    });
-  });
-
-  describe('Create RFT collection from EVM', () => {
-    before(async function() {
-      await usingEthPlaygrounds((helper) => {
-        requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-        return Promise.resolve();
-      });
-    });
-
-    itEth('Create collection', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const name = 'CollectionEVM';
-      const description = 'Some description';
-      const prefix = 'token prefix';
-
-      const {collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'rft')).send();
-      const data = (await helper.rft.getData(collectionId))!;
-      const collection = helper.rft.getCollectionObject(collectionId);
-
-      expect(data.name).to.be.eq(name);
-      expect(data.description).to.be.eq(description);
-      expect(data.raw.tokenPrefix).to.be.eq(prefix);
-      expect(data.raw.mode).to.be.eq('ReFungible');
-
-      const options = await collection.getOptions();
-
-      expect(options.tokenPropertyPermissions).to.be.empty;
-    });
-
-    itEth('Create collection with properties & get description', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const name = 'CollectionEVM';
-      const description = 'Some description';
-      const prefix = 'token prefix';
-      const baseUri = 'BaseURI';
-
-      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);
-      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-      const collection = helper.rft.getCollectionObject(collectionId);
-      const data = (await collection.getData())!;
-
-      expect(data.name).to.be.eq(name);
-      expect(data.description).to.be.eq(description);
-      expect(data.raw.tokenPrefix).to.be.eq(prefix);
-      expect(data.raw.mode).to.be.eq('ReFungible');
-
-      expect(await contract.methods.description().call()).to.deep.equal(description);
-
-      const options = await collection.getOptions();
-      expect(options.tokenPropertyPermissions).to.be.deep.equal([
-        {
-          key: 'URI',
-          permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
-        },
-        {
-          key: 'URISuffix',
-          permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
-        },
-      ]);
-    });
-
-    itEth('Set sponsorship', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const ss58Format = helper.chain.getChainProperties().ss58Format;
-      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send();
-
-      const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      await collection.methods.setCollectionSponsorCross(sponsorCross).send();
-
-      let data = (await helper.rft.getData(collectionId))!;
-      expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-      await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
-      await sponsorCollection.methods.confirmCollectionSponsorship().send();
-
-      data = (await helper.rft.getData(collectionId))!;
-      expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-    });
-
-    itEth('Collection address exist', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-      expect(await collectionHelpers
-        .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
-        .to.be.false;
-
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'rft')).send();
-      expect(await collectionHelpers
-        .methods.isCollectionExist(collectionAddress).call())
-        .to.be.true;
-
-      // check collectionOwner:
-      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
-      const collectionOwner = await collectionEvm.methods.collectionOwner().call();
-      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
-    });
-
-    itEth('destroyCollection', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send();
-      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-
-      await expect(collectionHelper.methods
-        .destroyCollection(collectionAddress)
-        .send({from: owner})).to.be.fulfilled;
-
-      expect(await collectionHelper.methods
-        .isCollectionExist(collectionAddress)
-        .call()).to.be.false;
-      expect(await helper.collection.getData(collectionId)).to.be.null;
-    });
-
-    itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-      {
-        const MAX_NAME_LENGTH = 64;
-        const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
-        const description = 'A';
-        const tokenPrefix = 'A';
-
-        await expect(collectionHelper.methods
-          .createCollection([
-            collectionName,
-            description,
-            tokenPrefix,
-            CollectionMode.Refungible,
-            DECIMALS,
-            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-          ])
-          .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
-      }
-      {
-        const MAX_DESCRIPTION_LENGTH = 256;
-        const collectionName = 'A';
-        const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
-        const tokenPrefix = 'A';
-        await expect(collectionHelper.methods
-          .createCollection([
-            collectionName,
-            description,
-            tokenPrefix,
-            CollectionMode.Refungible,
-            DECIMALS,
-            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-          ])
-          .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
-      }
-      {
-        const MAX_TOKEN_PREFIX_LENGTH = 16;
-        const collectionName = 'A';
-        const description = 'A';
-        const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
-        await expect(collectionHelper.methods
-          .createCollection([
-            collectionName,
-            description,
-            tokenPrefix,
-            CollectionMode.Refungible,
-            DECIMALS,
-            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-          ])
-          .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
-      }
-    });
-
-    itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-      await expect(collectionHelper.methods
-        .createCollection([
-          'Peasantry',
-          'absolutely anything',
-          'TWIW',
-          CollectionMode.Refungible,
-          0,
-          ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,
-        ])
-        .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-    });
-  });
-
-  describe('Sponsoring', () => {
-    itEth('Сan remove collection sponsor', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);
-
-      const {collectionAddress} = await helper.eth.createCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'Sponsor collection',
-          description: '1',
-          tokenPrefix: '1',
-          collectionMode: 'nft',
-          pendingSponsor: sponsorCross,
-        },
-      ).send();
-      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
-
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-      let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
-      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;
-
-      await collectionEvm.methods.removeCollectionSponsor().send({from: owner});
-
-      sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});
-      expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');
-    });
-
-    itEth('Can sponsor from evm address via access list', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsorEth);
-
-      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'Sponsor collection',
-          description: '1',
-          tokenPrefix: '1',
-          collectionMode: 'nft',
-          pendingSponsor: sponsorCross,
-          limits: [{field: CollectionLimitField.SponsoredDataRateLimit, value: 30n}],
-          tokenPropertyPermissions: [{key: 'key', permissions: [{code: TokenPermissionField.TokenOwner, value: true}]}],
-        },
-        '',
-      );
-
-      const collectionSub = helper.nft.getCollectionObject(collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-      let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
-      expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
-      // Account cannot confirm sponsorship if it is not set as a sponsor
-      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-      // Sponsor can confirm sponsorship:
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
-      sponsorship = (await collectionSub.getData())!.raw.sponsorship;
-      expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
-
-      // Create user with no balance:
-      const user = helper.ethCrossAccount.createAccount();
-      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-
-      // Set collection permissions:
-      const oldPermissions = (await collectionSub.getData())!.raw.permissions;
-      expect(oldPermissions.mintMode).to.be.false;
-      expect(oldPermissions.access).to.be.equal('Normal');
-
-      await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-      await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});
-      await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
-
-      const newPermissions = (await collectionSub.getData())!.raw.permissions;
-      expect(newPermissions.mintMode).to.be.true;
-      expect(newPermissions.access).to.be.equal('AllowList');
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
-
-      // User can mint token without balance:
-      {
-        const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});
-        const event = helper.eth.normalizeEvents(result.events)
-          .find(event => event.event === 'Transfer');
-
-        expect(event).to.be.deep.equal({
-          address: collectionAddress,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: user.eth,
-            tokenId: '1',
-          },
-        });
-
-        // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth});
-
-        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
-        const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));
-
-        expect(await collectionEvm.methods.properties(nextTokenId, []).call())
-          .to.be.like([
-            [
-              'key',
-              '0x' + Buffer.from('Value').toString('hex'),
-            ],
-          ]);
-        expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-        expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
-      }
-    });
-
-    itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);
-      const user = helper.eth.createAccount();
-      const userCross = helper.ethCrossAccount.fromAddress(user);
-
-      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'Sponsor collection',
-          description: '1',
-          tokenPrefix: '1',
-          collectionMode: 'nft',
-          pendingSponsor: sponsorCross,
-          adminList: [userCross],
-        },
-        '',
-      );
-
-      const collectionSub = helper.nft.getCollectionObject(collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-      // Set collection sponsor:
-      let collectionData = (await collectionSub.getData())!;
-      expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-      collectionData = (await collectionSub.getData())!;
-      expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-
-      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-
-      const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-      const tokenId = mintingResult.events.Transfer.returnValues.tokenId;
-
-      const event = helper.eth.normalizeEvents(mintingResult.events)
-        .find(event => event.event === 'Transfer');
-      const address = helper.ethAddress.fromCollectionId(collectionId);
-
-      expect(event).to.be.deep.equal({
-        address,
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user,
-          tokenId: '1',
-        },
-      });
-      expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');
-
-      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-    });
-
-    itEth('Can reassign collection sponsor', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const sponsorEth = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCrossEth = helper.ethCrossAccount.fromAddr(sponsorEth);
-      const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);
-      const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);
-
-      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'Sponsor collection',
-          description: '1',
-          tokenPrefix: '1',
-          collectionMode: 'nft',
-          pendingSponsor: sponsorCrossEth,
-        },
-        '',
-      );
-      const collectionSub = helper.nft.getCollectionObject(collectionId);
-      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-      // Set and confirm sponsor:
-      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
-
-      // Can reassign sponsor:
-      await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});
-      const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
-      expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
-    });
-
-    [
-      'transfer',
-      'transferCross',
-      'transferFrom',
-      'transferFromCross',
-    ].map(testCase =>
-      itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const sponsor = await helper.eth.createAccountWithBalance(donor);
-        const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);
-        const user = await helper.eth.createAccountWithBalance(donor);
-        const userCross = helper.ethCrossAccount.fromAddress(user);
-
-        const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(
-          owner,
-          {
-            ...CREATE_COLLECTION_DATA_DEFAULTS,
-            name: 'Sponsor collection',
-            description: '1',
-            tokenPrefix: '1',
-            collectionMode: 'rft',
-            pendingSponsor: sponsorCross,
-            adminList: [userCross],
-          },
-          '',
-        );
-        const receiver = await helper.eth.createAccountWithBalance(donor);
-        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-        await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-
-        const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-        const tokenId = result.events.Transfer.returnValues.tokenId;
-
-        const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-        const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-        const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-
-        switch (testCase) {
-          case 'transfer':
-            await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});
-            break;
-          case 'transferCross':
-            await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
-            break;
-          case 'transferFrom':
-            await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});
-            break;
-          case 'transferFromCross':
-            await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
-            break;
-        }
-
-        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-        expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-        expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-        const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-      }));
-  });
-
-  describe('Collection admins', () => {
-    let donor: IKeyringPair;
-
-    before(async function() {
-      await usingEthPlaygrounds(async (_helper, privateKey) => {
-        donor = await privateKey({url: import.meta.url});
-      });
-    });
-
-    [
-      {mode: 'nft' as const, requiredPallets: []},
-      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-      {mode: 'ft' as const, requiredPallets: []},
-    ].map(testCase => {
-      itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {
-        // arrange
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const adminSub = await privateKey('//admin2');
-        const adminEth = helper.eth.createAccount().toLowerCase();
-
-        const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
-        const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
-
-        const {collectionAddress, collectionId} = await helper.eth.createCollection(
-          owner,
-          {
-            ...CREATE_COLLECTION_DATA_DEFAULTS,
-            name: 'Mint collection',
-            description: 'a',
-            tokenPrefix: 'b',
-            collectionMode: testCase.mode,
-            adminList: [adminCrossSub, adminCrossEth],
-          },
-        ).send();
-        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
-
-        // 1. Expect api.rpc.unique.adminlist returns admins:
-        const adminListRpc = await helper.collection.getAdmins(collectionId);
-        expect(adminListRpc).to.has.length(2);
-        expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}]);
-
-        // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist
-        let adminListEth = await collectionEvm.methods.collectionAdmins().call();
-        adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));
-        expect(adminListRpc).to.be.like(adminListEth);
-
-        // 3. check isOwnerOrAdminCross returns true:
-        expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;
-        expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;
-      });
-    });
-
-    itEth('cross account admin can mint', async ({helper}) => {
-      // arrange: create collection and accounts
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
-      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
-      const [adminSub] = await helper.arrange.createAccounts([100n], donor);
-      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
-      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'Mint collection',
-          description: 'a',
-          tokenPrefix: 'b',
-          collectionMode: 'nft',
-          adminList: [adminCrossSub, adminCrossEth],
-        },
-        'uri',
-      );
-      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-
-      // admin (sub and eth) can mint token:
-      await collectionEvm.methods.mint(owner).send({from: adminEth});
-      await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});
-
-      expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);
-    });
-
-    itEth('cannot add invalid cross account admin', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);
-
-      const adminCross = {
-        eth: helper.address.substrateToEth(admin.address),
-        sub: admin.addressRaw,
-      };
-
-      await expect(helper.eth.createCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'A',
-          description: 'B',
-          tokenPrefix: 'C',
-          collectionMode: 'nft',
-          adminList: [adminCross],
-        },
-      ).call()).to.be.rejected;
-    });
-
-    itEth('Remove [cross] admin by owner', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const [adminSub] = await helper.arrange.createAccounts([10n], donor);
-      const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();
-      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);
-      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);
-
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'A',
-          description: 'B',
-          tokenPrefix: 'C',
-          collectionMode: 'nft',
-          adminList: [adminCrossSub, adminCrossEth],
-        },
-      ).send();
-
-      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-      {
-        const adminList = await helper.collection.getAdmins(collectionId);
-        expect(adminList).to.deep.include({Substrate: adminSub.address});
-        expect(adminList).to.deep.include({Ethereum: adminEth});
-      }
-
-      await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();
-      await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();
-      const adminList = await helper.collection.getAdmins(collectionId);
-      expect(adminList.length).to.be.eq(0);
-
-      // Non admin cannot mint:
-      await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);
-      await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;
-    });
-  });
-
-  describe('Collection limits', () => {
-    describe('Can set collection limits', () => {
-      [
-        {case: 'nft' as const},
-        {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-        {case: 'ft' as const},
-      ].map(testCase =>
-        itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
-          const owner = await helper.eth.createAccountWithBalance(donor);
-
-          const limits = {
-            accountTokenOwnershipLimit: 1000n,
-            sponsoredDataSize: 1024n,
-            sponsoredDataRateLimit: 30n,
-            tokenLimit: 1000000n,
-            sponsorTransferTimeout: 6n,
-            sponsorApproveTimeout: 6n,
-            ownerCanTransfer: 1n,
-            ownerCanDestroy: 0n,
-            transfersEnabled: 0n,
-          };
-
-          const {collectionId, collectionAddress} = await helper.eth.createCollection(
-            owner,
-            {
-              ...CREATE_COLLECTION_DATA_DEFAULTS,
-              name: 'Limits',
-              description: 'absolutely anything',
-              tokenPrefix: 'FLO',
-              collectionMode: testCase.case,
-              limits: [
-                {field: CollectionLimitField.AccountTokenOwnership, value: limits.accountTokenOwnershipLimit},
-                {field: CollectionLimitField.SponsoredDataSize, value: limits.sponsoredDataSize},
-                {field: CollectionLimitField.SponsoredDataRateLimit, value: limits.sponsoredDataRateLimit},
-                {field: CollectionLimitField.TokenLimit, value: limits.tokenLimit},
-                {field: CollectionLimitField.SponsorTransferTimeout, value: limits.sponsorTransferTimeout},
-                {field: CollectionLimitField.SponsorApproveTimeout, value: limits.sponsorApproveTimeout},
-                {field: CollectionLimitField.OwnerCanTransfer, value: limits.ownerCanTransfer},
-                {field: CollectionLimitField.OwnerCanDestroy, value: limits.ownerCanDestroy},
-                {field: CollectionLimitField.TransferEnabled, value: limits.transfersEnabled},
-              ],
-            },
-          ).send();
-
-          const expectedLimits = {
-            accountTokenOwnershipLimit: 1000,
-            sponsoredDataSize: 1024,
-            sponsoredDataRateLimit: {blocks: 30},
-            tokenLimit: 1000000,
-            sponsorTransferTimeout: 6,
-            sponsorApproveTimeout: 6,
-            ownerCanTransfer: true,
-            ownerCanDestroy: false,
-            transfersEnabled: false,
-          };
-
-          const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
-
-          // Check limits from sub:
-          const data = (await helper.rft.getData(collectionId))!;
-          expect(data.raw.limits).to.deep.eq(expectedLimits);
-          expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits);
-          // Check limits from eth:
-          const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner});
-          expect(limitsEvm).to.have.length(9);
-          expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]);
-          expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]);
-          expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]);
-          expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]);
-          expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]);
-          expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]);
-          expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]);
-          expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]);
-          expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]);
-        }));
-    });
-
-    describe('(!negative test!) Cannot set invalid collection limits', () => {
-      [
-        {case: 'nft' as const},
-        {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-        {case: 'ft' as const},
-      ].map(testCase =>
-        itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {
-          const invalidLimits = {
-            accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER),
-            transfersEnabled: 3,
-          };
-
-          const createCollectionData = {
-            ...CREATE_COLLECTION_DATA_DEFAULTS,
-            name: 'Limits',
-            description: 'absolutely anything',
-            tokenPrefix: 'ISNI',
-            collectionMode: testCase.case,
-          };
-
-          const owner = await helper.eth.createAccountWithBalance(donor);
-          await expect(helper.eth.createCollection(
-            owner,
-            {
-              ...createCollectionData,
-              limits: [{field: 9 as CollectionLimitField, value: 1n}],
-            },
-          ).call()).to.be.rejectedWith('value not convertible into enum "CollectionLimitField"');
-
-          await expect(helper.eth.createCollection(
-            owner,
-            {
-              ...createCollectionData,
-              limits: [{field: CollectionLimitField.AccountTokenOwnership, value: invalidLimits.accountTokenOwnershipLimit}],
-            },
-          ).call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`);
-
-          await expect(helper.eth.createCollection(
-            owner,
-            {
-              ...createCollectionData,
-              limits: [{field: CollectionLimitField.TransferEnabled, value: 3n}],
-            },
-          ).call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`);
-
-          await expect(helper.eth.createCollection(
-            owner,
-            {
-              ...createCollectionData,
-              limits: [{field: CollectionLimitField.SponsoredDataSize, value: -1n}],
-            },
-          ).call()).to.be.rejectedWith('value out-of-bounds');
-        }));
-    });
-  });
-
-  describe('Collection properties', () => {
-
-    [
-      {mode: 'nft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
-      {mode: 'rft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
-      {mode: 'ft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},
-    ].map(testCase =>
-      itEth.ifWithPallets(`Collection properties can be set for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
-        const caller = await helper.eth.createAccountWithBalance(donor);
-        const callerCross = helper.ethCrossAccount.fromAddress(caller);
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const {collectionId, collectionAddress} = await helper.eth.createCollection(
-          owner,
-          {
-            ...CREATE_COLLECTION_DATA_DEFAULTS,
-            name: 'name',
-            description: 'test',
-            tokenPrefix: 'test',
-            collectionMode: testCase.mode,
-            adminList: [callerCross],
-            properties: testCase.methodParams,
-          },
-        ).send();
-
-        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
-
-        const raw = (await helper[testCase.mode].getData(collectionId))?.raw;
-        expect(raw.properties).to.deep.equal(testCase.expectedProps);
-
-        // collectionProperties returns properties:
-        expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));
-      }));
-
-    [
-      {mode: 'nft' as const},
-      {mode: 'rft' as const},
-      {mode: 'ft' as const},
-    ].map(testCase =>
-      itEth.ifWithPallets(`Collection properties can be deleted for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {
-        const caller = await helper.eth.createAccountWithBalance(donor);
-        const callerCross = helper.ethCrossAccount.fromAddress(caller);
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const {collectionId, collectionAddress} = await helper.eth.createCollection(
-          owner,
-          {
-            ...CREATE_COLLECTION_DATA_DEFAULTS,
-            name: 'name',
-            description: 'test',
-            tokenPrefix: 'test',
-            collectionMode: testCase.mode,
-            adminList: [callerCross],
-            properties:[
-              {key: 'testKey1', value: Buffer.from('testValue1')},
-              {key: 'testKey2', value: Buffer.from('testValue2')},
-              {key: 'testKey3', value: Buffer.from('testValue3')}],
-          },
-        ).send();
-
-        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
-
-        await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller});
-
-        const raw = (await helper[testCase.mode].getData(collectionId))?.raw;
-
-        expect(raw.properties.length).to.equal(1);
-        expect(raw.properties).to.deep.equal([{key: 'testKey3', value: 'testValue3'}]);
-      }));
-
-    itEth('(!negative test!) Cannot set invalid properties', async({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const callerCross = helper.ethCrossAccount.fromAddress(caller);
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const createCollectionData = {
-        ...CREATE_COLLECTION_DATA_DEFAULTS,
-        name: 'name',
-        description: 'test',
-        tokenPrefix: 'test',
-        collectionMode: 'nft' as TCollectionMode,
-        adminList: [callerCross],
-      };
-      await expect(helper.eth.createCollection(
-        owner,
-        {
-          ...createCollectionData,
-          properties: [{key: '', value: Buffer.from('val1')}],
-        },
-      ).call()).to.be.rejected;
-
-      await expect(helper.eth.createCollection(
-        owner,
-        {
-          ...createCollectionData,
-          properties: [{key: 'déjà vu', value: Buffer.from('hmm...')}],
-        },
-      ).call()).to.be.rejected;
-
-      await expect(helper.eth.createCollection(
-        owner,
-        {
-          ...createCollectionData,
-          properties: [{key: 'a'.repeat(257), value: Buffer.from('val3')}],
-        },
-      ).call()).to.be.rejected;
-    });
-
-    itEth('(!negative test!) cannot delete properties of non-owned collections', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress} = await helper.eth.createCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'name',
-          description: 'test',
-          tokenPrefix: 'test',
-          collectionMode: 'nft',
-          properties:[
-            {key: 'testKey1', value: Buffer.from('testValue1')},
-            {key: 'testKey2', value: Buffer.from('testValue2')}],
-        },
-      ).send();
-
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
-
-      await expect(collectionEvm.methods.deleteCollectionProperties(['testKey2']).send({from: caller})).to.be.rejected;
-    });
-  });
-
-  describe('Token property permissions', () => {
-    [
-      {mode: 'nft' as const, requiredPallets: []},
-      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    ].map(testCase =>
-      itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
-        for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
-          const {collectionId, collectionAddress} = await helper.eth.createCollection(
-            owner,
-            {
-              ...CREATE_COLLECTION_DATA_DEFAULTS,
-              name: 'A',
-              description: 'B',
-              tokenPrefix: 'C',
-              collectionMode: testCase.mode,
-              adminList: [caller],
-              tokenPropertyPermissions: [
-                {
-                  key: 'testKey',
-                  permissions: [
-                    {code: TokenPermissionField.Mutable, value: mutable},
-                    {code: TokenPermissionField.TokenOwner, value: tokenOwner},
-                    {code: TokenPermissionField.CollectionAdmin, value: collectionAdmin},
-                  ],
-                },
-              ],
-            },
-          ).send();
-          const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-          expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
-            key: 'testKey',
-            permission: {mutable, collectionAdmin, tokenOwner},
-          }]);
-
-          expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
-            ['testKey', [
-              [TokenPermissionField.Mutable.toString(), mutable],
-              [TokenPermissionField.TokenOwner.toString(), tokenOwner],
-              [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]],
-            ],
-          ]);
-        }
-      }));
-
-    [
-      {mode: 'nft' as const, requiredPallets: []},
-      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    ].map(testCase =>
-      itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions`, testCase.requiredPallets, async({helper}) => {
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const {collectionId, collectionAddress} = await helper.eth.createCollection(
-          owner,
-          {
-            ...CREATE_COLLECTION_DATA_DEFAULTS,
-            name: 'A',
-            description: 'B',
-            tokenPrefix: 'C',
-            collectionMode: testCase.mode,
-            tokenPropertyPermissions: [
-              {
-                key: 'testKey_0',
-                permissions: [
-                  {code: TokenPermissionField.Mutable, value: true},
-                  {code: TokenPermissionField.TokenOwner, value: true},
-                  {code: TokenPermissionField.CollectionAdmin, value: true}],
-              },
-              {
-                key: 'testKey_1',
-                permissions: [
-                  {code: TokenPermissionField.Mutable, value: true},
-                  {code: TokenPermissionField.TokenOwner, value: false},
-                  {code: TokenPermissionField.CollectionAdmin, value: true}],
-              },
-              {
-                key: 'testKey_2',
-                permissions: [
-                  {code: TokenPermissionField.Mutable, value: false},
-                  {code: TokenPermissionField.TokenOwner, value: true},
-                  {code: TokenPermissionField.CollectionAdmin, value: false}],
-              },
-            ],
-          },
-        ).send();
-        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-        expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
-          {
-            key: 'testKey_0',
-            permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
-          },
-          {
-            key: 'testKey_1',
-            permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
-          },
-          {
-            key: 'testKey_2',
-            permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
-          },
-        ]);
-
-        expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([
-          ['testKey_0', [
-            [TokenPermissionField.Mutable.toString(), true],
-            [TokenPermissionField.TokenOwner.toString(), true],
-            [TokenPermissionField.CollectionAdmin.toString(), true]],
-          ],
-          ['testKey_1', [
-            [TokenPermissionField.Mutable.toString(), true],
-            [TokenPermissionField.TokenOwner.toString(), false],
-            [TokenPermissionField.CollectionAdmin.toString(), true]],
-          ],
-          ['testKey_2', [
-            [TokenPermissionField.Mutable.toString(), false],
-            [TokenPermissionField.TokenOwner.toString(), true],
-            [TokenPermissionField.CollectionAdmin.toString(), false]],
-          ],
-        ]);
-      }));
-
-    [
-      {mode: 'nft' as const, requiredPallets: []},
-      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    ].map(testCase =>
-      itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
-        const caller = await helper.eth.createAccountWithBalance(donor);
-        const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
-        const {collectionAddress} = await helper.eth.createCollection(
-          caller,
-          {
-            ...CREATE_COLLECTION_DATA_DEFAULTS,
-            name: 'A',
-            description: 'B',
-            tokenPrefix: 'C',
-            collectionMode: testCase.mode,
-            adminList: [receiver],
-            tokenPropertyPermissions: [
-              {
-                key: 'testKey',
-                permissions: [
-                  {code: TokenPermissionField.Mutable, value: true},
-                  {code: TokenPermissionField.CollectionAdmin, value: true}],
-              },
-              {
-                key: 'testKey_1',
-                permissions: [
-                  {code: TokenPermissionField.Mutable, value: true},
-                  {code: TokenPermissionField.CollectionAdmin, value: true}],
-              },
-            ],
-          },
-        ).send();
-
-        const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
-        const tokenId = (await collection.methods.mintCross(receiver, [{key: 'testKey', value: Buffer.from('testValue')}, {key: 'testKey_1', value: Buffer.from('testValue_1')}]).send()).events.Transfer.returnValues.tokenId;
-        expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2);
-
-        await collection.methods.deleteProperties(tokenId, ['testKey', 'testKey_1']).send({from: caller});
-        expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(0);
-      }));
-  });
-
-  describe('Nesting', () => {
-    itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'A',
-          description: 'B',
-          tokenPrefix: 'C',
-          collectionMode: 'nft',
-          nestingSettings: {token_owner: true, collection_admin: false, restricted: []},
-        },
-      ).send();
-
-      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-      // Create a token to be nested to
-      const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;
-      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);
-
-      // Create a nested token
-      const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});
-      const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;
-      expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);
-
-      // Create a token to be nested and nest
-      const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;
-
-      await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});
-      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);
-
-      // Unnest token back
-      await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});
-      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);
-    });
-
-    itEth('NFT: collectionNesting()', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createCollection(
-        owner,
-        new CreateCollectionData('A', 'B', 'C', 'nft'),
-      ).send();
-
-      const unnestedContract = helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);
-      expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
-
-      const {collectionAddress} = await helper.eth.createCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'A',
-          description: 'B',
-          tokenPrefix: 'C',
-          collectionMode: 'nft',
-          nestingSettings: {token_owner: true, collection_admin: false, restricted: [unnestedCollectionAddress.toString()]},
-        },
-      ).send();
-
-      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]);
-      await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});
-      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
-    });
-
-    itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionId, collectionAddress} = await helper.eth.createCollection(
-        owner,
-        {
-          ...CREATE_COLLECTION_DATA_DEFAULTS,
-          name: 'A',
-          description: 'B',
-          tokenPrefix: 'C',
-          collectionMode: 'nft',
-          nestingSettings: {token_owner: false, collection_admin: false, restricted: []},
-        },
-      ).send();
-
-      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-      // Create a token to nest into
-      const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
-      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);
-
-      // Create a token to nest
-      const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;
-
-      // Try to nest
-      await expect(contract.methods
-        .transfer(targetNftTokenAddress, nftTokenId)
-        .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
-    });
-  });
-
-  describe('Flags', () => {
-    const createCollectionData = {
-      ...CREATE_COLLECTION_DATA_DEFAULTS,
-      name: 'A',
-      description: 'B',
-      tokenPrefix: 'C',
-      collectionMode: 'nft' as TCollectionMode,
-    };
-
-    itEth('NFT: use numbers for flags', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      {
-        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 0}).send();
-        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});
-      }
-
-      {
-        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 64}).send();
-        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});
-      }
-    });
-
-    itEth('NFT: can\'t set foreign flag number', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      {
-        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
-      }
-
-      {
-        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
-      }
-    });
-
-    itEth('NFT: use enum for flags', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      {
-        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata]}).send();
-        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});
-      }
-    });
-
-    itEth('NFT: foreign flag enum is ignored', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      {
-        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
-      }
-
-      {
-        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
-      }
-    });
-  });
-});
deletedjs-packages/tests/src/eth/createFTCollection.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/createFTCollection.seqtest.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-
-const DECIMALS = 18;
-
-describe('Create FT collection from EVM', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Create collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const name = 'CollectionEVM';
-    const description = 'Some description';
-    const prefix = 'token prefix';
-
-    // todo:playgrounds this might fail when in async environment.
-    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-
-    const {collectionId} = await helper.eth.createFungibleCollection(owner, name, DECIMALS, description, prefix);
-
-    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-    const data = (await helper.ft.getData(collectionId))!;
-
-    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
-    expect(collectionId).to.be.eq(collectionCountAfter);
-    expect(data.name).to.be.eq(name);
-    expect(data.description).to.be.eq(description);
-    expect(data.raw.tokenPrefix).to.be.eq(prefix);
-    expect(data.raw.mode).to.be.deep.eq({Fungible: DECIMALS.toString()});
-  });
-
-  // todo:playgrounds this test will fail when in async environment.
-  itEth('Check collection address exist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
-    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.false;
-
-
-    await helper.eth.createFungibleCollection(owner, 'A', DECIMALS, 'A', 'A');
-
-
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.true;
-  });
-});
deletedjs-packages/tests/src/eth/createFTCollection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/createFTCollection.test.ts
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {evmToAddress} from '@polkadot/util-crypto';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import {CollectionLimitField} from './util/playgrounds/types.js';
-
-const DECIMALS = 18;
-
-describe('Create FT collection from EVM', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  // TODO move sponsorship tests to another file:
-  // Soft-deprecated
-  itEth('[eth] Set sponsorship', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const ss58Format = helper.chain.getChainProperties().ss58Format;
-    const description = 'absolutely anything';
-
-    const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY');
-
-    const collection = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
-    await collection.methods.setCollectionSponsor(sponsor).send();
-
-    let data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
-
-    data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-  });
-
-  itEth('[cross] Set sponsorship', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const ss58Format = helper.chain.getChainProperties().ss58Format;
-    const description = 'absolutely anything';
-    const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY');
-
-    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-    await collection.methods.setCollectionSponsorCross(sponsorCross).send();
-
-    let data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
-
-    data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-    expect(await collection.methods.description().call()).to.deep.equal(description);
-  });
-
-  itEth('Collection address exist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await collectionHelpers
-      .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
-      .to.be.false;
-
-    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT');
-    expect(await collectionHelpers
-      .methods.isCollectionExist(collectionAddress).call())
-      .to.be.true;
-
-    // check collectionOwner:
-    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
-    const collectionOwner = await collectionEvm.methods.collectionOwner().call();
-    expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
-  });
-
-  itEth('destroyCollection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT');
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-
-    const result = await collectionHelper.methods
-      .destroyCollection(collectionAddress)
-      .send({from: owner});
-
-    const events = helper.eth.normalizeEvents(result.events);
-
-    expect(events).to.be.deep.equal([
-      {
-        address: collectionHelper.options.address,
-        event: 'CollectionDestroyed',
-        args: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-
-    expect(await collectionHelper.methods
-      .isCollectionExist(collectionAddress)
-      .call()).to.be.false;
-    expect(await helper.collection.getData(collectionId)).to.be.null;
-  });
-});
-
-describe('(!negative tests!) Create FT collection from EVM', () => {
-  let donor: IKeyringPair;
-  let nominal: bigint;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
-      donor = await privateKey({url: import.meta.url});
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-    {
-      const MAX_NAME_LENGTH = 64;
-      const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
-      const description = 'A';
-      const tokenPrefix = 'A';
-
-      await expect(collectionHelper.methods
-        .createFTCollection(collectionName, DECIMALS, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
-    }
-    {
-      const MAX_DESCRIPTION_LENGTH = 256;
-      const collectionName = 'A';
-      const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
-      const tokenPrefix = 'A';
-      await expect(collectionHelper.methods
-        .createFTCollection(collectionName, DECIMALS, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
-    }
-    {
-      const MAX_TOKEN_PREFIX_LENGTH = 16;
-      const collectionName = 'A';
-      const description = 'A';
-      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
-      await expect(collectionHelper.methods
-        .createFTCollection(collectionName, DECIMALS, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
-    }
-  });
-
-  itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-    const expects = [0n, 1n, 30n].map(async value => {
-      await expect(collectionHelper.methods
-        .createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW')
-        .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-    });
-    await Promise.all(expects);
-  });
-
-  // Soft-deprecated
-  itEth('(!negative test!) [eth] Check owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const peasant = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
-    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true);
-    const EXPECTED_ERROR = 'NoPermission';
-    {
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      await expect(peasantCollection.methods
-        .setCollectionSponsor(sponsor)
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-
-      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true);
-      await expect(sponsorCollection.methods
-        .confirmCollectionSponsorship()
-        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-    }
-    {
-      await expect(peasantCollection.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-    }
-  });
-
-  itEth('(!negative test!) [cross] Check owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const peasant = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
-    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant);
-    const EXPECTED_ERROR = 'NoPermission';
-    {
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      await expect(peasantCollection.methods
-        .setCollectionSponsorCross(sponsorCross)
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-
-      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor);
-      await expect(sponsorCollection.methods
-        .confirmCollectionSponsorship()
-        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-    }
-    {
-      await expect(peasantCollection.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-    }
-  });
-});
deletedjs-packages/tests/src/eth/createNFTCollection.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/createNFTCollection.seqtest.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-
-
-describe('Create NFT collection from EVM', () => {
-  let donor: IKeyringPair;
-
-  before(async function () {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Create collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const name = 'CollectionEVM';
-    const description = 'Some description';
-    const prefix = 'token prefix';
-
-    // todo:playgrounds this might fail when in async environment.
-    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-    const {collectionId, collectionAddress, events} = await helper.eth.createNFTCollection(owner, name, description, prefix);
-
-    expect(events).to.be.deep.equal([
-      {
-        address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
-        event: 'CollectionCreated',
-        args: {
-          owner: owner,
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-
-    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
-
-    const collection = helper.nft.getCollectionObject(collectionId);
-    const data = (await collection.getData())!;
-
-    expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
-    expect(collectionId).to.be.eq(collectionCountAfter);
-    expect(data.name).to.be.eq(name);
-    expect(data.description).to.be.eq(description);
-    expect(data.raw.tokenPrefix).to.be.eq(prefix);
-    expect(data.raw.mode).to.be.eq('NFT');
-
-    const options = await collection.getOptions();
-
-    expect(options.tokenPropertyPermissions).to.be.empty;
-  });
-
-  // this test will occasionally fail when in async environment.
-  itEth('Check collection address exist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
-    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.false;
-
-    await collectionHelpers.methods
-      .createNFTCollection('A', 'A', 'A')
-      .send({value: Number(2n * helper.balance.getOneTokenNominal())});
-
-    expect(await collectionHelpers.methods
-      .isCollectionExist(expectedCollectionAddress)
-      .call()).to.be.true;
-  });
-});
deletedjs-packages/tests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/createNFTCollection.test.ts
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {evmToAddress} from '@polkadot/util-crypto';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import {CollectionLimitField} from './util/playgrounds/types.js';
-
-
-describe('Create NFT collection from EVM', () => {
-  let donor: IKeyringPair;
-
-  before(async function () {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Create collection with properties & get desctription', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const name = 'CollectionEVM';
-    const description = 'Some description';
-    const prefix = 'token prefix';
-    const baseUri = 'BaseURI';
-
-    const {collectionId, collectionAddress, events} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);
-    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    expect(events).to.be.deep.equal([
-      {
-        address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',
-        event: 'CollectionCreated',
-        args: {
-          owner: owner,
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-
-    const collection = helper.nft.getCollectionObject(collectionId);
-    const data = (await collection.getData())!;
-
-    expect(data.name).to.be.eq(name);
-    expect(data.description).to.be.eq(description);
-    expect(data.raw.tokenPrefix).to.be.eq(prefix);
-    expect(data.raw.mode).to.be.eq('NFT');
-
-    expect(await contract.methods.description().call()).to.deep.equal(description);
-
-    const options = await collection.getOptions();
-    expect(options.tokenPropertyPermissions).to.be.deep.equal([
-      {
-        key: 'URI',
-        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
-      },
-      {
-        key: 'URISuffix',
-        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
-      },
-    ]);
-  });
-
-  // Soft-deprecated
-  itEth('[eth] Set sponsorship', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const ss58Format = helper.chain.getChainProperties().ss58Format;
-    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
-
-    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-    await collection.methods.setCollectionSponsor(sponsor).send();
-
-    let data = (await helper.nft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
-
-    data = (await helper.nft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-  });
-
-  itEth('[cross] Set sponsorship & get description', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const ss58Format = helper.chain.getChainProperties().ss58Format;
-    const description = 'absolutely anything';
-    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', description, 'ROC');
-
-    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-    await collection.methods.setCollectionSponsorCross(sponsorCross).send();
-
-    let data = (await helper.nft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-    const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
-
-    data = (await helper.nft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-    expect(await sponsorCollection.methods.description().call()).to.deep.equal(description);
-  });
-
-  itEth('Collection address exist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await collectionHelpers
-      .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
-      .to.be.false;
-
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Exister', 'absolutely anything', 'EVC');
-    expect(await collectionHelpers
-      .methods.isCollectionExist(collectionAddress).call())
-      .to.be.true;
-
-    // check collectionOwner:
-    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
-    const collectionOwner = await collectionEvm.methods.collectionOwner().call();
-    expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
-  });
-});
-
-describe('(!negative tests!) Create NFT collection from EVM', () => {
-  let donor: IKeyringPair;
-  let nominal: bigint;
-
-  before(async function () {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-    {
-      const MAX_NAME_LENGTH = 64;
-      const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
-      const description = 'A';
-      const tokenPrefix = 'A';
-
-      await expect(collectionHelper.methods
-        .createNFTCollection(collectionName, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
-
-    }
-    {
-      const MAX_DESCRIPTION_LENGTH = 256;
-      const collectionName = 'A';
-      const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
-      const tokenPrefix = 'A';
-      await expect(collectionHelper.methods
-        .createNFTCollection(collectionName, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
-    }
-    {
-      const MAX_TOKEN_PREFIX_LENGTH = 16;
-      const collectionName = 'A';
-      const description = 'A';
-      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
-      await expect(collectionHelper.methods
-        .createNFTCollection(collectionName, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
-    }
-  });
-
-  itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-    await expect(collectionHelper.methods
-      .createNFTCollection('Peasantry', 'absolutely anything', 'CVE')
-      .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-  });
-
-  // Soft-deprecated
-  itEth('(!negative test!) [eth] Check owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const malfeasant = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
-    const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant, true);
-    const EXPECTED_ERROR = 'NoPermission';
-    {
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      await expect(malfeasantCollection.methods
-        .setCollectionSponsor(sponsor)
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-
-      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
-      await expect(sponsorCollection.methods
-        .confirmCollectionSponsorship()
-        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-    }
-    {
-      await expect(malfeasantCollection.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-    }
-  });
-
-  itEth('(!negative test!) [cross] Check owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const malfeasant = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
-    const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
-    const EXPECTED_ERROR = 'NoPermission';
-    {
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      await expect(malfeasantCollection.methods
-        .setCollectionSponsorCross(sponsorCross)
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-
-      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
-      await expect(sponsorCollection.methods
-        .confirmCollectionSponsorship()
-        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-    }
-    {
-      await expect(malfeasantCollection.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-    }
-  });
-
-  itEth('destroyCollection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-
-
-    const result = await collectionHelper.methods
-      .destroyCollection(collectionAddress)
-      .send({from: owner});
-
-    const events = helper.eth.normalizeEvents(result.events);
-
-    expect(events).to.be.deep.equal([
-      {
-        address: collectionHelper.options.address,
-        event: 'CollectionDestroyed',
-        args: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-
-    expect(await collectionHelper.methods
-      .isCollectionExist(collectionAddress)
-      .call()).to.be.false;
-    expect(await helper.collection.getData(collectionId)).to.be.null;
-  });
-});
deletedjs-packages/tests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/createRFTCollection.test.ts
+++ /dev/null
@@ -1,273 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {evmToAddress} from '@polkadot/util-crypto';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import {CollectionLimitField} from './util/playgrounds/types.js';
-
-
-describe('Create RFT collection from EVM', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Create collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const name = 'CollectionEVM';
-    const description = 'Some description';
-    const prefix = 'token prefix';
-
-    const {collectionId} = await helper.eth.createRFTCollection(owner, name, description, prefix);
-    const data = (await helper.rft.getData(collectionId))!;
-    const collection = helper.rft.getCollectionObject(collectionId);
-
-    expect(data.name).to.be.eq(name);
-    expect(data.description).to.be.eq(description);
-    expect(data.raw.tokenPrefix).to.be.eq(prefix);
-    expect(data.raw.mode).to.be.eq('ReFungible');
-
-    const options = await collection.getOptions();
-
-    expect(options.tokenPropertyPermissions).to.be.empty;
-  });
-
-
-
-  itEth('Create collection with properties & get description', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const name = 'CollectionEVM';
-    const description = 'Some description';
-    const prefix = 'token prefix';
-    const baseUri = 'BaseURI';
-
-    const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);
-    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    const collection = helper.rft.getCollectionObject(collectionId);
-    const data = (await collection.getData())!;
-
-    expect(data.name).to.be.eq(name);
-    expect(data.description).to.be.eq(description);
-    expect(data.raw.tokenPrefix).to.be.eq(prefix);
-    expect(data.raw.mode).to.be.eq('ReFungible');
-
-    expect(await contract.methods.description().call()).to.deep.equal(description);
-
-    const options = await collection.getOptions();
-    expect(options.tokenPropertyPermissions).to.be.deep.equal([
-      {
-        key: 'URI',
-        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
-      },
-      {
-        key: 'URISuffix',
-        permission: {mutable: true, collectionAdmin: true, tokenOwner: false},
-      },
-    ]);
-  });
-
-  // Soft-deprecated
-  itEth('[eth] Set sponsorship', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const ss58Format = helper.chain.getChainProperties().ss58Format;
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
-
-    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
-    await collection.methods.setCollectionSponsor(sponsor).send();
-
-    let data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
-
-    data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-  });
-
-  itEth('[cross] Set sponsorship', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const ss58Format = helper.chain.getChainProperties().ss58Format;
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
-
-    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-    await collection.methods.setCollectionSponsorCross(sponsorCross).send();
-
-    let data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-
-    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-
-    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
-
-    data = (await helper.rft.getData(collectionId))!;
-    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
-  });
-
-  itEth('Collection address exist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
-
-    expect(await collectionHelpers
-      .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
-      .to.be.false;
-
-    const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Exister', 'absolutely anything', 'WIWT');
-    expect(await collectionHelpers
-      .methods.isCollectionExist(collectionAddress).call())
-      .to.be.true;
-
-    // check collectionOwner:
-    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
-    const collectionOwner = await collectionEvm.methods.collectionOwner().call();
-    expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
-  });
-});
-
-describe('(!negative tests!) Create RFT collection from EVM', () => {
-  let donor: IKeyringPair;
-  let nominal: bigint;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      donor = await privateKey({url: import.meta.url});
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-    {
-      const MAX_NAME_LENGTH = 64;
-      const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
-      const description = 'A';
-      const tokenPrefix = 'A';
-
-      await expect(collectionHelper.methods
-        .createRFTCollection(collectionName, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
-    }
-    {
-      const MAX_DESCRIPTION_LENGTH = 256;
-      const collectionName = 'A';
-      const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
-      const tokenPrefix = 'A';
-      await expect(collectionHelper.methods
-        .createRFTCollection(collectionName, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
-    }
-    {
-      const MAX_TOKEN_PREFIX_LENGTH = 16;
-      const collectionName = 'A';
-      const description = 'A';
-      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
-      await expect(collectionHelper.methods
-        .createRFTCollection(collectionName, description, tokenPrefix)
-        .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
-    }
-  });
-
-  itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-    await expect(collectionHelper.methods
-      .createRFTCollection('Peasantry', 'absolutely anything', 'TWIW')
-      .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-  });
-
-  // Soft-deprecated
-  itEth('(!negative test!) [eth] Check owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const peasant = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
-    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true);
-    const EXPECTED_ERROR = 'NoPermission';
-    {
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      await expect(peasantCollection.methods
-        .setCollectionSponsor(sponsor)
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-
-      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
-      await expect(sponsorCollection.methods
-        .confirmCollectionSponsorship()
-        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-    }
-    {
-      await expect(peasantCollection.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-    }
-  });
-
-  itEth('(!negative test!) [cross] Check owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const peasant = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
-    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
-    const EXPECTED_ERROR = 'NoPermission';
-    {
-      const sponsor = await helper.eth.createAccountWithBalance(donor);
-      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-      await expect(peasantCollection.methods
-        .setCollectionSponsorCross(sponsorCross)
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-
-      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
-      await expect(sponsorCollection.methods
-        .confirmCollectionSponsorship()
-        .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
-    }
-    {
-      await expect(peasantCollection.methods
-        .setCollectionLimit({field: CollectionLimitField.AccountTokenOwnership, value: {status: true, value: 1000}})
-        .call()).to.be.rejectedWith(EXPECTED_ERROR);
-    }
-  });
-
-  itEth('destroyCollection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress, collectionId} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
-    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
-
-    await expect(collectionHelper.methods
-      .destroyCollection(collectionAddress)
-      .send({from: owner})).to.be.fulfilled;
-
-    expect(await collectionHelper.methods
-      .isCollectionExist(collectionAddress)
-      .call()).to.be.false;
-    expect(await helper.collection.getData(collectionId)).to.be.null;
-  });
-});
deletedjs-packages/tests/src/eth/crossTransfer.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/crossTransfer.test.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {itEth, usingEthPlaygrounds} from './util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/src/unique.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-describe('Token transfer between substrate address and EVM address. Fungible', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    });
-  });
-
-  itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => {
-    const bobCA = CrossAccountId.fromKeyring(bob).toICrossAccountId();
-    const charlieCA = CrossAccountId.fromKeyring(charlie).toICrossAccountId();
-    const charlieCAEth = CrossAccountId.fromKeyring(charlie).toEthereum().toICrossAccountId();
-
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-
-    await collection.mint(alice, 200n);
-    await collection.transfer(alice, charlieCAEth, 200n);
-    await collection.transferFrom(alice, charlieCAEth, charlieCA, 50n);
-    await collection.transfer(charlie, bobCA, 50n);
-  });
-
-  itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => {
-    const aliceProxy = await helper.eth.createAccountWithBalance(donor);
-    const bobProxy = await helper.eth.createAccountWithBalance(donor);
-
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'ft', aliceProxy);
-
-    await collection.mint(alice, 200n, {Ethereum: aliceProxy});
-    await contract.methods.transfer(bobProxy, 50).send({from: aliceProxy});
-    await collection.transferFrom(alice, {Ethereum: bobProxy}, CrossAccountId.fromKeyring(bob).toICrossAccountId(), 50n);
-    await collection.transfer(bob, CrossAccountId.fromKeyring(alice).toICrossAccountId(), 50n);
-  });
-});
-
-describe('Token transfer between substrate address and EVM address. NFT', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    });
-  });
-
-  itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => {
-    const charlieEth = CrossAccountId.fromKeyring(charlie, 'Ethereum').toICrossAccountId();
-
-    const collection = await helper.nft.mintCollection(alice);
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-    const token = await collection.mintToken(alice);
-    await token.transfer(alice, charlieEth);
-    await token.transferFrom(alice, charlieEth, CrossAccountId.fromKeyring(charlie).toICrossAccountId());
-    await token.transfer(charlie, CrossAccountId.fromKeyring(bob).toICrossAccountId());
-  });
-
-  itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => {
-    const aliceProxy = await helper.eth.createAccountWithBalance(donor);
-    const bobProxy = await helper.eth.createAccountWithBalance(donor);
-
-    const collection = await helper.nft.mintCollection(alice);
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', aliceProxy);
-
-    const token = await collection.mintToken(alice);
-    await token.transfer(alice, {Ethereum: aliceProxy});
-    await contract.methods.transfer(bobProxy, 1).send({from: aliceProxy});
-    await token.transferFrom(alice, {Ethereum: bobProxy}, {Substrate: bob.address});
-    await token.transfer(bob, {Substrate: charlie.address});
-  });
-});
deletedjs-packages/tests/src/eth/destroyCollection.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/destroyCollection.test.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../util/index.js';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import type {TCollectionMode} from '@unique/playgrounds/src/types.js';
-import {CreateCollectionData} from './util/playgrounds/types.js';
-
-describe('Destroy Collection from EVM', function() {
-  let donor: IKeyringPair;
-  const testCases = [
-    {case: 'rft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'rft'], requiredPallets: [Pallets.ReFungible]},
-    {case: 'nft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'nft'], requiredPallets: [Pallets.NFT]},
-    {case: 'ft' as const, params: ['Limits', 'absolutely anything', 'OLF', 'ft', 18], requiredPallets: [Pallets.Fungible]},
-  ];
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  testCases.map((testCase) =>
-    itEth.ifWithPallets(`Cannot burn non-owned or non-existing collection ${testCase.case}`, testCase.requiredPallets, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const signer = await helper.eth.createAccountWithBalance(donor);
-
-      const unexistedCollection = helper.ethAddress.fromCollectionId(1000000);
-
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(signer);
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData(...testCase.params as [string, string, string, TCollectionMode, number?])).send();
-      // cannot burn collec
-      await expect(collectionHelpers.methods
-        .destroyCollection(collectionAddress)
-        .send({from: signer})).to.be.rejected;
-
-      await expect(collectionHelpers.methods
-        .destroyCollection(unexistedCollection)
-        .send({from: signer})).to.be.rejected;
-
-      expect(await collectionHelpers.methods
-        .isCollectionExist(unexistedCollection)
-        .call()).to.be.false;
-
-      expect(await collectionHelpers.methods
-        .isCollectionExist(collectionAddress)
-        .call()).to.be.true;
-    }));
-});
deletedjs-packages/tests/src/eth/ethFeesAreCorrect.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/ethFeesAreCorrect.test.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://witww.gnu.org/licenses/>.
-
-import type {IKeyringPair} from '@polkadot/types/types';
-import {itEth, usingEthPlaygrounds, expect} from './util/index.js';
-
-describe('Eth fees are correct', () => {
-  let donor: IKeyringPair;
-  let minter: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [minter, alice] = await helper.arrange.createAccounts([100n, 200n], donor);
-    });
-  });
-
-
-  itEth('web3 fees are the same as evm.call fees', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const aliceEth = helper.address.substrateToEth(alice.address);
-
-    const {tokenId: tokenA} = await collection.mintToken(minter, {Ethereum: owner});
-    const {tokenId: tokenB} = await collection.mintToken(minter, {Ethereum: aliceEth});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    const balanceBeforeWeb3Transfer = await helper.balance.getEthereum(owner);
-    await contract.methods.transfer(receiver, tokenA).send({from: owner, maxFeePerGas: ((await helper.eth.getGasPrice())!).toString()});
-    const balanceAfterWeb3Transfer = await helper.balance.getEthereum(owner);
-    const web3Diff = balanceBeforeWeb3Transfer - balanceAfterWeb3Transfer;
-
-    const encodedCall = contract.methods.transfer(receiver, tokenB)
-      .encodeABI();
-
-    const balanceBeforeEvmCall = await helper.balance.getSubstrate(alice.address);
-    await helper.eth.sendEVM(
-      alice,
-      collectionAddress,
-      encodedCall,
-      '0',
-    );
-    const balanceAfterEvmCall = await helper.balance.getSubstrate(alice.address);
-    const evmCallDiff = balanceBeforeEvmCall - balanceAfterEvmCall;
-
-    expect(web3Diff).to.be.equal(evmCallDiff);
-  });
-});
deletedjs-packages/tests/src/eth/events.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/events.test.ts
+++ /dev/null
@@ -1,548 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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} from 'chai';
-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/src/types.js';
-import {Pallets, requirePalletsOrSkip} from '../util/index.js';
-import {CollectionLimitField, TokenPermissionField, CreateCollectionData} from './util/playgrounds/types.js';
-import type {NormalizedEvent} from './util/playgrounds/types.js';
-
-let donor: IKeyringPair;
-
-before(async function () {
-  await usingEthPlaygrounds(async (_helper, privateKey) => {
-    donor = await privateKey({url: import.meta.url});
-  });
-});
-
-function clearEvents(ethEvents: NormalizedEvent[] | null, subEvents: IEvent[]) {
-  if(ethEvents !== null) {
-    ethEvents.splice(0);
-  }
-  subEvents.splice(0);
-}
-
-async function testCollectionCreatedAndDestroy(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionCreated', 'CollectionDestroyed']}]);
-  const {collectionAddress, events: ethEvents} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-
-  await helper.wait.newBlocks(1);
-  {
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionCreated',
-        args: {
-          owner: owner,
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionCreated'}]);
-    clearEvents(ethEvents, subEvents);
-  }
-  {
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-    const result = await collectionHelper.methods.destroyCollection(collectionAddress).send({from:owner});
-    await helper.wait.newBlocks(1);
-    expect(result.events).to.containSubset({
-      CollectionDestroyed: {
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    });
-    expect(subEvents).to.containSubset([{method: 'CollectionDestroyed'}]);
-  }
-  unsubscribe();
-}
-
-async function testCollectionPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-
-  const ethEvents: any = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPropertySet', 'CollectionPropertyDeleted']}]);
-  {
-    await collection.methods.setCollectionProperties([{key: 'A', value: [0,1,2,3]}]).send({from:owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionPropertySet'}]);
-    clearEvents(ethEvents, subEvents);
-  }
-  {
-    await collection.methods.deleteCollectionProperties(['A']).send({from:owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionPropertyDeleted'}]);
-  }
-  unsubscribe();
-}
-
-async function testPropertyPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-  const ethEvents: any = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['PropertyPermissionSet']}]);
-  await collection.methods.setTokenPropertyPermissions([
-    ['A', [
-      [TokenPermissionField.Mutable, true],
-      [TokenPermissionField.TokenOwner, true],
-      [TokenPermissionField.CollectionAdmin, true]],
-    ],
-  ]).send({from: owner});
-  await helper.wait.newBlocks(1);
-  expect(ethEvents).to.containSubset([
-    {
-      event: 'CollectionChanged',
-      returnValues: {
-        collectionId: collectionAddress,
-      },
-    },
-  ]);
-  expect(subEvents).to.containSubset([{method: 'PropertyPermissionSet'}]);
-  unsubscribe();
-}
-
-async function testAllowListAddressAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const user = helper.ethCrossAccount.createAccount();
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-  const ethEvents: any[] = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['AllowListAddressAdded', 'AllowListAddressRemoved']}]);
-  {
-    await collection.methods.addToCollectionAllowListCross(user).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'AllowListAddressAdded'}]);
-    clearEvents(ethEvents, subEvents);
-  }
-  {
-    await collection.methods.removeFromCollectionAllowListCross(user).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'AllowListAddressRemoved'}]);
-  }
-  unsubscribe();
-}
-
-async function testCollectionAdminAddedAndRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const user = helper.ethCrossAccount.createAccount();
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-  const ethEvents: any = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionAdminAdded', 'CollectionAdminRemoved']}]);
-  {
-    await collection.methods.addCollectionAdminCross(user).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionAdminAdded'}]);
-    clearEvents(ethEvents, subEvents);
-  }
-  {
-    await collection.methods.removeCollectionAdminCross(user).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionAdminRemoved'}]);
-  }
-  unsubscribe();
-}
-
-async function testCollectionLimitSet(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-  const ethEvents: any = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionLimitSet']}]);
-  {
-    await collection.methods.setCollectionLimit({field: CollectionLimitField.OwnerCanTransfer, value: {status: true, value: 0}}).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionLimitSet'}]);
-  }
-  unsubscribe();
-}
-
-async function testCollectionOwnerChanged(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const newOwner = helper.ethCrossAccount.createAccount();
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-  const ethEvents: any = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionOwnerChanged']}]);
-  {
-    await collection.methods.changeCollectionOwnerCross(newOwner).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionOwnerChanged'}]);
-  }
-  unsubscribe();
-}
-
-async function testCollectionPermissionSet(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-  const ethEvents: any = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['CollectionPermissionSet']}]);
-  {
-    await collection.methods.setCollectionMintMode(true).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
-    clearEvents(ethEvents, subEvents);
-  }
-  {
-    await collection.methods.setCollectionAccess(1).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionPermissionSet'}]);
-  }
-  unsubscribe();
-}
-
-async function testCollectionSponsorSetAndConfirmedAndThenRemoved(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const sponsor = await helper.ethCrossAccount.createAccountWithBalance(donor);
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
-  const ethEvents: any = [];
-  collectionHelper.events.allEvents((_: any, event: any) => {
-    ethEvents.push(event);
-  });
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{
-    section: 'common', names: ['CollectionSponsorSet', 'SponsorshipConfirmed', 'CollectionSponsorRemoved',
-    ]}]);
-  {
-    await collection.methods.setCollectionSponsorCross(sponsor).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([{
-      event: 'CollectionChanged',
-      returnValues: {
-        collectionId: collectionAddress,
-      },
-    }]);
-    expect(subEvents).to.containSubset([{method: 'CollectionSponsorSet'}]);
-    clearEvents(ethEvents, subEvents);
-  }
-  {
-    await collection.methods.confirmCollectionSponsorship().send({from: sponsor.eth});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'SponsorshipConfirmed'}]);
-    clearEvents(ethEvents, subEvents);
-  }
-  {
-    await collection.methods.removeCollectionSponsor().send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(ethEvents).to.containSubset([
-      {
-        event: 'CollectionChanged',
-        returnValues: {
-          collectionId: collectionAddress,
-        },
-      },
-    ]);
-    expect(subEvents).to.containSubset([{method: 'CollectionSponsorRemoved'}]);
-  }
-  unsubscribe();
-}
-
-async function testTokenPropertySetAndDeleted(helper: EthUniqueHelper, mode: TCollectionMode) {
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', mode, 18)).send();
-  const collection = await helper.ethNativeContract.collection(collectionAddress, mode, owner);
-  const result = await collection.methods.mint(owner).send({from: owner});
-  const tokenId = result.events.Transfer.returnValues.tokenId;
-  await collection.methods.setTokenPropertyPermissions([
-    ['A', [
-      [TokenPermissionField.Mutable, true],
-      [TokenPermissionField.TokenOwner, true],
-      [TokenPermissionField.CollectionAdmin, true]],
-    ],
-  ]).send({from: owner});
-
-  const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['TokenPropertySet', 'TokenPropertyDeleted']}]);
-  {
-    const result = await collection.methods.setProperties(tokenId, [{key: 'A', value: [1,2,3]}]).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(result.events.TokenChanged).to.be.like({
-      event: 'TokenChanged',
-      returnValues: {
-        tokenId: tokenId,
-      },
-    });
-    expect(subEvents).to.containSubset([{method: 'TokenPropertySet'}]);
-    clearEvents(null, subEvents);
-  }
-  {
-    const result = await collection.methods.deleteProperties(tokenId, ['A']).send({from: owner});
-    await helper.wait.newBlocks(1);
-    expect(result.events.TokenChanged).to.be.like({
-      event: 'TokenChanged',
-      returnValues: {
-        tokenId: tokenId,
-      },
-    });
-    expect(subEvents).to.containSubset([{method: 'TokenPropertyDeleted'}]);
-  }
-  unsubscribe();
-}
-
-describe('[FT] Sync sub & eth events', () => {
-  const mode: TCollectionMode = 'ft';
-
-  itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => {
-    await testCollectionCreatedAndDestroy(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => {
-    await testCollectionPropertySetAndDeleted(helper, mode);
-  });
-
-  itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => {
-    await testAllowListAddressAddedAndRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => {
-    await testCollectionAdminAddedAndRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => {
-    await testCollectionLimitSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => {
-    await testCollectionOwnerChanged(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => {
-    await testCollectionPermissionSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => {
-    await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode);
-  });
-});
-
-describe('[NFT] Sync sub & eth events', () => {
-  const mode: TCollectionMode = 'nft';
-
-  itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => {
-    await testCollectionCreatedAndDestroy(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => {
-    await testCollectionPropertySetAndDeleted(helper, mode);
-  });
-
-  itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => {
-    await testPropertyPermissionSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => {
-    await testAllowListAddressAddedAndRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => {
-    await testCollectionAdminAddedAndRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => {
-    await testCollectionLimitSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => {
-    await testCollectionOwnerChanged(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => {
-    await testCollectionPermissionSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => {
-    await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => {
-    await testTokenPropertySetAndDeleted(helper, mode);
-  });
-});
-
-describe('[RFT] Sync sub & eth events', () => {
-  const mode: TCollectionMode = 'rft';
-
-  before(async function() {
-    await usingEthPlaygrounds((helper) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      return Promise.resolve();
-    });
-  });
-
-  itEth('CollectionCreated and CollectionDestroyed events', async ({helper}) => {
-    await testCollectionCreatedAndDestroy(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionPropertySet and CollectionPropertyDeleted', async ({helper}) => {
-    await testCollectionPropertySetAndDeleted(helper, mode);
-  });
-
-  itEth('CollectionChanged event for PropertyPermissionSet', async ({helper}) => {
-    await testPropertyPermissionSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for AllowListAddressAdded, AllowListAddressRemoved', async ({helper}) => {
-    await testAllowListAddressAddedAndRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionAdminAdded, CollectionAdminRemoved', async ({helper}) => {
-    await testCollectionAdminAddedAndRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionLimitSet', async ({helper}) => {
-    await testCollectionLimitSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionOwnerChanged', async ({helper}) => {
-    await testCollectionOwnerChanged(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionPermissionSet', async ({helper}) => {
-    await testCollectionPermissionSet(helper, mode);
-  });
-
-  itEth('CollectionChanged event for CollectionSponsorSet, SponsorshipConfirmed, CollectionSponsorRemoved', async ({helper}) => {
-    await testCollectionSponsorSetAndConfirmedAndThenRemoved(helper, mode);
-  });
-
-  itEth('CollectionChanged event for TokenPropertySet, TokenPropertyDeleted', async ({helper}) => {
-    await testTokenPropertySetAndDeleted(helper, mode);
-  });
-});
deletedjs-packages/tests/src/eth/evmCoder.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/evmCoder.test.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itEth, expect, usingEthPlaygrounds} from './util/index.js';
-
-const getContractSource = (collectionAddress: string, contractAddress: string): string => `
-  // SPDX-License-Identifier: MIT
-  pragma solidity ^0.8.0;
-  interface ITest {
-    function ztestzzzzzzz() external returns (uint256 n);
-  }
-  contract Test {
-    event Result(bool, uint256);
-    function test1() public {
-        try
-            ITest(${collectionAddress}).ztestzzzzzzz()
-        returns (uint256 n) {
-            // enters
-            emit Result(true, n); // => [true, BigNumber { value: "43648854190028290368124427828690944273759144372138548774646036134290060795932" }]
-        } catch {
-            emit Result(false, 0);
-        }
-    }
-    function test2() public {
-        try
-            ITest(${contractAddress}).ztestzzzzzzz()
-        returns (uint256 n) {
-            emit Result(true, n);
-        } catch {
-            // enters
-            emit Result(false, 0); // => [ false, BigNumber { value: "0" } ]
-        }
-    }
-    function test3() public {
-      ITest(${collectionAddress}).ztestzzzzzzz();
-    }
-  }
-  `;
-
-
-describe('Evm Coder tests', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Call non-existing function', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.eth.createNFTCollection(owner, 'EVMCODER', '', 'TEST');
-    const contract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, '0x1bfed5D614b886b9Ab2eA4CBAc22A96B7EC29c9c'));
-    const testContract = await helper.ethContract.deployByCode(owner, 'Test', getContractSource(collection.collectionAddress, contract.options.address));
-    {
-      const result = await testContract.methods.test1().send();
-      expect(result.events.Result.returnValues).to.deep.equal({
-        '0': false,
-        '1': '0',
-      });
-    }
-    {
-      const result = await testContract.methods.test2().send();
-      expect(result.events.Result.returnValues).to.deep.equal({
-        '0': false,
-        '1': '0',
-      });
-    }
-    {
-      await expect(testContract.methods.test3().call())
-        .to.be.rejectedWith(/unrecognized selector: 0xd9f02b36$/g);
-    }
-  });
-});
deletedjs-packages/tests/src/eth/fractionalizer/Fractionalizer.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/fractionalizer/Fractionalizer.sol
+++ /dev/null
@@ -1,172 +0,0 @@
-// SPDX-License-Identifier:  Apache License
-pragma solidity >=0.8.0;
-import {CollectionHelpers} from "../api/CollectionHelpers.sol";
-import {ContractHelpers} from "../api/ContractHelpers.sol";
-import {UniqueRefungibleToken} from "../api/UniqueRefungibleToken.sol";
-import {UniqueRefungible, CrossAddress} from "../api/UniqueRefungible.sol";
-import {UniqueNFT} from "../api/UniqueNFT.sol";
-
-/// @dev Fractionalization contract. It stores mappings between NFT and RFT tokens,
-///  stores allowlist of NFT tokens available for fractionalization, has methods
-///  for fractionalization and defractionalization of NFT tokens.
-contract Fractionalizer {
-	struct Token {
-		address _collection;
-		uint256 _tokenId;
-	}
-	address rftCollection;
-	mapping(address => bool) nftCollectionAllowList;
-	mapping(address => mapping(uint256 => uint256)) public nft2rftMapping;
-	mapping(address => Token) public rft2nftMapping;
-	//use constant to reduce gas cost
-	bytes32 constant refungibleCollectionType = keccak256(bytes("ReFungible"));
-
-	receive() external payable onlyOwner {}
-
-	/// @dev Method modifier to only allow contract owner to call it.
-	modifier onlyOwner() {
-		address contracthelpersAddress = 0x842899ECF380553E8a4de75bF534cdf6fBF64049;
-		ContractHelpers contractHelpers = ContractHelpers(contracthelpersAddress);
-		address contractOwner = contractHelpers.contractOwner(address(this));
-		require(msg.sender == contractOwner, "Only owner can");
-		_;
-	}
-
-	/// @dev This emits when RFT collection setting is changed.
-	event RFTCollectionSet(address _collection);
-
-	/// @dev This emits when NFT collection is allowed or disallowed.
-	event AllowListSet(address _collection, bool _status);
-
-	/// @dev This emits when NFT token is fractionalized by contract.
-	event Fractionalized(address _collection, uint256 _tokenId, address _rftToken, uint128 _amount);
-
-	/// @dev This emits when NFT token is defractionalized by contract.
-	event Defractionalized(address _rftToken, address _nftCollection, uint256 _nftTokenId);
-
-	/// Set RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens
-	/// would be created in this collection.
-	/// @dev Throws if RFT collection is already configured for this contract.
-	///  Throws if collection of wrong type (NFT, Fungible) is provided instead
-	///  of RFT collection.
-	///  Throws if `msg.sender` is not owner or admin of provided RFT collection.
-	///  Can only be called by contract owner.
-	/// @param _collection address of RFT collection.
-	function setRFTCollection(address _collection) external onlyOwner {
-		require(rftCollection == address(0), "RFT collection is already set");
-		UniqueRefungible refungibleContract = UniqueRefungible(_collection);
-		string memory collectionType = refungibleContract.uniqueCollectionType();
-
-		// compare hashed to reduce gas cost
-		require(
-			keccak256(bytes(collectionType)) == refungibleCollectionType,
-			"Wrong collection type. Collection is not refungible."
-		);
-		require(
-			refungibleContract.isOwnerOrAdminCross(CrossAddress({eth: address(this), sub: uint256(0)})),
-			"Fractionalizer contract should be an admin of the collection"
-		);
-		rftCollection = _collection;
-		emit RFTCollectionSet(rftCollection);
-	}
-
-	/// Creates and sets RFT collection that contract will work with. RFT tokens for fractionalized NFT tokens
-	/// would be created in this collection.
-	/// @dev Throws if RFT collection is already configured for this contract.
-	///  Can only be called by contract owner.
-	/// @param _name name for created RFT collection.
-	/// @param _description description for created RFT collection.
-	/// @param _tokenPrefix token prefix for created RFT collection.
-	function createAndSetRFTCollection(
-		string calldata _name,
-		string calldata _description,
-		string calldata _tokenPrefix
-	) external payable onlyOwner {
-		require(rftCollection == address(0), "RFT collection is already set");
-		address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
-		rftCollection = CollectionHelpers(collectionHelpers).createRFTCollection{value: msg.value}(
-			_name,
-			_description,
-			_tokenPrefix
-		);
-		emit RFTCollectionSet(rftCollection);
-	}
-
-	/// Allow or disallow NFT collection tokens from being fractionalized by this contract.
-	/// @dev Can only be called by contract owner.
-	/// @param collection NFT token address.
-	/// @param status `true` to allow and `false` to disallow NFT token.
-	function setNftCollectionIsAllowed(address collection, bool status) external onlyOwner {
-		nftCollectionAllowList[collection] = status;
-		emit AllowListSet(collection, status);
-	}
-
-	/// Fractionilize NFT token.
-	/// @dev Takes NFT token from `msg.sender` and transfers RFT token to `msg.sender`
-	///  instead. Creates new RFT token if provided NFT token never was fractionalized
-	///  by this contract or existing RFT token if it was.
-	///  Throws if RFT collection isn't configured for this contract.
-	///  Throws if fractionalization of provided NFT token is not allowed
-	///  Throws if `msg.sender` is not owner of provided NFT token
-	/// @param  _collection NFT collection address
-	/// @param  _token id of NFT token to be fractionalized
-	/// @param  _pieces number of pieces new RFT token would have
-	function nft2rft(
-		address _collection,
-		uint256 _token,
-		uint128 _pieces
-	) external {
-		require(rftCollection != address(0), "RFT collection is not set");
-		UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection);
-		require(
-			nftCollectionAllowList[_collection] == true,
-			"Fractionalization of this collection is not allowed by admin"
-		);
-		require(UniqueNFT(_collection).ownerOf(_token) == msg.sender, "Only token owner could fractionalize it");
-		UniqueNFT(_collection).transferFrom(msg.sender, address(this), _token);
-		uint256 rftTokenId;
-		address rftTokenAddress;
-		UniqueRefungibleToken rftTokenContract;
-		if (nft2rftMapping[_collection][_token] == 0) {
-			rftTokenId = rftCollectionContract.mint(address(this));
-			rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId);
-			nft2rftMapping[_collection][_token] = rftTokenId;
-			rft2nftMapping[rftTokenAddress] = Token(_collection, _token);
-
-			rftTokenContract = UniqueRefungibleToken(rftTokenAddress);
-		} else {
-			rftTokenId = nft2rftMapping[_collection][_token];
-			rftTokenAddress = rftCollectionContract.tokenContractAddress(rftTokenId);
-			rftTokenContract = UniqueRefungibleToken(rftTokenAddress);
-		}
-		rftTokenContract.repartition(_pieces);
-		rftTokenContract.transfer(msg.sender, _pieces);
-		emit Fractionalized(_collection, _token, rftTokenAddress, _pieces);
-	}
-
-	/// Defrationalize NFT token.
-	/// @dev Takes RFT token from `msg.sender` and transfers corresponding NFT token
-	///  to `msg.sender` instead.
-	///  Throws if RFT collection isn't configured for this contract.
-	///  Throws if provided RFT token is no from configured RFT collection.
-	///  Throws if RFT token was not created by this contract.
-	///  Throws if `msg.sender` isn't owner of all RFT token pieces.
-	/// @param _collection RFT collection address
-	/// @param _token id of RFT token
-	function rft2nft(address _collection, uint256 _token) external {
-		require(rftCollection != address(0), "RFT collection is not set");
-		require(rftCollection == _collection, "Wrong RFT collection");
-		UniqueRefungible rftCollectionContract = UniqueRefungible(rftCollection);
-		address rftTokenAddress = rftCollectionContract.tokenContractAddress(_token);
-		Token memory nftToken = rft2nftMapping[rftTokenAddress];
-		require(nftToken._collection != address(0), "No corresponding NFT token found");
-		UniqueRefungibleToken rftTokenContract = UniqueRefungibleToken(rftTokenAddress);
-		require(
-			rftTokenContract.balanceOf(msg.sender) == rftTokenContract.totalSupply(),
-			"Not all pieces are owned by the caller"
-		);
-		rftCollectionContract.transferFrom(msg.sender, address(this), _token);
-		UniqueNFT(nftToken._collection).transferFrom(address(this), msg.sender, nftToken._tokenId);
-		emit Defractionalized(rftTokenAddress, nftToken._collection, nftToken._tokenId);
-	}
-}
deletedjs-packages/tests/src/eth/fractionalizer/fractionalizer.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/fractionalizer/fractionalizer.test.ts
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {readFile} from 'fs/promises';
-
-import type {IKeyringPair} from '@polkadot/types/types';
-import {evmToAddress} from '@polkadot/util-crypto';
-
-import {Contract} from 'web3-eth-contract';
-
-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';
-
-const {dirname} = makeNames(import.meta.url);
-
-let compiledFractionalizer: CompiledContract;
-
-const compileContract = async (helper: EthUniqueHelper): Promise<CompiledContract> => {
-  if(!compiledFractionalizer) {
-    compiledFractionalizer = await helper.ethContract.compile('Fractionalizer', (await readFile(`${dirname}/Fractionalizer.sol`)).toString(), [
-      {solPath: 'api/CollectionHelpers.sol', fsPath: `${dirname}/../api/CollectionHelpers.sol`},
-      {solPath: 'api/ContractHelpers.sol', fsPath: `${dirname}/../api/ContractHelpers.sol`},
-      {solPath: 'api/UniqueRefungibleToken.sol', fsPath: `${dirname}/../api/UniqueRefungibleToken.sol`},
-      {solPath: 'api/UniqueRefungible.sol', fsPath: `${dirname}/../api/UniqueRefungible.sol`},
-      {solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`},
-    ]);
-  }
-  return compiledFractionalizer;
-};
-
-
-const deployContract = async (helper: EthUniqueHelper, owner: string): Promise<Contract> => {
-  const compiled = await compileContract(helper);
-  return await helper.ethContract.deployByAbi(owner, compiled.abi, compiled.object);
-};
-
-
-const initContract = async (helper: EthUniqueHelper, owner: string): Promise<{contract: Contract, rftCollectionAddress: string}> => {
-  const fractionalizer = await deployContract(helper, owner);
-  const amount = 10n * helper.balance.getOneTokenNominal();
-  const web3 = helper.getWeb3();
-  await web3.eth.sendTransaction({from: owner, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS});
-  const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({value: Number(2n * helper.balance.getOneTokenNominal())});
-  const rftCollectionAddress = result.events.RFTCollectionSet.returnValues._collection;
-  return {contract: fractionalizer, rftCollectionAddress};
-};
-
-const mintRFTToken = async (helper: EthUniqueHelper, owner: string, fractionalizer: Contract, amount: bigint): Promise<{
-  nftCollectionAddress: string, nftTokenId: number, rftTokenAddress: string
-}> => {
-  const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-  const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-  const mintResult = await nftContract.methods.mint(owner).send({from: owner});
-  const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-  await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
-  await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
-  const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, amount).send({from: owner});
-  const {_collection, _tokenId, _rftToken} = result.events.Fractionalized.returnValues;
-  return {
-    nftCollectionAddress: _collection,
-    nftTokenId: _tokenId,
-    rftTokenAddress: _rftToken,
-  };
-};
-
-
-describe('Fractionalizer contract usage', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Set RFT collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const fractionalizer = await deployContract(helper, owner);
-    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
-    const rftContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
-
-    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
-    await rftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
-    const result = await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});
-    expect(result.events).to.be.like({
-      RFTCollectionSet: {
-        returnValues: {
-          _collection: rftCollection.collectionAddress,
-        },
-      },
-    });
-  });
-
-  itEth('Mint RFT collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const fractionalizer = await deployContract(helper, owner);
-    await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal());
-
-    const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())});
-    expect(result.events).to.be.like({
-      RFTCollectionSet: {},
-    });
-    expect(result.events.RFTCollectionSet.returnValues._collection).to.be.ok;
-  });
-
-  itEth('Set Allowlist', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {contract: fractionalizer} = await initContract(helper, owner);
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-
-    const result1 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
-    expect(result1.events).to.be.like({
-      AllowListSet: {
-        returnValues: {
-          _collection: nftCollection.collectionAddress,
-          _status: true,
-        },
-      },
-    });
-    const result2 = await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, false).send({from: owner});
-    expect(result2.events).to.be.like({
-      AllowListSet: {
-        returnValues: {
-          _collection: nftCollection.collectionAddress,
-          _status: false,
-        },
-      },
-    });
-  });
-
-  itEth('NFT to RFT', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-    const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
-    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    const {contract: fractionalizer} = await initContract(helper, owner);
-
-    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
-    await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
-    const result = await fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).send({from: owner});
-    expect(result.events).to.be.like({
-      Fractionalized: {
-        returnValues: {
-          _collection: nftCollection.collectionAddress,
-          _tokenId: nftTokenId,
-          _amount: '100',
-        },
-      },
-    });
-    const rftTokenAddress = result.events.Fractionalized.returnValues._rftToken;
-
-    // FIXME: should work without the caller
-    const rftTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress);
-    expect(await rftTokenContract.methods.balanceOf(owner).call()).to.equal('100');
-  });
-
-  itEth('RFT to NFT', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner);
-    const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n);
-
-    const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress);
-    const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId);
-    expect(rftCollectionAddress).to.be.equal(refungibleAddress);
-    const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner);
-    await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner});
-    const result = await fractionalizer.methods.rft2nft(refungibleAddress, tokenId).send({from: owner});
-    expect(result.events).to.be.like({
-      Defractionalized: {
-        returnValues: {
-          _rftToken: rftTokenAddress,
-          _nftCollection: nftCollectionAddress,
-          _nftTokenId: nftTokenId,
-        },
-      },
-    });
-  });
-
-  itEth('Test fractionalizer NFT <-> RFT mapping ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner);
-    const {rftTokenAddress, nftCollectionAddress, nftTokenId} = await mintRFTToken(helper, owner, fractionalizer, 100n);
-
-    const {collectionId, tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress);
-    const refungibleAddress = helper.ethAddress.fromCollectionId(collectionId);
-    expect(rftCollectionAddress).to.be.equal(refungibleAddress);
-    const refungibleTokenContract = await helper.ethNativeContract.rftToken(rftTokenAddress, owner);
-    await refungibleTokenContract.methods.approve(fractionalizer.options.address, 100).send({from: owner});
-
-    const rft2nft = await fractionalizer.methods.rft2nftMapping(rftTokenAddress).call();
-    expect(rft2nft).to.be.like({
-      _collection: nftCollectionAddress,
-      _tokenId: nftTokenId,
-    });
-
-    const nft2rft = await fractionalizer.methods.nft2rftMapping(nftCollectionAddress, nftTokenId).call();
-    expect(nft2rft).to.be.eq(tokenId.toString());
-  });
-});
-
-
-
-describe('Negative Integration Tests for fractionalizer', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper: EthUniqueHelper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('call setRFTCollection twice', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
-    const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
-
-    const fractionalizer = await deployContract(helper, owner);
-    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
-    await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
-    await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});
-
-    await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call())
-      .to.be.rejectedWith(/RFT collection is already set$/g);
-  });
-
-  itEth('call setRFTCollection with NFT collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-
-    const fractionalizer = await deployContract(helper, owner);
-    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
-    await nftContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
-
-    await expect(fractionalizer.methods.setRFTCollection(nftCollection.collectionAddress).call())
-      .to.be.rejectedWith(/Wrong collection type. Collection is not refungible.$/g);
-  });
-
-  itEth('call setRFTCollection while not collection admin', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const fractionalizer = await deployContract(helper, owner);
-    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
-
-    await expect(fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).call())
-      .to.be.rejectedWith(/Fractionalizer contract should be an admin of the collection$/g);
-  });
-
-  itEth('call setRFTCollection after createAndSetRFTCollection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const fractionalizer = await deployContract(helper, owner);
-    await helper.balance.transferToSubstrate(donor, evmToAddress(fractionalizer.options.address), 10n * helper.balance.getOneTokenNominal());
-
-    const result = await fractionalizer.methods.createAndSetRFTCollection('A', 'B', 'C').send({from: owner, value: Number(2n * helper.balance.getOneTokenNominal())});
-    const collectionIdAddress = result.events.RFTCollectionSet.returnValues._collection;
-
-    await expect(fractionalizer.methods.setRFTCollection(collectionIdAddress).call())
-      .to.be.rejectedWith(/RFT collection is already set$/g);
-  });
-
-  itEth('call nft2rft without setting RFT collection for contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
-    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    const fractionalizer = await deployContract(helper, owner);
-
-    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call())
-      .to.be.rejectedWith(/RFT collection is not set$/g);
-  });
-
-  itEth('call nft2rft while not owner of NFT token', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const nftOwner = await helper.eth.createAccountWithBalance(donor);
-
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
-    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-    await nftContract.methods.transfer(nftOwner, 1).send({from: owner});
-
-
-    const {contract: fractionalizer} = await initContract(helper, owner);
-    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
-
-    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call({from: owner}))
-      .to.be.rejectedWith(/Only token owner could fractionalize it$/g);
-  });
-
-  itEth('call nft2rft while not in list of allowed accounts', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-    const nftContract = helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
-    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    const {contract: fractionalizer} = await initContract(helper, owner);
-
-    await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
-    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call())
-      .to.be.rejectedWith(/Fractionalization of this collection is not allowed by admin$/g);
-  });
-
-  itEth('call nft2rft while fractionalizer doesnt have approval for nft token', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-    const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
-    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    const {contract: fractionalizer} = await initContract(helper, owner);
-
-    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
-    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100).call())
-      .to.be.rejectedWith(/ApprovedValueTooLow$/g);
-  });
-
-  itEth('call rft2nft without setting RFT collection for contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const fractionalizer = await deployContract(helper, owner);
-    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
-    const refungibleContract = await helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
-    const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});
-    const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call({from: owner}))
-      .to.be.rejectedWith(/RFT collection is not set$/g);
-  });
-
-  itEth('call rft2nft for RFT token that is not from configured RFT collection', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const {contract: fractionalizer} = await initContract(helper, owner);
-    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
-    const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
-    const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});
-    const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())
-      .to.be.rejectedWith(/Wrong RFT collection$/g);
-  });
-
-  itEth('call rft2nft for RFT token that was not minted by fractionalizer contract', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const rftCollection = await helper.eth.createRFTCollection(owner, 'rft', 'RFT collection', 'RFT');
-    const refungibleContract = helper.ethNativeContract.collection(rftCollection.collectionAddress, 'rft', owner);
-
-    const fractionalizer = await deployContract(helper, owner);
-
-    const fractionalizerAddressCross = helper.ethCrossAccount.fromAddress(fractionalizer.options.address);
-    await refungibleContract.methods.addCollectionAdminCross(fractionalizerAddressCross).send({from: owner});
-    await fractionalizer.methods.setRFTCollection(rftCollection.collectionAddress).send({from: owner});
-
-    const mintResult = await refungibleContract.methods.mint(owner).send({from: owner});
-    const rftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    await expect(fractionalizer.methods.rft2nft(rftCollection.collectionAddress, rftTokenId).call())
-      .to.be.rejectedWith(/No corresponding NFT token found$/g);
-  });
-
-  itEth('call rft2nft without owning all RFT pieces', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-
-    const {contract: fractionalizer, rftCollectionAddress} = await initContract(helper, owner);
-    const {rftTokenAddress} = await mintRFTToken(helper, owner, fractionalizer, 100n);
-
-    const {tokenId} = helper.ethAddress.extractTokenId(rftTokenAddress);
-    const refungibleTokenContract = helper.ethNativeContract.rftToken(rftTokenAddress, owner);
-    await refungibleTokenContract.methods.transfer(receiver, 50).send({from: owner});
-    await refungibleTokenContract.methods.approve(fractionalizer.options.address, 50).send({from: receiver});
-    await expect(fractionalizer.methods.rft2nft(rftCollectionAddress, tokenId).call({from: receiver}))
-      .to.be.rejectedWith(/Not all pieces are owned by the caller$/g);
-  });
-
-  itEth('send QTZ/UNQ to contract from non owner', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const payer = await helper.eth.createAccountWithBalance(donor);
-
-    const fractionalizer = await deployContract(helper, owner);
-    const amount = 10n * helper.balance.getOneTokenNominal();
-    const web3 = helper.getWeb3();
-    await expect(web3.eth.sendTransaction({from: payer, to: fractionalizer.options.address, value: `${amount}`, gas: helper.eth.DEFAULT_GAS})).to.be.rejected;
-  });
-
-  itEth('fractionalize NFT with NFT transfers disallowed', async ({helper}) => {
-    const nftCollection = await helper.nft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const nftToken = await nftCollection.mintToken(donor, {Ethereum: owner});
-    await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [nftCollection.collectionId, false], true);
-    const nftCollectionAddress = helper.ethAddress.fromCollectionId(nftCollection.collectionId);
-    const {contract: fractionalizer} = await initContract(helper, owner);
-    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollectionAddress, true).send({from: owner});
-
-    const nftContract = await helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner);
-    await nftContract.methods.approve(fractionalizer.options.address, nftToken.tokenId).send({from: owner});
-    await expect(fractionalizer.methods.nft2rft(nftCollectionAddress, nftToken.tokenId, 100).call())
-      .to.be.rejectedWith(/TransferNotAllowed$/g);
-  });
-
-  itEth('fractionalize NFT with RFT transfers disallowed', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const rftCollection = await helper.rft.mintCollection(donor, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const rftCollectionAddress = helper.ethAddress.fromCollectionId(rftCollection.collectionId);
-    const fractionalizer = await deployContract(helper, owner);
-    await rftCollection.addAdmin(donor, {Ethereum: fractionalizer.options.address});
-
-    await fractionalizer.methods.setRFTCollection(rftCollectionAddress).send({from: owner});
-    await helper.executeExtrinsic(donor, 'api.tx.unique.setTransfersEnabledFlag', [rftCollection.collectionId, false], true);
-
-    const nftCollection = await helper.eth.createNFTCollection(owner, 'nft', 'NFT collection', 'NFT');
-    const nftContract = await helper.ethNativeContract.collection(nftCollection.collectionAddress, 'nft', owner);
-    const mintResult = await nftContract.methods.mint(owner).send({from: owner});
-    const nftTokenId = mintResult.events.Transfer.returnValues.tokenId;
-
-    await fractionalizer.methods.setNftCollectionIsAllowed(nftCollection.collectionAddress, true).send({from: owner});
-    await nftContract.methods.approve(fractionalizer.options.address, nftTokenId).send({from: owner});
-
-    await expect(fractionalizer.methods.nft2rft(nftCollection.collectionAddress, nftTokenId, 100n).call())
-      .to.be.rejectedWith(/TransferNotAllowed$/g);
-  });
-});
deletedjs-packages/tests/src/eth/fungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/fungible.test.ts
+++ /dev/null
@@ -1,643 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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, itEth, usingEthPlaygrounds} from './util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-describe('Fungible: Plain calls', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let owner: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, owner] = await helper.arrange.createAccounts([30n, 20n], donor);
-    });
-  });
-
-  [
-    'substrate' as const,
-    'ethereum' as const,
-  ].map(testCase => {
-    itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
-      // 1. Create receiver depending on the test case:
-      const receiverEth = helper.eth.createAccount();
-      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-      const receiverSub = owner;
-      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(owner);
-
-      const ethOwner = await helper.eth.createAccountWithBalance(donor);
-      const collection = await helper.ft.mintCollection(alice);
-      await collection.addAdmin(alice, {Ethereum: ethOwner});
-
-      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', ethOwner);
-
-      // 2. Mint tokens:
-      const result = await collectionEvm.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, 100).send();
-
-      const event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(receiverSub.address));
-      expect(event.returnValues.value).to.equal('100');
-
-      // 3. Get balance depending on the test case:
-      let balance;
-      if(testCase === 'ethereum') balance = await collection.getBalance({Ethereum: receiverEth});
-      else if(testCase === 'substrate') balance = await collection.getBalance({Substrate: receiverSub.address});
-      // 3.1 Check balance:
-      expect(balance).to.eq(100n);
-    });
-  });
-
-  itEth('Can perform mintBulk()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const bulkSize = 3;
-    const receivers = [...new Array(bulkSize)].map(() => helper.eth.createAccount());
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.addAdmin(alice, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const result = await contract.methods.mintBulk(Array.from({length: bulkSize}, (_, i) => (
-      [receivers[i], (i + 1) * 10]
-    ))).send();
-    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.value - b.returnValues.value);
-    for(let i = 0; i < bulkSize; i++) {
-      const event = events[i];
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.equal(receivers[i]);
-      expect(event.returnValues.value).to.equal(String(10 * (i + 1)));
-    }
-  });
-
-  // Soft-deprecated
-  itEth('Can perform burn()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.addAdmin(alice, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
-    await contract.methods.mint(receiver, 100).send();
-
-    const result = await contract.methods.burnFrom(receiver, 49).send({from: receiver});
-
-    const event = result.events.Transfer;
-    expect(event.address).to.equal(collectionAddress);
-    expect(event.returnValues.from).to.equal(receiver);
-    expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.value).to.equal('49');
-
-    const balance = await contract.methods.balanceOf(receiver).call();
-    expect(balance).to.equal('51');
-  });
-
-  itEth('Can perform approve()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    {
-      const result = await contract.methods.approve(spender, 100).send({from: owner});
-
-      const event = result.events.Approval;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.spender).to.be.equal(spender);
-      expect(event.returnValues.value).to.be.equal('100');
-    }
-
-    {
-      const allowance = await contract.methods.allowance(owner, spender).call();
-      expect(+allowance).to.equal(100);
-    }
-    {
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const spenderCross = helper.ethCrossAccount.fromAddress(spender);
-      const allowance = await contract.methods.allowanceCross(ownerCross, spenderCross).call();
-      expect(+allowance).to.equal(100);
-    }
-  });
-
-  itEth('Can perform approveCross()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-    const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0];
-    const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
-    const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub);
-
-
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    {
-      const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
-      const event = result.events.Approval;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.spender).to.be.equal(spender);
-      expect(event.returnValues.value).to.be.equal('100');
-    }
-
-    {
-      const allowance = await contract.methods.allowance(owner, spender).call();
-      expect(+allowance).to.equal(100);
-    }
-
-
-    {
-      const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner});
-      const event = result.events.Approval;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address));
-      expect(event.returnValues.value).to.be.equal('100');
-    }
-
-    {
-      const allowance = await collection.getApprovedTokens({Ethereum: owner}, {Substrate: spenderSub.address});
-      expect(allowance).to.equal(100n);
-    }
-
-    {
-      //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call()
-    }
-  });
-
-  itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
-    const nonOwner = await helper.eth.createAccountWithBalance(donor);
-    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.ft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    await collection.mint(alice, 100n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    await expect(collectionEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
-  });
-
-
-  itEth('Can perform burnFromCross()', async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-
-    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
-
-    await collection.mint(owner, 200n, {Substrate: owner.address});
-    await collection.approveTokens(owner, {Ethereum: sender}, 100n);
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'ft');
-
-    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});
-
-    const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
-    const result = await contract.methods.burnFromCross(ownerCross, 49).send({from: sender});
-    const events = result.events;
-
-    expect(events).to.be.like({
-      Transfer: {
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: helper.address.substrateToEth(owner.address),
-          to: '0x0000000000000000000000000000000000000000',
-          value: '49',
-        },
-      },
-      Approval: {
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        returnValues: {
-          owner: helper.address.substrateToEth(owner.address),
-          spender: sender,
-          value: '51',
-        },
-        event: 'Approval',
-      },
-    });
-
-    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});
-    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(49n);
-  });
-
-  itEth('Can perform transferFrom()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    await contract.methods.approve(spender, 100).send();
-
-    {
-      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});
-
-      let event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(owner);
-      expect(event.returnValues.to).to.be.equal(receiver);
-      expect(event.returnValues.value).to.be.equal('49');
-
-      event = result.events.Approval;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.spender).to.be.equal(spender);
-      expect(event.returnValues.value).to.be.equal('51');
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(49);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(owner).call();
-      expect(+balance).to.equal(151);
-    }
-  });
-
-  itEth('Can perform transferCross()', async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-    const receiverEth = await helper.eth.createAccountWithBalance(donor);
-    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(donor);
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: sender});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender);
-
-    {
-      // Can transferCross to ethereum address:
-      const result = await collectionEvm.methods.transferCross(receiverCrossEth, 50).send({from: sender});
-      // Check events:
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(sender);
-      expect(event.returnValues.to).to.be.equal(receiverEth);
-      expect(event.returnValues.value).to.be.equal('50');
-      // Sender's balance decreased:
-      const ownerBalance = await collectionEvm.methods.balanceOf(sender).call();
-      expect(+ownerBalance).to.equal(150);
-      // Receiver's balance increased:
-      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
-      expect(+receiverBalance).to.equal(50);
-    }
-
-    {
-      // Can transferCross to substrate address:
-      const result = await collectionEvm.methods.transferCross(receiverCrossSub, 50).send({from: sender});
-      // Check events:
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(sender);
-      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(donor.address));
-      expect(event.returnValues.value).to.be.equal('50');
-      // Sender's balance decreased:
-      const senderBalance = await collection.getBalance({Ethereum: sender});
-      expect(senderBalance).to.equal(100n);
-      // Receiver's balance increased:
-      const balance = await collection.getBalance({Substrate: donor.address});
-      expect(balance).to.equal(50n);
-    }
-  });
-
-  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} incorrect amount`, async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-    const receiverEth = await helper.eth.createAccountWithBalance(donor);
-    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-    const BALANCE = 200n;
-    const BALANCE_TO_TRANSFER = BALANCE + 100n;
-
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, BALANCE, {Ethereum: sender});
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', sender);
-
-    // 1. Cannot transfer more than have
-    const receiver = testCase === 'transfer' ? receiverEth : receiverCrossEth;
-    await expect(collectionEvm.methods[testCase](receiver, BALANCE_TO_TRANSFER).send({from: sender})).to.be.rejected;
-    // 2. Zero transfer allowed (EIP-20):
-    await collectionEvm.methods[testCase](receiver, 0n).send({from: sender});
-  }));
-
-
-  itEth('Can perform transfer()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    {
-      const result = await contract.methods.transfer(receiver, 50).send({from: owner});
-
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(owner);
-      expect(event.returnValues.to).to.be.equal(receiver);
-      expect(event.returnValues.value).to.be.equal('50');
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(owner).call();
-      expect(+balance).to.equal(150);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(50);
-    }
-  });
-
-  itEth('Can perform transferFromCross()', async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-
-    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
-
-    const receiver = helper.eth.createAccount();
-
-    await collection.mint(owner, 200n, {Substrate: owner.address});
-    await collection.approveTokens(owner, {Ethereum: sender}, 100n);
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'ft');
-
-    const from = helper.ethCrossAccount.fromKeyringPair(owner);
-    const to = helper.ethCrossAccount.fromAddress(receiver);
-
-    const fromBalanceBefore = await collection.getBalance({Substrate: owner.address});
-    const toBalanceBefore = await collection.getBalance({Ethereum: receiver});
-
-    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});
-
-    expect(result.events).to.be.like({
-      Transfer: {
-        address,
-        event: 'Transfer',
-        returnValues: {
-          from: helper.address.substrateToEth(owner.address),
-          to: receiver,
-          value: '51',
-        },
-      },
-      Approval: {
-        address,
-        event: 'Approval',
-        returnValues: {
-          owner: helper.address.substrateToEth(owner.address),
-          spender: sender,
-          value: '49',
-        },
-      }});
-
-    const fromBalanceAfter = await collection.getBalance({Substrate: owner.address});
-    expect(fromBalanceBefore - fromBalanceAfter).to.be.eq(51n);
-    const toBalanceAfter = await collection.getBalance({Ethereum: receiver});
-    expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n);
-  });
-
-  itEth('Check balanceOfCross()', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {});
-    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const other = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
-
-    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
-    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
-
-    await collection.mint(alice, 100n, {Ethereum: owner.eth});
-    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
-    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
-
-    await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
-    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
-    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
-
-    await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
-    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
-    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
-
-    await collectionEvm.methods.transferCross(owner, 100n).send({from: other.eth});
-    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
-    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
-  });
-});
-
-describe('Fungible: Fees', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([20n], donor);
-    });
-  });
-
-  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-
-  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    await contract.methods.approve(spender, 100).send({from: owner});
-
-    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-
-  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-});
-
-describe('Fungible: Substrate calls', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let owner: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, owner] = await helper.arrange.createAccounts([20n, 20n], donor);
-    });
-  });
-
-  itEth('Events emitted for approve()', async ({helper}) => {
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n);
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await collection.approveTokens(alice, {Ethereum: receiver}, 100n);
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.event).to.be.equal('Approval');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.spender).to.be.equal(receiver);
-    expect(event.returnValues.value).to.be.equal('100');
-  });
-
-  itEth('Events emitted for transferFrom()', async ({helper}) => {
-    const [bob] = await helper.arrange.createAccounts([10n], donor);
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n);
-    await collection.approveTokens(alice, {Substrate: bob.address}, 100n);
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await collection.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver}, 51n);
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    let event = events[0];
-
-    expect(event.event).to.be.equal('Transfer');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.value).to.be.equal('51');
-
-    event = events[1];
-    expect(event.event).to.be.equal('Approval');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
-    expect(event.returnValues.value).to.be.equal('49');
-  });
-
-  itEth('Events emitted for transfer()', async ({helper}) => {
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.ft.mintCollection(alice);
-    await collection.mint(alice, 200n);
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await collection.transfer(alice, {Ethereum:receiver}, 51n);
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.event).to.be.equal('Transfer');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.value).to.be.equal('51');
-  });
-
-  itEth('Events emitted for transferFromCross()', async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-
-    const collection = await helper.ft.mintCollection(owner, {name: 'A', description: 'B', tokenPrefix: 'C'}, 0);
-
-    const receiver = helper.eth.createAccount();
-
-    await collection.mint(owner, 200n, {Substrate: owner.address});
-    await collection.approveTokens(owner, {Ethereum: sender}, 100n);
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'ft');
-
-    const from = helper.ethCrossAccount.fromKeyringPair(owner);
-    const to = helper.ethCrossAccount.fromAddress(receiver);
-
-    const result = await contract.methods.transferFromCross(from, to, 51).send({from: sender});
-
-    expect(result.events).to.be.like({
-      Transfer: {
-        address,
-        event: 'Transfer',
-        returnValues: {
-          from: helper.address.substrateToEth(owner.address),
-          to: receiver,
-          value: '51',
-        },
-      },
-      Approval: {
-        address,
-        event: 'Approval',
-        returnValues: {
-          owner: helper.address.substrateToEth(owner.address),
-          spender: sender,
-          value: '49',
-        },
-      }});
-  });
-});
deletedjs-packages/tests/src/eth/getCode.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/getCode.test.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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, itEth, usingEthPlaygrounds} from './util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {COLLECTION_HELPER, CONTRACT_HELPER} from '../util/index.js';
-
-describe('RPC eth_getCode', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  [
-    {address: COLLECTION_HELPER},
-    {address: CONTRACT_HELPER},
-  ].map(testCase => {
-    itEth(`returns value for native contract: ${testCase.address}`, async ({helper}) => {
-      const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [testCase.address])).toJSON();
-      const contractCodeEth = (await helper.getWeb3().eth.getCode(testCase.address));
-
-      expect(contractCodeSub).to.has.length.greaterThan(4);
-      expect(contractCodeEth).to.has.length.greaterThan(4);
-    });
-  });
-
-  itEth('returns value for custom contract', async ({helper}) => {
-    const signer = await helper.eth.createAccountWithBalance(donor);
-    const flipper = await helper.eth.deployFlipper(signer);
-
-    const contractCodeSub = (await helper.callRpc('api.rpc.eth.getCode', [flipper.options.address])).toJSON();
-    const contractCodeEth = (await helper.getWeb3().eth.getCode(flipper.options.address));
-
-    expect(contractCodeSub).to.has.length.greaterThan(4);
-    expect(contractCodeEth).to.has.length.greaterThan(4);
-  });
-});
deletedjs-packages/tests/src/eth/helpersSmoke.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/helpersSmoke.test.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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, itEth, usingEthPlaygrounds} from './util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-describe('Helpers sanity check', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Contract owner is recorded', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await (await helper.ethNativeContract.contractHelpers(owner)).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner);
-  });
-
-  itEth('Flipper is working', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    expect(await flipper.methods.getValue().call()).to.be.false;
-    await flipper.methods.flip().send({from: owner});
-    expect(await flipper.methods.getValue().call()).to.be.true;
-  });
-});
deletedjs-packages/tests/src/eth/marketplace-v2/Market.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace-v2/Market.sol
+++ /dev/null
@@ -1,414 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.18;
-
-import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
-import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
-import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
-import "@openzeppelin/contracts/access/Ownable.sol";
-import { UniqueNFT, CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol";
-import { UniqueFungible, CrossAddress as CrossAddressF } from "@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol";
-import { CollectionHelpers } from "@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol";
-import "./royalty/UniqueRoyaltyHelper.sol";
-
-contract Market is Ownable, ReentrancyGuard {
-    using ERC165Checker for address;
-
-    struct Order {
-      uint32 id;
-      uint32 collectionId;
-      uint32 tokenId;
-      uint32 amount;
-      uint256 price;
-      CrossAddress seller;
-    }
-
-    uint32 public constant version = 0;
-    uint32 public constant buildVersion = 3;
-    bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd;
-    bytes4 private constant InterfaceId_ERC165 = 0x5755c3f2;
-    CollectionHelpers private constant collectionHelpers =
-        CollectionHelpers(0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F);
-
-    mapping(uint32 => mapping(uint32 => Order)) orders;
-    uint32 private idCount = 1;
-    uint32 public marketFee;
-    uint64 public ctime;
-    address public ownerAddress;
-    mapping(address => bool) public admins;
-
-    event TokenIsUpForSale(uint32 version, Order item);
-    event TokenRevoke(uint32 version, Order item, uint32 amount);
-    event TokenIsApproved(uint32 version, Order item);
-    event TokenIsPurchased(
-      uint32 version,
-      Order item,
-      uint32 salesAmount,
-      CrossAddress buyer,
-      RoyaltyAmount[] royalties
-    );
-    event Log(string message);
-
-    error InvalidArgument(string info);
-    error InvalidMarketFee();
-    error SellerIsNotOwner();
-    error TokenIsAlreadyOnSale();
-    error TokenIsNotApproved();
-    error CollectionNotFound();
-    error CollectionNotSupportedERC721();
-    error OrderNotFound();
-    error TooManyAmountRequested();
-    error NotEnoughMoneyError();
-    error InvalidRoyaltiesError(uint256 totalRoyalty);
-    error FailTransferToken(string reason);
-
-    modifier onlyAdmin() {
-      require(msg.sender == this.owner() || admins[msg.sender], "Only admin can");
-      _;
-    }
-
-    modifier validCrossAddress(address eth, uint256 sub) {
-      if (eth == address(0) && sub == 0) {
-        revert InvalidArgument("Ethereum and Substrate addresses cannot be null at the same time");
-      }
-
-      if (eth != address(0) && sub != 0) {
-        revert InvalidArgument("Ethereum and Substrate addresses cannot be not null at the same time");
-      }
-
-      _;
-    }
-
-    constructor(uint32 fee, uint64 timestamp) {
-        marketFee = fee;
-        ctime = timestamp;
-
-        if (marketFee >= 100) {
-            revert InvalidMarketFee();
-        }
-    }
-
-    /**
-     * Fallback that allows this contract to receive native token.
-     * We need this for self-sponsoring
-     */
-    fallback() external payable {}
-
-    /**
-     * Receive also allows this contract to receive native token.
-     * We need this for self-sponsoring
-     */
-    receive() external payable {}
-
-    function getErc721(uint32 collectionId) private view returns (IERC721) {
-        address collectionAddress = collectionHelpers.collectionAddress(
-            collectionId
-        );
-
-        uint size;
-        assembly {
-            size := extcodesize(collectionAddress)
-        }
-
-        if (size == 0) {
-            revert CollectionNotFound();
-        }
-
-        if (!collectionAddress.supportsInterface(InterfaceId_ERC721)) {
-            revert CollectionNotSupportedERC721();
-        }
-
-        return IERC721(collectionAddress);
-    }
-
-    /**
-     * Add new admin. Only owner or an existing admin can add admins.
-     *
-     * @param admin: Address of a new admin to add
-     */
-    function addAdmin(address admin) public onlyAdmin {
-      admins[admin] = true;
-    }
-
-    /**
-     * Remove an admin. Only owner or an existing admin can remove admins.
-     *
-     * @param admin: Address of a new admin to add
-     */
-    function removeAdmin(address admin) public onlyAdmin {
-      delete admins[admin];
-    }
-
-    /**
-     * Place an NFT or RFT token for sale. It must be pre-approved for transfers by this contract address.
-     *
-     * @param collectionId: ID of the token collection
-     * @param tokenId: ID of the token
-     * @param price: Price (with proper network currency decimals)
-     * @param amount: Number of token fractions to list (must always be 1 for NFT)
-     * @param seller: The seller cross-address (the beneficiary account to receive payment, may be different from transaction sender)
-     */
-    function put(
-        uint32 collectionId,
-        uint32 tokenId,
-        uint256 price,
-        uint32 amount,
-        CrossAddress memory seller
-    ) public validCrossAddress(seller.eth, seller.sub) {
-        if (price == 0) {
-          revert InvalidArgument("price must not be zero");
-        }
-        if (amount == 0) {
-          revert InvalidArgument("amount must not be zero");
-        }
-
-        if (orders[collectionId][tokenId].price > 0) {
-            revert TokenIsAlreadyOnSale();
-        }
-
-        IERC721 erc721 = getErc721(collectionId);
-
-        if (erc721.ownerOf(tokenId) != msg.sender) {
-          revert SellerIsNotOwner();
-        }
-
-        if (erc721.getApproved(tokenId) != address(this)) {
-          revert TokenIsNotApproved();
-        }
-
-        Order memory order = Order(
-            0,
-            collectionId,
-            tokenId,
-            amount,
-            price,
-            seller
-        );
-
-        order.id = idCount++;
-        orders[collectionId][tokenId] = order;
-
-        emit TokenIsUpForSale(version, order);
-    }
-
-    /**
-     * Get information about the listed token order
-     *
-     * @param collectionId: ID of the token collection
-     * @param tokenId: ID of the token
-     * @return The order information
-     */
-    function getOrder(
-        uint32 collectionId,
-        uint32 tokenId
-    ) external view returns (Order memory) {
-        return orders[collectionId][tokenId];
-    }
-
-    /**
-     * Revoke the token from the sale. Only the original lister can use this method.
-     *
-     * @param collectionId: ID of the token collection
-     * @param tokenId: ID of the token
-     * @param amount: Number of token fractions to de-list (must always be 1 for NFT)
-     */
-    function revoke(
-        uint32 collectionId,
-        uint32 tokenId,
-        uint32 amount
-    ) external {
-        if (amount == 0) {
-          revert InvalidArgument("amount must not be zero");
-        }
-
-        Order memory order = orders[collectionId][tokenId];
-
-        if (order.price == 0) {
-          revert OrderNotFound();
-        }
-
-        if (amount > order.amount) {
-          revert TooManyAmountRequested();
-        }
-
-        IERC721 erc721 = getErc721(collectionId);
-
-        address ethAddress;
-        if (order.seller.eth != address(0)) {
-          ethAddress = order.seller.eth;
-        } else {
-          ethAddress = payable(address(uint160(order.seller.sub >> 96)));
-        }
-        if (erc721.ownerOf(tokenId) != ethAddress) {
-          revert SellerIsNotOwner();
-        }
-
-        order.amount -= amount;
-        if (order.amount == 0) {
-            delete orders[collectionId][tokenId];
-        } else {
-            orders[collectionId][tokenId] = order;
-        }
-
-        emit TokenRevoke(version, order, amount);
-    }
-
-    /**
-     * Test if the token is still approved to be transferred by this contract and delete the order if not.
-     *
-     * @param collectionId: ID of the token collection
-     * @param tokenId: ID of the token
-     */
-    function checkApproved(uint32 collectionId, uint32 tokenId) public onlyAdmin {
-        Order memory order = orders[collectionId][tokenId];
-        if (order.price == 0) {
-            revert OrderNotFound();
-        }
-
-        IERC721 erc721 = getErc721(collectionId);
-
-        if (erc721.getApproved(tokenId) != address(this) || erc721.ownerOf(tokenId) != getAddressFromCrossAccount(order.seller)) {
-          uint32 amount = order.amount;
-          order.amount = 0;
-          emit TokenRevoke(version, order, amount);
-
-          delete orders[collectionId][tokenId];
-        } else {
-          emit TokenIsApproved(version, order);
-        }
-    }
-
-    function getAddressFromCrossAccount(CrossAddress memory account) private pure returns (address) {
-        if (account.eth != address(0)) {
-            return account.eth;
-        } else {
-            return address(uint160(account.sub >> 96));
-        }
-    }
-
-    /**
-     * Revoke the token from the sale. Only the contract admin can use this method.
-     *
-     * @param collectionId: ID of the token collection
-     * @param tokenId: ID of the token
-     */
-    function revokeAdmin(uint32 collectionId, uint32 tokenId) public onlyAdmin {
-        Order memory order = orders[collectionId][tokenId];
-        if (order.price == 0) {
-          revert OrderNotFound();
-        }
-
-        uint32 amount = order.amount;
-        order.amount = 0;
-        emit TokenRevoke(version, order, amount);
-
-        delete orders[collectionId][tokenId];
-    }
-
-    /**
-     * Buy a token (partially for an RFT).
-     *
-     * @param collectionId: ID of the token collection
-     * @param tokenId: ID of the token
-     * @param amount: Number of token fractions to buy (must always be 1 for NFT)
-     * @param buyer: Cross-address of the buyer, eth part must be equal to the transaction signer address
-     */
-    function buy(
-        uint32 collectionId,
-        uint32 tokenId,
-        uint32 amount,
-        CrossAddress memory buyer
-    ) public payable validCrossAddress(buyer.eth, buyer.sub) nonReentrant {
-        if (msg.value == 0) {
-          revert InvalidArgument("msg.value must not be zero");
-        }
-        if (amount == 0) {
-          revert InvalidArgument("amount must not be zero");
-        }
-
-        Order memory order = orders[collectionId][tokenId];
-        if (order.price == 0) {
-            revert OrderNotFound();
-        }
-
-        if (amount > order.amount) {
-            revert TooManyAmountRequested();
-        }
-
-        uint256 totalValue = order.price * amount;
-        uint256 feeValue = (totalValue * marketFee) / 100;
-
-        if (msg.value < totalValue) {
-            revert NotEnoughMoneyError();
-        }
-
-        IERC721 erc721 = getErc721(order.collectionId);
-        if (erc721.getApproved(tokenId) != address(this)) {
-          revert TokenIsNotApproved();
-        }
-
-        order.amount -= amount;
-        if (order.amount == 0) {
-            delete orders[collectionId][tokenId];
-        } else {
-            orders[collectionId][tokenId] = order;
-        }
-
-        address collectionAddress = collectionHelpers.collectionAddress(collectionId);
-        UniqueNFT nft = UniqueNFT(collectionAddress);
-
-        nft.transferFromCross(
-          order.seller,
-          buyer,
-          order.tokenId
-        );
-
-        (uint256 totalRoyalty, RoyaltyAmount[] memory royalties) = sendRoyalties(collectionAddress, tokenId, totalValue - feeValue);
-
-        if (totalRoyalty >= totalValue - feeValue) {
-          revert InvalidRoyaltiesError(totalRoyalty);
-        }
-
-        sendMoney(order.seller, totalValue - feeValue - totalRoyalty);
-
-        if (msg.value > totalValue) {
-            sendMoney(buyer, msg.value - totalValue);
-        }
-
-        emit TokenIsPurchased(version, order, amount, buyer, royalties);
-    }
-
-    function sendMoney(CrossAddress memory to, uint256 money) private {
-      address collectionAddress = collectionHelpers.collectionAddress(0);
-
-      UniqueFungible fungible = UniqueFungible(collectionAddress);
-
-      CrossAddressF memory fromF = CrossAddressF(address(this), 0);
-      CrossAddressF memory toF = CrossAddressF(to.eth, to.sub);
-
-      fungible.transferFromCross(fromF, toF, money);
-    }
-
-    function sendRoyalties(address collection, uint tokenId, uint sellPrice) private returns (uint256, RoyaltyAmount[] memory) {
-      RoyaltyAmount[] memory royalties = UniqueRoyaltyHelper.calculate(collection, tokenId, sellPrice);
-
-      uint256 totalRoyalty = 0;
-
-      for (uint256 i=0; i<royalties.length; i++) {
-        RoyaltyAmount memory royalty = royalties[i];
-
-        totalRoyalty += royalty.amount;
-
-        sendMoney(royalty.crossAddress, royalty.amount);
-      }
-
-      return (totalRoyalty, royalties);
-    }
-
-    function withdraw(address transferTo) public onlyOwner {
-        uint256 balance = address(this).balance;
-
-        if (balance > 0) {
-            payable(transferTo).transfer(balance);
-        }
-    }
-}
deletedjs-packages/tests/src/eth/marketplace-v2/marketplace.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace-v2/marketplace.test.ts
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 Web3 from 'web3';
-import type {IKeyringPair} from '@polkadot/types/types';
-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';
-
-const {dirname} = makeNames(import.meta.url);
-
-const MARKET_FEE = 1;
-
-describe('Market V2 Contract', () => {
-  let donor: IKeyringPair;
-
-  before(async () => {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-
-      const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n);
-
-      await deployMarket(helper, marketOwner);
-    });
-  });
-
-  async function deployMarket(helper: EthUniqueHelper, marketOwner: string) {
-    return await helper.ethContract.deployByCode(
-      marketOwner,
-      'Market',
-      (await readFile(`${dirname}/Market.sol`)).toString(),
-      [
-        {
-          solPath: '@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol',
-          fsPath: `${dirname}/../api/UniqueNFT.sol`,
-        },
-        {
-          solPath: '@unique-nft/solidity-interfaces/contracts/UniqueFungible.sol',
-          fsPath: `${dirname}/../api/UniqueFungible.sol`,
-        },
-        {
-          solPath: '@openzeppelin/contracts/utils/introspection/IERC165.sol',
-          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol`,
-        },
-        {
-          solPath: '@openzeppelin/contracts/access/Ownable.sol',
-          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/access/Ownable.sol`,
-        },
-        {
-          solPath: '@openzeppelin/contracts/utils/Context.sol',
-          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/Context.sol`,
-        },
-        {
-          solPath: '@openzeppelin/contracts/security/ReentrancyGuard.sol',
-          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol`,
-        },
-        {
-          solPath: '@openzeppelin/contracts/utils/introspection/ERC165Checker.sol',
-          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/utils/introspection/ERC165Checker.sol`,
-        },
-        {
-          solPath: '@openzeppelin/contracts/token/ERC721/IERC721.sol',
-          fsPath: `${dirname}/../../../../node_modules/@openzeppelin/contracts/token/ERC721/IERC721.sol`,
-        },
-        {
-          solPath: '@unique-nft/solidity-interfaces/contracts/CollectionHelpers.sol',
-          fsPath: `${dirname}/../api/CollectionHelpers.sol`,
-        },
-        {
-          solPath: 'royalty/UniqueRoyaltyHelper.sol',
-          fsPath: `${dirname}/royalty/UniqueRoyaltyHelper.sol`,
-        },
-        {
-          solPath: 'royalty/UniqueRoyalty.sol',
-          fsPath: `${dirname}/royalty/UniqueRoyalty.sol`,
-        },
-        {
-          solPath: 'royalty/LibPart.sol',
-          fsPath: `${dirname}/royalty/LibPart.sol`,
-        },
-      ],
-      15000000,
-      [MARKET_FEE, 0],
-    );
-  }
-
-  function substrateAddressToHex(sub: Uint8Array| string, web3: Web3.default) {
-    if(typeof sub === 'string')
-      return web3.utils.padLeft(web3.utils.toHex(web3.utils.toBN(sub)), 64);
-    else if(sub instanceof Uint8Array)
-      return web3.utils.padLeft(web3.utils.bytesToHex(Array.from(sub)), 64);
-    throw Error('Infallible');
-  }
-
-  itEth('Put + Buy [eth]', async ({helper}) => {
-    const ONE_TOKEN = helper.balance.getOneTokenNominal();
-    const PRICE = 2n * ONE_TOKEN;  // 2 UNQ
-    const marketOwner = await helper.eth.createAccountWithBalance(donor, 60000n);
-    const market = await deployMarket(helper, marketOwner);
-    const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner);
-
-    // Set external sponsoring
-    await contractHelpers.methods.setSponsor(market.options.address, marketOwner).send({from: marketOwner});
-    await contractHelpers.methods.confirmSponsorship(market.options.address).send({from: marketOwner});
-
-    // Configure sponsoring
-    await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner});
-    await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner});
-
-    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC');
-    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true);
-
-    // Set collection sponsoring
-    await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner});
-    await collection.methods.confirmCollectionSponsorship().send({from: marketOwner});
-
-    const sellerCross = helper.ethCrossAccount.createAccount();
-    const result = await collection.methods.mintCross(sellerCross, []).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-    await collection.methods.approve(market.options.address, tokenId).send({from: sellerCross.eth});
-
-    // Seller has no funds at all, his transactions are sponsored
-    const sellerBalance = await helper.balance.getEthereum(sellerCross.eth);
-    expect(sellerBalance).to.be.eq(0n);
-
-    const putResult = await market.methods.put(collectionId, tokenId, PRICE.toString(), 1, sellerCross).send({
-      from: sellerCross.eth, gasLimit: 1_000_000,
-    });
-    expect(putResult.events.TokenIsUpForSale).is.not.undefined;
-
-    // Seller balance are still 0
-    const sellerBalanceAfter = await helper.balance.getEthereum(sellerCross.eth);
-    expect(sellerBalanceAfter).to.be.eq(0n);
-
-    let ownerCross = await collection.methods.ownerOfCross(tokenId).call();
-    expect(ownerCross.eth).to.be.eq(sellerCross.eth);
-    expect(ownerCross.sub).to.be.eq(sellerCross.sub);
-
-    const buyerCross = await helper.ethCrossAccount.createAccountWithBalance(donor, 10n);
-
-    // Buyer has only 10 UNQ
-    const buyerBalance = await helper.balance.getEthereum(buyerCross.eth);
-    expect(buyerBalance).to.be.eq(10n * ONE_TOKEN);
-
-    const buyResult = await market.methods.buy(collectionId, tokenId, 1, buyerCross).send({from: buyerCross.eth, value: PRICE.toString(), gasLimit: 1_000_000});
-    expect(buyResult.events.TokenIsPurchased).is.not.undefined;
-
-    // Buyer pays only value, transaction use sponsoring
-    const buyerBalanceAfter = await helper.balance.getEthereum(buyerCross.eth);
-    expect(buyerBalanceAfter).to.be.eq(10n * ONE_TOKEN - PRICE);
-
-    ownerCross = await collection.methods.ownerOfCross(tokenId).call();
-    expect(ownerCross.eth).to.be.eq(buyerCross.eth);
-    expect(ownerCross.sub).to.be.eq(buyerCross.sub);
-  });
-
-  itEth('Put + Buy [sub]', async ({helper}) => {
-    const ONE_TOKEN = helper.balance.getOneTokenNominal();
-    const PRICE = 2n * ONE_TOKEN;  // 2 UNQ
-    const web3 = helper.getWeb3();
-    const marketOwner = await helper.eth.createAccountWithBalance(donor, 600n);
-    const market = await deployMarket(helper, marketOwner);
-    const contractHelpers = helper.ethNativeContract.contractHelpers(marketOwner);
-
-    // Set self sponsoring from contract balance
-    await contractHelpers.methods.selfSponsoredEnable(market.options.address).send({from: marketOwner});
-    await helper.eth.transferBalanceFromSubstrate(donor, market.options.address, 10n);
-
-    // Configure sponsoring
-    await contractHelpers.methods.setSponsoringMode(market.options.address, SponsoringMode.Generous).send({from: marketOwner});
-    await contractHelpers.methods.setSponsoringRateLimit(market.options.address, 0).send({from: marketOwner});
-
-    const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(marketOwner, 'Sponsor', 'absolutely anything', 'ROC');
-    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', marketOwner, true);
-
-    // Set collection sponsoring
-    await collection.methods.setCollectionSponsor(marketOwner).send({from: marketOwner});
-    await collection.methods.confirmCollectionSponsorship().send({from: marketOwner});
-
-    const seller = helper.util.fromSeed(`//Market-seller-${(new Date()).getTime()}`);
-    const sellerCross = helper.ethCrossAccount.fromKeyringPair(seller);
-
-    // Seller has no funds at all, his transactions are sponsored
-    {
-      const sellerBalance = await helper.balance.getSubstrate(seller.address);
-      expect(sellerBalance).to.be.eq(0n);
-    }
-
-    const result = await collection.methods.mintCross(sellerCross, []).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-    await helper.nft.approveToken(seller, collectionId, tokenId, {Ethereum: market.options.address});
-
-    await helper.eth.sendEVM(seller, market.options.address, market.methods.put(collectionId, tokenId, PRICE, 1, sellerCross).encodeABI(), '0');
-    // Seller balance is still zero
-    {
-      const sellerBalance = await helper.balance.getSubstrate(seller.address);
-      expect(sellerBalance).to.be.eq(0n);
-    }
-    let ownerCross = await collection.methods.ownerOfCross(tokenId).call();
-    expect(ownerCross.eth).to.be.eq(sellerCross.eth);
-    expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(sellerCross.sub, web3));
-
-    const [buyer] = await helper.arrange.createAccounts([600n], donor);
-    // Buyer has only expected balance
-    {
-      const buyerBalance = await helper.balance.getSubstrate(buyer.address);
-      expect(buyerBalance).to.be.eq(600n * ONE_TOKEN);
-    }
-    const buyerCross = helper.ethCrossAccount.fromKeyringPair(buyer);
-
-    const buyerBalanceBefore = await helper.balance.getSubstrate(buyer.address);
-    await helper.eth.sendEVM(buyer, market.options.address, market.methods.buy(collectionId, tokenId, 1, buyerCross).encodeABI(), PRICE.toString());
-    const buyerBalanceAfter = await helper.balance.getSubstrate(buyer.address);
-    // Buyer balance not changed: transaction is sponsored
-    expect(buyerBalanceBefore).to.be.eq(buyerBalanceAfter + PRICE);
-
-    const sellerBalanceAfterBuy = BigInt(await helper.balance.getSubstrate(seller.address));
-    ownerCross = await collection.methods.ownerOfCross(tokenId).call();
-    expect(ownerCross.eth).to.be.eq(buyerCross.eth);
-    expect(substrateAddressToHex(ownerCross.sub, web3)).to.be.eq(substrateAddressToHex(buyerCross.sub, web3));
-
-    // Seller got only PRICE - MARKET_FEE
-    expect(sellerBalanceAfterBuy).to.be.eq(PRICE * BigInt(100 - MARKET_FEE) / 100n);
-  });
-});
deletedjs-packages/tests/src/eth/marketplace-v2/royalty/LibPart.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace-v2/royalty/LibPart.sol
+++ /dev/null
@@ -1,111 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.0;
-
-import "./UniqueRoyalty.sol";
-
-library LibPart {
-    bytes32 public constant TYPE_HASH = keccak256("Part(address account,uint96 value)");
-
-    struct Part {
-        address payable account;
-        uint96 value;
-    }
-
-    function hash(Part memory part) internal pure returns (bytes32) {
-        return keccak256(abi.encode(TYPE_HASH, part.account, part.value));
-    }
-}
-
-library LibPartAdapter {
-    function encode(LibPart.Part[] memory parts) internal pure returns (bytes memory) {
-        if (parts.length == 0) return "";
-
-        uint256[] memory encoded = new uint256[](parts.length * 2);
-
-        for (uint i = 0; i < parts.length; i++) {
-            encoded[i * 2] = 0x0100000000000000000000000000000000000000000000040000000000000000 | uint256(parts[i].value);
-            encoded[i * 2 + 1] = uint256(uint160(address(parts[i].account)));
-        }
-
-        return abi.encodePacked(encoded);
-    }
-
-    function decode(bytes memory b) internal pure returns (LibPart.Part[] memory) {
-        if (b.length == 0) return new LibPart.Part[](0);
-
-        require((b.length % (32 * 2)) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count");
-        uint partsCount = b.length / (32 * 2);
-        uint numbersCount = partsCount * 2;
-
-        LibPart.Part[] memory parts = new LibPart.Part[](partsCount);
-
-        // need this because numbers encoded via abi.encodePacked
-        bytes memory prefix = new bytes(64);
-
-        assembly {
-            mstore(add(prefix, 32), 32)
-            mstore(add(prefix, 64), numbersCount)
-        }
-
-        uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[]));
-
-        for (uint i = 0; i < partsCount; i++) {
-            uint96 value = uint96(encoded[i * 2] & 0xFFFFFFFFFFFFFFFF);
-            address account = address(uint160(encoded[i * 2 + 1]));
-
-            parts[i] = LibPart.Part({
-                account: payable(account),
-                value: value
-            });
-        }
-
-        return parts;
-    }
-}
-
-library LibPartAdapterComplex {
-    function decodeSafe(bytes memory data) internal pure returns (LibPart.Part[] memory) {
-        return fromUniqueRoyalties(UniqueRoyalty.decode(data));
-    }
-
-    function encodeSafe(LibPart.Part[] memory parts) internal pure returns (bytes memory) {
-        return UniqueRoyalty.encode(toUniqueRoyalties(parts));
-    }
-
-    function fromUniqueRoyalties(UniqueRoyaltyPart[] memory royalties) internal pure returns (LibPart.Part[] memory) {
-        LibPart.Part[] memory parts = new LibPart.Part[](royalties.length);
-
-        for (uint i = 0; i < royalties.length; i++) {
-            uint96 value = royalties[i].decimals >= 4
-            ? uint96(royalties[i].value * (10 ** (royalties[i].decimals - 4)))
-            : uint96(royalties[i].value / (10 ** (4 - royalties[i].decimals)));
-
-            parts[i] = LibPart.Part({
-                account: payable(CrossAddressLib.toAddress(royalties[i].crossAddress)),
-                value: value
-            });
-        }
-
-        return parts;
-    }
-
-    function toUniqueRoyalties(LibPart.Part[] memory parts) internal pure returns (UniqueRoyaltyPart[] memory) {
-        UniqueRoyaltyPart[] memory royalties = new UniqueRoyaltyPart[](parts.length);
-
-        for (uint i = 0; i < parts.length; i++) {
-            royalties[i] = UniqueRoyaltyPart({
-                version: 1,
-                value: uint64(parts[i].value),
-                decimals: 4,
-                crossAddress: CrossAddress({
-                    sub: 0,
-                    eth: parts[i].account
-                }),
-                isPrimarySaleOnly: false
-            });
-        }
-
-        return royalties;
-    }
-}
\ No newline at end of file
deletedjs-packages/tests/src/eth/marketplace-v2/royalty/UniqueRoyalty.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace-v2/royalty/UniqueRoyalty.sol
+++ /dev/null
@@ -1,122 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity >=0.8.17;
-
-import { CrossAddress } from "@unique-nft/solidity-interfaces/contracts/UniqueNFT.sol";
-
-struct UniqueRoyaltyPart {
-    uint8 version;
-    uint8 decimals;
-    uint64 value;
-    bool isPrimarySaleOnly;
-    CrossAddress crossAddress;
-}
-
-library CrossAddressLib {
-    function toAddress(CrossAddress memory crossAddress) internal pure returns (address) {
-        return crossAddress.eth != address(0) ? crossAddress.eth : address(uint160(crossAddress.sub >> 96));
-    }
-}
-
-library UniqueRoyalty {
-    uint private constant DECIMALS_OFFSET = 4 * 16;
-    uint private constant ADDRESS_TYPE_OFFSET = 4 * (16 + 2);           // 0 - eth, 1 - sub
-    uint private constant ROYALTY_TYPE_OFFSET = 4 * (16 + 2 + 1);       // 0 - default, 1 - primary sale only
-    uint private constant VERSION_OFFSET = 4 * (16 + 2 + 1 + 1 + 42);
-
-    uint private constant PART_LENGTH = 32 * 2;
-
-    function decode(bytes memory b) internal pure returns (UniqueRoyaltyPart[] memory) {
-        if (b.length == 0) return new UniqueRoyaltyPart[](0);
-
-        require((b.length % PART_LENGTH) == 0, "Invalid bytes length, expected (32 * 2) * UniqueRoyaltyParts count");
-        uint partsCount = b.length / PART_LENGTH;
-        uint numbersCount = partsCount * 2;
-
-        UniqueRoyaltyPart[] memory parts = new UniqueRoyaltyPart[](partsCount);
-
-        // need this because numbers encoded via abi.encodePacked
-        bytes memory prefix = new bytes(64);
-
-        assembly {
-            mstore(add(prefix, 32), 32)
-            mstore(add(prefix, 64), numbersCount)
-        }
-
-        uint256[] memory encoded = abi.decode(bytes.concat(prefix, b), (uint256[]));
-
-        for (uint i = 0; i < partsCount; i++) {
-            parts[i] = decodePart(encoded[i * 2], encoded[i * 2 + 1]);
-        }
-
-        return parts;
-    }
-
-    function encode(UniqueRoyaltyPart[] memory parts) internal pure returns (bytes memory) {
-        if (parts.length == 0) return "";
-
-        uint256[] memory encoded = new uint256[](parts.length * 2);
-
-        for (uint i = 0; i < parts.length; i++) {
-            (uint256 encodedMeta, uint256 encodedAddress) = encodePart(parts[i]);
-
-            encoded[i * 2] = encodedMeta;
-            encoded[i * 2 + 1] = encodedAddress;
-        }
-
-        return abi.encodePacked(encoded);
-    }
-
-    function decodePart(bytes memory b) internal pure returns (UniqueRoyaltyPart memory) {
-        require(b.length == PART_LENGTH, "Invalid bytes length, expected 32 * 2");
-
-        uint256[2] memory encoded = abi.decode(b, (uint256[2]));
-
-        return decodePart(encoded[0], encoded[1]);
-    }
-
-    function decodePart(
-        uint256 _meta,
-        uint256 _address
-    ) internal pure returns (UniqueRoyaltyPart memory) {
-        uint256 version = _meta >> VERSION_OFFSET;
-        bool isPrimarySaleOnly = (_meta & (1 << ROYALTY_TYPE_OFFSET)) > 0;
-        bool isEthereumAddress = (_meta & (1 << ADDRESS_TYPE_OFFSET)) == 0;
-        uint256 decimals = (_meta >> 4 * 16) & 0xFF;
-        uint256 value = _meta & 0xFFFFFFFFFFFFFFFF;
-
-        CrossAddress memory crossAddress = isEthereumAddress
-            ? CrossAddress({ sub: 0, eth: address(uint160(_address)) })
-            : CrossAddress({ sub: _address, eth: address(0) });
-
-        UniqueRoyaltyPart memory royaltyPart = UniqueRoyaltyPart({
-            version: uint8(version),
-            decimals: uint8(decimals),
-            value: uint64(value),
-            isPrimarySaleOnly: isPrimarySaleOnly,
-            crossAddress: crossAddress
-        });
-
-        return royaltyPart;
-    }
-
-    function encodePart(UniqueRoyaltyPart memory royaltyPart) internal pure returns (uint256, uint256) {
-        uint256 encodedMeta = 0;
-        uint256 encodedAddress = 0;
-
-        encodedMeta |= uint256(royaltyPart.version) << VERSION_OFFSET;
-        if (royaltyPart.isPrimarySaleOnly) encodedMeta |= 1 << ROYALTY_TYPE_OFFSET;
-
-        if (royaltyPart.crossAddress.eth == address(0x0)) {
-            encodedMeta |= 1 << ADDRESS_TYPE_OFFSET;
-            encodedAddress = royaltyPart.crossAddress.sub;
-        } else {
-            encodedAddress = uint256(uint160(royaltyPart.crossAddress.eth));
-        }
-
-        encodedMeta |= uint256(royaltyPart.decimals) << DECIMALS_OFFSET;
-        encodedMeta |= uint256(royaltyPart.value);
-
-        return (encodedMeta, encodedAddress);
-    }
-}
\ No newline at end of file
deletedjs-packages/tests/src/eth/marketplace-v2/royalty/UniqueRoyaltyHelper.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace-v2/royalty/UniqueRoyaltyHelper.sol
+++ /dev/null
@@ -1,107 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity >=0.8.17;
-
-import "./UniqueRoyalty.sol";
-import "./LibPart.sol";
-
-string constant ROYALTIES_PROPERTY = "royalties";
-
-interface ICollection {
-    function collectionProperty(string memory key) external view returns (bytes memory);
-    function property(uint256 tokenId, string memory key) external view returns (bytes memory);
-}
-
-struct RoyaltyAmount {
-    CrossAddress crossAddress;
-    uint amount;
-}
-
-library UniqueRoyaltyHelper {
-    function encodePart(UniqueRoyaltyPart memory part) internal pure returns (bytes memory) {
-        (uint256 encodedMeta, uint256 encodedAddress) = UniqueRoyalty.encodePart(part);
-
-        return abi.encodePacked(encodedMeta, encodedAddress);
-    }
-
-    function decodePart(bytes memory data) internal pure returns (UniqueRoyaltyPart memory) {
-        return UniqueRoyalty.decodePart(data);
-    }
-
-    // todo - implement smth better - check royalties sum is lte 100%
-    function validatePart(bytes memory b) internal pure returns (bool isValid) {
-        isValid = b.length == 64;
-    }
-
-    function encode(UniqueRoyaltyPart[] memory royalties) internal pure returns (bytes memory) {
-        return UniqueRoyalty.encode(royalties);
-    }
-
-    function decode(bytes memory data) internal pure returns (UniqueRoyaltyPart[] memory) {
-        return UniqueRoyalty.decode(data);
-    }
-
-    // todo - implement smth better - check royalties sum is lte 100%
-    function validate(bytes memory b) internal pure returns (bool) {
-        return b.length % 64 == 0;
-    }
-
-    function getTokenRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory) {
-        try ICollection(collection).property(tokenId, ROYALTIES_PROPERTY) returns (bytes memory encoded) {
-            return UniqueRoyalty.decode(encoded);
-        } catch {
-            return new UniqueRoyaltyPart[](0);
-        }
-    }
-
-    function getCollectionRoyalty(address collection) internal view returns (UniqueRoyaltyPart[] memory) {
-        try ICollection(collection).collectionProperty(ROYALTIES_PROPERTY) returns (bytes memory encoded) {
-            return UniqueRoyalty.decode(encoded);
-        } catch {
-            return new UniqueRoyaltyPart[](0);
-        }
-    }
-
-    function getRoyalty(address collection, uint tokenId) internal view returns (UniqueRoyaltyPart[] memory royalty) {
-        royalty = getTokenRoyalty(collection, tokenId);
-
-        if (royalty.length == 0) {
-            royalty = getCollectionRoyalty(collection);
-        }
-    }
-
-    function calculateRoyalties(UniqueRoyaltyPart[] memory royalties, bool isPrimarySale, uint sellPrice) internal pure returns (RoyaltyAmount[] memory) {
-        RoyaltyAmount[] memory royaltyAmounts = new RoyaltyAmount[](royalties.length);
-        uint amountsCount = 0;
-
-        for (uint i = 0; i < royalties.length; i++) {
-            if (isPrimarySale == royalties[i].isPrimarySaleOnly) {
-                uint amount = (sellPrice * royalties[i].value) / (10 ** (royalties[i].decimals));
-
-                royaltyAmounts[amountsCount] = RoyaltyAmount({
-                    crossAddress: royalties[i].crossAddress,
-                    amount: amount
-                });
-
-                amountsCount += 1;
-            }
-        }
-
-        // shrink royaltyAmounts to amountsCount length
-        assembly { mstore(royaltyAmounts, amountsCount) }
-
-        return royaltyAmounts;
-    }
-
-    function calculateForPrimarySale(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) {
-        UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId);
-
-        return calculateRoyalties(royalties, true, sellPrice);
-    }
-
-    function calculate(address collection, uint tokenId, uint sellPrice) internal view returns (RoyaltyAmount[] memory) {
-        UniqueRoyaltyPart[] memory royalties = getRoyalty(collection, tokenId);
-
-        return calculateRoyalties(royalties, false, sellPrice);
-    }
-}
\ No newline at end of file
deletedjs-packages/tests/src/eth/marketplace-v2/utils.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace-v2/utils.sol
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.17;
-
-contract Utils {
-    function toString(address account) public pure returns (string memory) {
-        return toString(abi.encodePacked(account));
-    }
-
-    function toString(bool value) public pure returns (string memory) {
-        return value ? "true" : "false";
-    }
-
-    function toString(uint256 value) public pure returns (string memory) {
-        return toString(abi.encodePacked(value));
-    }
-
-    function toString(bytes32 value) public pure returns (string memory) {
-        return toString(abi.encodePacked(value));
-    }
-
-    function toString(bytes memory data) public pure returns (string memory) {
-        bytes memory alphabet = "0123456789abcdef";
-
-        bytes memory str = new bytes(2 + data.length * 2);
-        str[0] = "0";
-        str[1] = "x";
-        for (uint i = 0; i < data.length; i++) {
-            str[2 + i * 2] = alphabet[uint(uint8(data[i] >> 4))];
-            str[3 + i * 2] = alphabet[uint(uint8(data[i] & 0x0f))];
-        }
-        return string(str);
-    }
-}
\ No newline at end of file
deletedjs-packages/tests/src/eth/marketplace/MarketPlace.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace/MarketPlace.sol
+++ /dev/null
@@ -1,414 +0,0 @@
-// SPDX-License-Identifier:  Apache License
-pragma solidity >=0.8.0;
-import {UniqueNFT, Dummy, ERC165} from "../api/UniqueNFT.sol";
-
-// Inline
-interface ERC20Events {
-    event Transfer(address indexed from, address indexed to, uint256 value);
-    event Approval(
-        address indexed owner,
-        address indexed spender,
-        uint256 value
-    );
-}
-
-interface ERC20 is Dummy, ERC165, ERC20Events {
-    // Selector: name() 06fdde03
-    function name() external view returns (string memory);
-
-    // Selector: symbol() 95d89b41
-    function symbol() external view returns (string memory);
-
-    // Selector: totalSupply() 18160ddd
-    function totalSupply() external view returns (uint256);
-
-    // Selector: decimals() 313ce567
-    function decimals() external view returns (uint8);
-
-    // Selector: balanceOf(address) 70a08231
-    function balanceOf(address owner) external view returns (uint256);
-
-    // Selector: transfer(address,uint256) a9059cbb
-    function transfer(address to, uint256 amount) external returns (bool);
-
-    // Selector: transferFrom(address,address,uint256) 23b872dd
-    function transferFrom(
-        address from,
-        address to,
-        uint256 amount
-    ) external returns (bool);
-
-    // Selector: approve(address,uint256) 095ea7b3
-    function approve(address spender, uint256 amount) external returns (bool);
-
-    // Selector: allowance(address,address) dd62ed3e
-    function allowance(address owner, address spender)
-        external
-        view
-        returns (uint256);
-}
-
-interface UniqueFungible is Dummy, ERC165, ERC20 {}
-
-contract MarketPlace {
-    struct Order {
-        
-        uint256 idNFT;
-        address currencyCode; // UNIQ tokens as address address (1); wKSM 
-        uint256 price;
-        uint256 time;
-        address idCollection;
-        address ownerAddr;
-        uint8 flagActive;
-        string name;
-        string symbol;
-        string tokenURI;        
-    }
-    Order[] public  orders;
-    uint test;
-    mapping (address => uint256) public balanceKSM;  //  [ownerAddr][currency] => [KSMs]
-    mapping (address => mapping (uint256 => uint256)) public  asks ; // [buyer][idCollection][idNFT] => idorder
-    mapping (address => mapping (uint => uint[])) public ordersbyNFT; // [addressCollection] =>idNFT =>idorder
-
-    mapping (address => uint[]) public asksbySeller; // [addressSeller] =>idorder
-
-    mapping (address =>bool) internal isEscrow;
-
-    //address escrow;
-    address owner;
-    address nativecoin;
-
-    // from abstract contract ReentrancyGuard 
-    // Booleans are more expensive than uint256 or any type that takes up a full
-    // word because each write operation emits an extra SLOAD to first read the
-    // slot's contents, replace the bits taken up by the boolean, and then write
-    // back. This is the compiler's defense against contract upgrades and
-    // pointer aliasing, and it cannot be disabled.
-
-    // The values being non-zero value makes deployment a bit more expensive,
-    // but in exchange the refund on every call to nonReentrant will be lower in
-    // amount. Since refunds are capped to a percentage of the total
-    // transaction's gas, it is best to keep them low in cases like this one, to
-    // increase the likelihood of the full refund coming into effect.
-    uint8 private constant _NOT_ENTERED = 1;
-    uint8 private constant _ENTERED = 2;
-
-    uint8 private _status;
-
-    struct NFT {
-        address  collection;
-        uint256 id;
-    }
-    
-     //function initialize() public initializer {
-   constructor () { // call setEscrow directly
-        owner = msg.sender;
-
-         orders.push(Order(        
-                    0,
-                    address(0),
-                    0,
-                    0,
-                    address(0),
-                    address(0),
-                    0, "","",""));
-         _status = _NOT_ENTERED;
-
-    }
-
-    modifier nonReentrant() { // from abstract contract ReentrancyGuard 
-        // On the first call to nonReentrant, _notEntered will be true
-        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
-
-        // Any calls to nonReentrant after this point will fail
-        _status = _ENTERED;
-
-        _;
-
-        // By storing the original value once again, a refund is triggered (see
-        // https://eips.ethereum.org/EIPS/eip-2200)
-        _status = _NOT_ENTERED;
-    }
-
-    modifier onlyEscrow () {
-        require(isEscrow [msg.sender] , "Only escrow can");
-        _;
-    }
-
-    modifier onlyOwner () {
-        require(msg.sender == owner, "Only owner can");
-        _;
-    }
-
-    /**
-    * Make bids (orders) to sell NFTs 
-    */
-
-     
-    receive  () external payable  {
-     //   revert ("Can't accept payment without collection and IDs, use dApp to send");
-    }
-    fallback () external payable {
-        revert ("No such function");
-    } 
-
-    event AddedAsk (uint256 _price, 
-                    address  _currencyCode, 
-                    address _idCollection, 
-                    uint256 _idNFT,
-                    uint256 orderId
-                  );
-    event EditedAsk (uint256 _price, 
-                    address  _currencyCode, 
-                    address _idCollection, 
-                    uint256 _idNFT,
-                    uint8  _active,
-                    uint orderId);
-
-    event  CanceledAsk (address _idCollection, 
-                        uint256 _idNFT, 
-                        uint orderId
-                        );
-
-    event DepositedKSM (uint256 _amount,  address _sender);
-
-    event BoughtNFT4KSM (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice );
-
-    event BoughtNFT (address _idCollection, uint256 _idNFT, uint orderID, uint orderPrice );
-
-    event WithdrawnAllKSM (address _sender, uint256 balance); 
-
-    event WithdrawnKSM (address _sender, uint256 balance); 
-
-    event Withdrawn (uint256 _amount, address _currencyCode, address _sender);
-    
-
-   function setOwner  (address _newOwner) public onlyOwner {
-        owner = _newOwner;
-    }
-
-    function setEscrow  (address _escrow, bool _state) public onlyOwner returns (bool) {
-        if (isEscrow[_escrow] != _state)  {
-            isEscrow[_escrow] = _state;
-            return true;
-        }
-        return false;
-    }
-
-    function setNativeCoin  (address _coin) public onlyOwner {
-        nativecoin = _coin;
-    }
-
-
-    function addAsk (uint256 _price, 
-                    address  _currencyCode, 
-                    address _idCollection, 
-                    uint256 _idNFT
-                  ) public  { //
-        address ownerNFT = UniqueNFT(_idCollection).ownerOf(_idNFT);
-        require (ownerNFT == msg.sender, "Only token owner can make ask");
-        string memory nameNFT;
-        string memory symbolNFT;
-        string memory uriNFT;
-        try UniqueNFT(_idCollection).name() returns (string memory name_) {
-            nameNFT = name_;
-        }
-        catch {
-            nameNFT="";
-        }   
-        try UniqueNFT(_idCollection).symbol() returns (string memory symbol_) {
-            symbolNFT = symbol_;
-        }
-        catch {
-            symbolNFT="";
-        }  
-        try UniqueNFT(_idCollection).tokenURI(_idNFT) returns (string memory uri_) {
-            uriNFT = uri_;
-        }
-        catch {
-            uriNFT="";
-        }        
-        orders.push(Order(        
-                    _idNFT,
-                    _currencyCode,
-                    _price,
-                    block.timestamp,
-                    _idCollection,
-                    msg.sender,
-                    1, // 1 = is active
-                    nameNFT, 
-                    symbolNFT,
-                    uriNFT
-             ));
-            
-            uint orderId = orders.length-1;
-            asks[_idCollection][_idNFT] = orderId;
-            asksbySeller[msg.sender].push(orderId);
-            UniqueNFT(_idCollection).transferFrom(msg.sender, address(this), _idNFT);
-            emit AddedAsk(_price, _currencyCode, _idCollection, _idNFT, orderId);     
-    }
-
-    function editAsk (uint256 _price, 
-                    address  _currencyCode, 
-                    address _idCollection, 
-                    uint256 _idNFT,
-                    uint8  _active) public {
-        
-
-        uint orderID =  asks[_idCollection][_idNFT];
-
-        require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask");
-        require (orders[orderID].flagActive != 0, "This ask is closed");
-        if (_price> 0 ) {
-            orders[orderID].price = _price ;  
-        }
-        
-        if (_currencyCode != address(0) ) {
-            orders[orderID].currencyCode = _currencyCode ;  
-        }
-
-        orders[orderID].time = block.timestamp;
-        orders[orderID].flagActive = _active;
-        
-        emit EditedAsk(_price, _currencyCode, _idCollection, _idNFT, _active, orderID);
-        }
-        
-
-    function cancelAsk (address _idCollection, 
-                        uint256 _idNFT
-                        ) public {
-
-        uint orderID =  asks[_idCollection][_idNFT];
-
-        require (orders[orderID].ownerAddr == msg.sender, "Only token owner can edit ask");
-        require (orders[orderID].flagActive != 0, "This ask is closed");
-
-        orders[orderID].time = block.timestamp;
-        orders[orderID].flagActive = 0;
-        UniqueNFT(_idCollection).transferFrom(address(this),orders[orderID].ownerAddr, _idNFT);
-        emit CanceledAsk(_idCollection, _idNFT, orderID);
-        }
-
-
-    function depositKSM (uint256 _amount,  address _sender) public onlyEscrow {
-        balanceKSM[_sender] = balanceKSM[_sender] + _amount;
-        emit DepositedKSM(_amount, _sender);
-    }
-
-    function buyKSM (address _idCollection, uint256 _idNFT, address _buyer, address _receiver ) public {
-        
-        Order memory order = orders[ asks[_idCollection][_idNFT]];
-        require(isEscrow[msg.sender]  || msg.sender == _buyer, "Only escrow or buyer can call buyKSM" );
-        //1. reduce balance
-
-        balanceKSM[_buyer] = balanceKSM[_buyer] - order.price;
-        balanceKSM[order.ownerAddr] = balanceKSM[order.ownerAddr] + order.price;
-        // 2. close order
-        orders[ asks[_idCollection][_idNFT]].flagActive = 0;
-        // 3. transfer NFT to buyer
-        UniqueNFT(_idCollection).transferFrom(address(this), _receiver, _idNFT);
-        emit BoughtNFT4KSM(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price);
-
-    }
-    function buy (address _idCollection, uint256 _idNFT ) public payable returns (bool result) { //buing for UNQ like as ethers 
-        
-        Order memory order = orders[asks[_idCollection][_idNFT]]; 
-        //1. check sent amount and send to seller
-        require (msg.value == order.price, "Not right amount sent, have to be equal price" );     
-        // 2. close order
-        orders[ asks[_idCollection][_idNFT]].flagActive = 0;
-        
-        // 3. transfer NFT to buyer
-        UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
-        //uint balance  = address(this).balance;
-        result = payable(order.ownerAddr).send (order.price); 
-        emit BoughtNFT(_idCollection, _idNFT, asks[_idCollection][_idNFT], order.price);
-        
-    }
-
-/* 
-    function buyOther (address _idCollection, uint256 _idNFT, address _currencyCode, uint _amount ) public  { //buy for sny token if seller wants
-        
-        Order memory order = orders[ asks[_idCollection][_idNFT]];
-        //1. check sent amount and transfer from buyer to seller
-        require (order.price == _amount && order.currencyCode == _currencyCode, "Not right amount or currency sent, have to be equal currency and price" );
-        // !!! transfer have to be approved to marketplace!
-        UniqueFungible(order.currencyCode).transferFrom(msg.sender, address(this), order.price); //to not disclojure buyer's address 
-        UniqueFungible(order.currencyCode).transfer(order.ownerAddr, order.price);
-        // 2. close order
-        orders[ asks[_idCollection][_idNFT]].flagActive = 0;
-        // 3. transfer NFT to buyer
-        UniqueNFT(_idCollection).transferFrom(address(this), msg.sender, _idNFT);
-
-
-    }
- */
-
-    function withdrawAllKSM (address _sender) public  nonReentrant returns (uint lastBalance ){
-        require(isEscrow[msg.sender]  || msg.sender == _sender, "Only escrow or balance owner can withdraw all KSM" );
-
-        lastBalance = balanceKSM[_sender];
-        balanceKSM[_sender] =0;
-        emit WithdrawnAllKSM(_sender, lastBalance);
-    }
-
-    function withdrawKSM (uint _amount, address _sender) onlyEscrow public {
-        balanceKSM[_sender] = balanceKSM[_sender] - _amount;
-        emit WithdrawnKSM(_sender, balanceKSM[_sender]);
-        
-    }
-
-    function withdraw (uint256 _amount, address _currencyCode) public  nonReentrant returns (bool result ){ //onlyOwner
-        address payable _sender = payable( msg.sender);
-        if (_currencyCode != nativecoin ) { //erc20 compat. tokens on UNIQUE chain
-            // uint balance = UniqueFungible(_currencyCode).balanceOf(address(this));
-            UniqueFungible(_currencyCode).transfer(_sender, _amount);
-        } else {
-            // uint balance  = address(this).balance;
-
-            result =  (_sender).send(_amount); // for UNQ like as ethers 
-        }
-        emit Withdrawn(_amount, _currencyCode, _sender);
-        return result;
-
-    }
-
-    
-   // event GettedOrder(uint , Order);
-    function getOrder (address _idCollection, uint256 _idNFT) public view returns (Order memory) {
-        uint orderId = asks[_idCollection][_idNFT];
-        Order memory order = orders[orderId];
- //       emit GettedOrder (orderId, order);
-        return order;
-    }
-
-    function getOrdersLen () public view returns (uint) {
-        return orders.length;
-    }
-
-    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) public pure returns(bytes4) {
-            return bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
-        }
-     
-    //TODO make destructing function to return all 
-    function terminate(address[] calldata tokens, NFT[] calldata nfts) public onlyOwner {
-    // Transfer tokens to owner (TODO: error handling)
-        for (uint i = 0; i < tokens.length; i++) {
-            address addr = tokens[i];
-            UniqueFungible token = UniqueFungible(addr);
-            uint256 balance = token.balanceOf(address(this));
-            token.transfer(owner, balance);
-        }
-        for (uint i = 0; i < nfts.length; i++) {
-            address addr = nfts[i].collection;
-            UniqueNFT token = UniqueNFT(addr);
-            token.transferFrom(address(this), owner, nfts[i].id);
-        }    
-    // Transfer Eth to owner and terminate contract
-        address payable owner1 = payable (owner);
-        uint balance = address(this).balance;
-        owner1.transfer(balance);
-        
-    } 
-    
-}
deletedjs-packages/tests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/marketplace/marketplace.test.ts
+++ /dev/null
@@ -1,208 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 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';
-
-const {dirname} = makeNames(import.meta.url);
-
-describe('Matcher contract usage', () => {
-  const PRICE = 2000n;
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let aliceMirror: string;
-  let aliceDoubleMirror: string;
-  let seller: IKeyringPair;
-  let sellerMirror: string;
-
-  before(async () => {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  beforeEach(async () => {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      [alice] = await helper.arrange.createAccounts([1000n], donor);
-      aliceMirror = helper.address.substrateToEth(alice.address).toLowerCase();
-      aliceDoubleMirror = helper.address.ethToSubstrate(aliceMirror);
-      seller = await privateKey(`//Seller/${Date.now()}`);
-      sellerMirror = helper.address.substrateToEth(seller.address).toLowerCase();
-
-      await helper.balance.transferToSubstrate(donor, aliceDoubleMirror, 10_000_000_000_000_000_000n);
-    });
-  });
-
-  itEth('With UNQ', async ({helper}) => {
-    const matcherOwner = await helper.eth.createAccountWithBalance(donor);
-    const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2);
-
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner);
-    await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner});
-    await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});
-
-    await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner});
-    await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor});
-
-    const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}});
-    await collection.confirmSponsorship(alice);
-    await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror});
-    const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
-    await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror);
-
-    await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner});
-    await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner});
-
-    const token = await collection.mintToken(alice, {Ethereum: sellerMirror});
-
-    // Token is owned by seller initially
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror});
-
-    // Ask
-    {
-      await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0');
-      await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0');
-    }
-
-    // Token is transferred to matcher
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});
-
-    // Buy
-    {
-      const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address);
-      await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString());
-      expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE);
-    }
-
-    // Token is transferred to evm account of alice
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror});
-
-    // Transfer token to substrate side of alice
-    await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address});
-
-    // Token is transferred to substrate account of alice, seller received funds
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itEth('With escrow', async ({helper}) => {
-    const matcherOwner = await helper.eth.createAccountWithBalance(donor);
-    const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2);
-
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const escrow = await helper.eth.createAccountWithBalance(donor);
-    await matcher.methods.setEscrow(escrow, true).send({from: matcherOwner});
-    const helpers = await helper.ethNativeContract.contractHelpers(matcherOwner);
-    await helpers.methods.setSponsoringMode(matcher.options.address, SponsoringMode.Allowlisted).send({from: matcherOwner});
-    await helpers.methods.setSponsoringRateLimit(matcher.options.address, 1).send({from: matcherOwner});
-
-    await helpers.methods.setSponsor(matcher.options.address, sponsor).send({from: matcherOwner});
-    await helpers.methods.confirmSponsorship(matcher.options.address).send({from: sponsor});
-
-    const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}, pendingSponsor: {Substrate: alice.address}});
-    await collection.confirmSponsorship(alice);
-    await collection.addToAllowList(alice, {Substrate: aliceDoubleMirror});
-    const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
-    await helper.eth.transferBalanceFromSubstrate(donor, aliceMirror);
-
-
-    await helpers.methods.toggleAllowed(matcher.options.address, aliceMirror, true).send({from: matcherOwner});
-
-    await helpers.methods.toggleAllowed(matcher.options.address, sellerMirror, true).send({from: matcherOwner});
-
-    const token = await collection.mintToken(alice, {Ethereum: sellerMirror});
-
-    // Token is owned by seller initially
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror});
-
-    // Ask
-    {
-      await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0');
-      await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0');
-    }
-
-    // Token is transferred to matcher
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});
-
-    // Give buyer KSM
-    await matcher.methods.depositKSM(PRICE, aliceMirror).send({from: escrow});
-
-    // Buy
-    {
-      expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal('0');
-      expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal(PRICE.toString());
-
-      await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buyKSM(evmCollection.options.address, token.tokenId, aliceMirror, aliceMirror).encodeABI(), '0');
-
-      // Price is removed from buyer balance, and added to seller
-      expect(await matcher.methods.balanceKSM(aliceMirror).call()).to.be.equal('0');
-      expect(await matcher.methods.balanceKSM(sellerMirror).call()).to.be.equal(PRICE.toString());
-    }
-
-    // Token is transferred to evm account of alice
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror});
-
-    // Transfer token to substrate side of alice
-    await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address});
-
-    // Token is transferred to substrate account of alice, seller received funds
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itEth('Sell tokens from substrate user via EVM contract', async ({helper}) => {
-    const matcherOwner = await helper.eth.createAccountWithBalance(donor);
-    const matcher = await helper.ethContract.deployByCode(matcherOwner, 'MarketPlace', (await readFile(`${dirname}/MarketPlace.sol`)).toString(), [{solPath: 'api/UniqueNFT.sol', fsPath: `${dirname}/../api/UniqueNFT.sol`}], helper.eth.DEFAULT_GAS * 2);
-
-    await helper.eth.transferBalanceFromSubstrate(donor, matcher.options.address);
-
-    const collection = await helper.nft.mintCollection(alice, {limits: {sponsorApproveTimeout: 1}});
-    const evmCollection = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
-
-    await helper.balance.transferToSubstrate(donor, seller.address, 100_000_000_000_000_000_000n);
-
-    const token = await collection.mintToken(alice, {Ethereum: sellerMirror});
-
-    // Token is owned by seller initially
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: sellerMirror});
-
-    // Ask
-    {
-      await helper.eth.sendEVM(seller, evmCollection.options.address, evmCollection.methods.approve(matcher.options.address, token.tokenId).encodeABI(), '0');
-      await helper.eth.sendEVM(seller, matcher.options.address, matcher.methods.addAsk(PRICE, '0x0000000000000000000000000000000000000001', evmCollection.options.address, token.tokenId).encodeABI(), '0');
-    }
-
-    // Token is transferred to matcher
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: matcher.options.address.toLowerCase()});
-
-    // Buy
-    {
-      const sellerBalanceBeforePurchase = await helper.balance.getSubstrate(seller.address);
-      await helper.eth.sendEVM(alice, matcher.options.address, matcher.methods.buy(evmCollection.options.address, token.tokenId).encodeABI(), PRICE.toString());
-      expect(await helper.balance.getSubstrate(seller.address) - sellerBalanceBeforePurchase === PRICE);
-    }
-
-    // Token is transferred to evm account of alice
-    expect(await token.getOwner()).to.be.deep.equal({Ethereum: aliceMirror});
-
-    // Transfer token to substrate side of alice
-    await token.transferFrom(alice, {Ethereum: aliceMirror}, {Substrate: alice.address});
-
-    // Token is transferred to substrate account of alice, seller received funds
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-});
deletedjs-packages/tests/src/eth/migration.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/migration.seqtest.ts
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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, itEth, usingEthPlaygrounds} from './util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {Struct} from '@polkadot/types';
-
-import type {IEvent} from '@unique/playgrounds/src/types.js';
-import type {InterfaceTypes} from '@polkadot/types/types/registry';
-import {ApiPromise} from '@polkadot/api';
-
-const encodeEvent = (api: ApiPromise, pallet: string, palletEvents: string, event: string, fields: any) => {
-  const palletIndex = api.runtimeMetadata.asV14.pallets.find(p => p.name.toString() == pallet)!.index.toNumber();
-  const eventMeta = api.events[palletEvents][event].meta;
-  const eventIndex = eventMeta.index.toNumber();
-  const data = [
-    palletIndex, eventIndex,
-  ];
-  const metaEvent = api.registry.findMetaEvent(new Uint8Array(data));
-  data.push(...new Struct(api.registry, {data: metaEvent}, {data: fields}).toU8a());
-
-  const typeName = api.registry.lookup.names.find(n => n.endsWith('RuntimeEvent'))!;
-  const obj = api.registry.createType(typeName, new Uint8Array(data)) as InterfaceTypes['RuntimeEvent'];
-  return obj.toHex();
-};
-
-describe('EVM Migrations', () => {
-  let superuser: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      superuser = await privateKey('//Alice');
-      charlie = await privateKey('//Charlie');
-    });
-  });
-
-  // todo:playgrounds requires sudo, look into later
-  itEth('Deploy contract saved state', async ({helper}) => {
-    /*
-      contract StatefulContract {
-        uint counter;
-        mapping (uint => uint) kv;
-
-        function inc() public {
-          counter = counter + 1;
-        }
-        function counterValue() public view returns (uint) {
-          return counter;
-        }
-
-        function set(uint key, uint value) public {
-          kv[key] = value;
-        }
-
-        function get(uint key) public view returns (uint) {
-          return kv[key];
-        }
-      }
-    */
-    const ADDRESS = '0x4956bf52ef9ed8789f21bc600e915e0d961079f6';
-    const CODE = '0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80631ab06ee514610051578063371303c01461006d5780637bfdec3b146100775780639507d39a14610095575b600080fd5b61006b60048036038101906100669190610160565b6100c5565b005b6100756100e1565b005b61007f6100f8565b60405161008c91906101af565b60405180910390f35b6100af60048036038101906100aa9190610133565b610101565b6040516100bc91906101af565b60405180910390f35b8060016000848152602001908152602001600020819055505050565b60016000546100f091906101ca565b600081905550565b60008054905090565b600060016000838152602001908152602001600020549050919050565b60008135905061012d8161025e565b92915050565b60006020828403121561014957610148610259565b5b60006101578482850161011e565b91505092915050565b6000806040838503121561017757610176610259565b5b60006101858582860161011e565b92505060206101968582860161011e565b9150509250929050565b6101a981610220565b82525050565b60006020820190506101c460008301846101a0565b92915050565b60006101d582610220565b91506101e083610220565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102155761021461022a565b5b828201905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61026781610220565b811461027257600080fd5b5056fea26469706673582212206a02d2fb5c244105ab884961479c1aee3b4c1011e4b5530ab483eb22344a865664736f6c63430008060033';
-    const DATA = [
-      // counter = 10
-      ['0x0000000000000000000000000000000000000000000000000000000000000000', '0x000000000000000000000000000000000000000000000000000000000000000a'],
-      // kv = {1: 1, 2: 2, 3: 3, 4: 4},
-      ['0xcc69885fda6bcc1a4ace058b4a62bf5e179ea78fd58a1ccd71c22cc9b688792f', '0x0000000000000000000000000000000000000000000000000000000000000001'],
-      ['0xd9d16d34ffb15ba3a3d852f0d403e2ce1d691fb54de27ac87cd2f993f3ec330f', '0x0000000000000000000000000000000000000000000000000000000000000002'],
-      ['0x7dfe757ecd65cbd7922a9c0161e935dd7fdbcc0e999689c7d31633896b1fc60b', '0x0000000000000000000000000000000000000000000000000000000000000003'],
-      ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'],
-    ];
-
-    const caller = await helper.eth.createAccountWithBalance(superuser);
-
-    const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]);
-    const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]);
-    const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]);
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled;
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled;
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled;
-
-    const web3 = helper.getWeb3();
-    const contract = new web3.eth.Contract([
-      {
-        inputs: [],
-        name: 'counterValue',
-        outputs: [{
-          internalType: 'uint256',
-          name: '',
-          type: 'uint256',
-        }],
-        stateMutability: 'view',
-        type: 'function',
-      },
-      {
-        inputs: [{
-          internalType: 'uint256',
-          name: 'key',
-          type: 'uint256',
-        }],
-        name: 'get',
-        outputs: [{
-          internalType: 'uint256',
-          name: '',
-          type: 'uint256',
-        }],
-        stateMutability: 'view',
-        type: 'function',
-      },
-    ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS});
-
-    expect(await contract.methods.counterValue().call()).to.be.equal('10');
-    for(let i = 1; i <= 4; i++) {
-      expect(await contract.methods.get(i).call()).to.be.equal(i.toString());
-    }
-  });
-  itEth('Fake collection creation on substrate side', async ({helper}) => {
-    const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[
-      encodeEvent(helper.getApi(), 'Common', 'common', 'CollectionCreated', [
-        // Collection Id
-        9999,
-        // Collection mode: NFT
-        1,
-        // Owner
-        charlie.address,
-      ]),
-    ]]);
-    await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contain('common.CollectionCreated');
-  });
-  itEth('Fake token creation on substrate side', async ({helper}) => {
-    const txInsertEvents = helper.constructApiCall('api.tx.evmMigration.insertEvents', [[
-      encodeEvent(helper.getApi(), 'Common', 'common', 'ItemCreated', [
-        // Collection Id
-        9999,
-        // TokenId
-        9999,
-        // Owner
-        {Substrate: charlie.address},
-        // Amount
-        1,
-      ]),
-    ]]);
-    await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEvents]);
-    const event = helper.chainLog[helper.chainLog.length - 1].events as IEvent[];
-    const eventStrings = event.map(e => `${e.section}.${e.method}`);
-
-    expect(eventStrings).to.contain('common.ItemCreated');
-  });
-  itEth('Fake token creation on ethereum side', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(superuser);
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const caller = await helper.eth.createAccountWithBalance(superuser);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    {
-      const txInsertEthLogs = helper.constructApiCall('api.tx.evmMigration.insertEthLogs', [[
-        {
-        // Contract, which has emitted this log
-          address: collectionAddress,
-
-          topics: [
-            // First topic - event signature
-            helper.getWeb3().eth.abi.encodeEventSignature('Transfer(address,address,uint256)'),
-            // Rest of topics - indexed event fields in definition order
-            helper.getWeb3().eth.abi.encodeParameter('address', '0x' + '00'.repeat(20)),
-            helper.getWeb3().eth.abi.encodeParameter('address', caller),
-            helper.getWeb3().eth.abi.encodeParameter('uint256', 9999),
-          ],
-
-          // Every field coming from event, which is not marked as indexed, should be encoded here
-          // NFT transfer has no such fields, but here is an example for some other possible event:
-          // data: helper.getWeb3().eth.abi.encodeParameters(['uint256', 'address'], [22, collectionAddress])
-          data: [],
-        },
-      ]]);
-      await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txInsertEthLogs]);
-    }
-
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal('0x' + '00'.repeat(20));
-    expect(event.returnValues.to).to.be.equal(caller);
-    expect(event.returnValues.tokenId).to.be.equal('9999');
-  });
-});
deletedjs-packages/tests/src/eth/nativeFungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/nativeFungible.test.ts
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2019-2023 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import {UniqueHelper} from '@unique/playgrounds/src/unique.js';
-
-describe('NativeFungible: ERC20 calls', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('approve()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    await expect(contract.methods.approve(spender, 100).call({from: owner})).to.be.rejectedWith('approve not supported');
-  });
-
-  itEth('balanceOf()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor, 123n);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const balance = await contract.methods.balanceOf(owner).call({from: owner});
-    expect(balance).to.be.eq('123000000000000000000');
-  });
-
-  itEth('decimals()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const realDecimals = (await helper.chain.getChainProperties().tokenDecimals)[0].toString();
-    const decimals = await contract.methods.decimals().call({from: owner});
-    expect(decimals).to.be.eq(realDecimals);
-  });
-
-  itEth('name()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const realName = await UniqueHelper.detectNetwork(helper.getApi());
-    const name = await contract.methods.name().call({from: owner});
-    expect(name).to.be.eq(realName);
-  });
-
-  itEth('symbol()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const realName = (await helper.chain.getChainProperties().tokenSymbol)[0];
-    const name = await contract.methods.symbol().call({from: owner});
-    expect(name).to.be.eq(realName);
-  });
-
-  itEth('totalSupply()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const totalSupplyEth = BigInt(await contract.methods.totalSupply().call({from: owner}));
-    const totalSupplySub = await helper.balance.getTotalIssuance();
-    expect(totalSupplyEth).to.be.eq(totalSupplySub);
-  });
-
-  itEth('transfer()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const balanceOwnerBefore = await helper.balance.getEthereum(owner);
-    const balanceReceiverBefore = await helper.balance.getEthereum(receiver);
-
-    await contract.methods.transfer(receiver, 50).send({from: owner});
-
-    const balanceOwnerAfter = await helper.balance.getEthereum(owner);
-    const balanceReceiverAfter = await helper.balance.getEthereum(receiver);
-
-    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
-    expect(balanceReceiverBefore + 50n).to.be.equal(balanceReceiverAfter);
-  });
-
-  itEth('transferFrom()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const balanceOwnerBefore = await helper.balance.getEthereum(owner);
-    const balanceReceiverBefore = await helper.balance.getEthereum(receiver);
-
-    await contract.methods.transferFrom(owner, receiver, 50).send({from: owner});
-
-    const balanceOwnerAfter = await helper.balance.getEthereum(owner);
-    const balanceReceiverAfter = await helper.balance.getEthereum(receiver);
-
-    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
-    expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
-
-    await expect(contract.methods.transferFrom(receiver, receiver, 50).call({from: owner})).to.be.rejectedWith('ApprovedValueTooLow');
-  });
-});
-
-describe('NativeFungible: ERC20UniqueExtensions calls', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('transferCross()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-
-    const balanceOwnerBefore = await helper.balance.getEthereum(owner);
-    const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth);
-
-    await contract.methods.transferCross(receiver, 50).send({from: owner});
-
-    const balanceOwnerAfter = await helper.balance.getEthereum(owner);
-    const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth);
-
-    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
-    expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
-  });
-
-  itEth('transferFromCross()', async ({helper}) => {
-    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(0);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
-
-    const balanceOwnerBefore = await helper.balance.getEthereum(owner.eth);
-    const balanceReceiverBefore = await helper.balance.getEthereum(receiver.eth);
-
-    await contract.methods.transferFromCross(owner, receiver, 50).send({from: owner.eth});
-
-    const balanceOwnerAfter = await helper.balance.getEthereum(owner.eth);
-    const balanceReceiverAfter = await helper.balance.getEthereum(receiver.eth);
-
-    expect(balanceOwnerBefore - 50n > balanceOwnerAfter).to.be.true;
-    expect(balanceReceiverBefore === balanceReceiverAfter - 50n).to.be.true;
-
-    await expect(contract.methods.transferFromCross(receiver, receiver, 50).call({from: owner.eth})).to.be.rejectedWith('no permission');
-  });
-});
deletedjs-packages/tests/src/eth/nativeRpc/estimateGas.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/nativeRpc/estimateGas.test.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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, itEth, usingEthPlaygrounds} from '../util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-
-describe('Ethereum native RPC calls', () => {
-  let donor: IKeyringPair;
-  const NATIVE_TOKEN_ADDRESS = '0x17c4e6453cc49aaaaeaca894e6d9683e00000000';
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('estimate gas', async ({helper}) => {
-    const BALANCE = 100n;
-    const BALANCE_TO_TRANSFER = 90n;
-
-    const owner = await helper.eth.createAccountWithBalance(donor, BALANCE);
-    const recepient = helper.eth.createAccount();
-
-    const web3 = helper.getWeb3();
-    // data: transfer(recepient, 90);
-    const data = web3.eth.abi.encodeFunctionCall({
-      name: 'transfer',
-      type: 'function',
-      inputs: [{
-        type: 'address',
-        name: 'to',
-      },{
-        type: 'uint256',
-        name: 'amount',
-      }],
-    }, [recepient, (BALANCE_TO_TRANSFER * (10n ** 18n)).toString()]);
-
-    const estimateGas = await web3.eth.estimateGas({
-      to: NATIVE_TOKEN_ADDRESS,
-      value: '0x0',
-      data,
-      from: owner,
-      maxFeePerGas: '0x14c9338c61d',
-    });
-
-    expect(estimateGas).to.be.greaterThan(35000).and.to.be.lessThan(50000);
-  });
-});
deletedjs-packages/tests/src/eth/nesting/nest.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/nesting/nest.test.ts
+++ /dev/null
@@ -1,289 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {Contract} from 'web3-eth-contract';
-
-import {itEth, usingEthPlaygrounds, expect} from '../util/index.js';
-import {EthUniqueHelper} from '../util/playgrounds/unique.dev.js';
-
-const createNestingCollection = async (
-  helper: EthUniqueHelper,
-  owner: string,
-  mergeDeprecated = false,
-): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {
-  const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-
-  const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, mergeDeprecated);
-  await contract.methods.setCollectionNesting([true, false, []]).send({from: owner});
-
-  return {collectionId, collectionAddress, contract};
-};
-
-
-describe('EVM nesting tests group', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  describe('Integration Test: EVM Nesting', () => {
-    itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionId, contract} = await createNestingCollection(helper, owner);
-
-      // Create a token to be nested to
-      const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;
-      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);
-
-      // Create a nested token
-      const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});
-      const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;
-      expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);
-
-      // Create a token to be nested and nest
-      const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;
-
-      await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});
-      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);
-
-      // Unnest token back
-      await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});
-      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);
-    });
-
-    itEth('NFT: collectionNesting()', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-      const unnestedContract = await helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);
-      expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
-
-      const {contract} = await createNestingCollection(helper, owner);
-      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, []]);
-      await contract.methods.setCollectionNesting([true, false, [unnestedCollectionAddress]]).send({from: owner});
-      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress]]);
-      await contract.methods.setCollectionNesting([false, true, [unnestedCollectionAddress]]).send({from: owner});
-      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, true, [unnestedCollectionAddress]]);
-      await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});
-      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
-    });
-
-    // Sof-deprecated
-    itEth('NFT: collectionNestingRestrictedCollectionIds() & collectionNestingPermissions', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const {collectionId: unnestedCollsectionId, collectionAddress: unnsetedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');
-      const unnestedContract = await helper.ethNativeContract.collection(unnsetedCollectionAddress, 'nft', owner, true);
-      expect(await unnestedContract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([false, []]);
-
-      const {contract} = await createNestingCollection(helper, owner, true);
-      expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, []]);
-      await contract.methods['setCollectionNesting(bool,address[])'](true, [unnsetedCollectionAddress]).send({from: owner});
-      expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, [unnestedCollsectionId.toString()]]);
-      expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', true]]);
-      await contract.methods['setCollectionNesting(bool)'](false).send({from: owner});
-      expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['1', false], ['0', false]]);
-    });
-
-    itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner);
-      const {contract: contractB} = await createNestingCollection(helper, owner);
-      await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner});
-
-      // Create a token to nest into
-      const mintingtargetNftTokenIdResult = await contractA.methods.mint(owner).send({from: owner});
-      const targetNftTokenId = mintingtargetNftTokenIdResult.events.Transfer.returnValues.tokenId;
-      const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId);
-
-      // Create a token for nesting in the same collection as the target
-      const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});
-      const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;
-
-      // Create a token for nesting in a different collection
-      const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});
-      const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;
-
-      // Nest
-      await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner});
-      expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1);
-
-      await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner});
-      expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1);
-    });
-  });
-
-  describe('Negative Test: EVM Nesting', () => {
-    itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionId, contract} = await createNestingCollection(helper, owner);
-      await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});
-
-      // Create a token to nest into
-      const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
-      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);
-
-      // Create a token to nest
-      const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;
-
-      // Try to nest
-      await expect(contract.methods
-        .transfer(targetNftTokenAddress, nftTokenId)
-        .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
-    });
-
-    itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const malignant = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionId, contract} = await createNestingCollection(helper, owner);
-
-      // Mint a token
-      const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});
-      const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
-      const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);
-
-      // Mint a token belonging to a different account
-      const mintingTokenIdResult = await contract.methods.mint(malignant).send({from: owner});
-      const tokenId = mintingTokenIdResult.events.Transfer.returnValues.tokenId;
-
-      // Try to nest one token in another as a non-owner account
-      await expect(contract.methods
-        .transfer(targetTokenAddress, tokenId)
-        .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
-    });
-
-    itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const malignant = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner);
-      const {contract: contractB} = await createNestingCollection(helper, owner);
-
-      await contractA.methods.setCollectionNesting([true, false, [contractA.options.address, contractB.options.address]]).send({from: owner});
-
-      // Create a token in one collection
-      const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});
-      const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;
-      const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);
-
-      // Create a token in another collection
-      const mintingTokenIdBResult = await contractB.methods.mint(malignant).send({from: owner});
-      const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;
-
-      // Try to drag someone else's token into the other collection and nest
-      await expect(contractB.methods
-        .transfer(nftTokenAddressA, nftTokenIdB)
-        .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');
-    });
-
-    itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionId: collectionIdA, contract: contractA} = await createNestingCollection(helper, owner);
-      const {contract: contractB} = await createNestingCollection(helper, owner);
-
-      await contractA.methods.setCollectionNesting([true, false, [contractA.options.address]]).send({from: owner});
-
-      // Create a token in one collection
-      const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});
-      const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;
-      const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);
-
-      // Create a token in another collection
-      const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});
-      const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;
-
-
-      // Try to nest into a token in the other collection, disallowed in the first
-      await expect(contractB.methods
-        .transfer(nftTokenAddressA, nftTokenIdB)
-        .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest');
-    });
-  });
-
-  describe('Fungible', () => {
-    async function createFungibleCollection(helper: EthUniqueHelper, owner: string, mode: 'ft' | 'native ft') {
-      if(mode === 'ft') {
-        const {collectionAddress} = await helper.eth.createFungibleCollection(owner, '', 18, '', '');
-        const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-        await contract.methods.mint(owner, 100n).send({from: owner});
-        return {collectionAddress, contract};
-      }
-
-      // native ft
-      const collectionAddress = helper.ethAddress.fromCollectionId(0);
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
-      return {collectionAddress, contract};
-    }
-
-    [
-      {mode: 'ft' as const},
-      {mode: 'native ft' as const},
-    ].map(testCase => {
-      itEth(`Allow nest [${testCase.mode}]`, async ({helper}) => {
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner);
-        const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode);
-
-        const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner});
-        const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
-        const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId);
-
-        await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner});
-        expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('10');
-      });
-    });
-
-    [
-      {mode: 'ft' as const},
-      {mode: 'native ft' as const},
-    ].map(testCase => {
-      itEth(`Allow partial/full unnest [${testCase.mode}]`, async ({helper}) => {
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner);
-        const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode);
-
-        const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner});
-        const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
-        const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId);
-
-        await ftContract.methods.transfer(targetTokenAddress, 10n).send({from: owner});
-
-        await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner});
-        expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('5');
-
-        await ftContract.methods.transferFrom(targetTokenAddress, owner, 5n).send({from: owner});
-        expect(await ftContract.methods.balanceOf(targetTokenAddress).call({from: owner})).to.be.equal('0');
-      });
-    });
-
-    [
-      {mode: 'ft' as const},
-      {mode: 'native ft' as const},
-    ].map(testCase => {
-      itEth(`Disallow nest into collection without nesting permission [${testCase.mode}] (except for native fungible collection)`, async ({helper}) => {
-        const owner = await helper.eth.createAccountWithBalance(donor);
-        const {collectionId: targetCollectionId, contract: targetContract} = await createNestingCollection(helper, owner);
-        await targetContract.methods.setCollectionNesting([false, false, []]).send({from: owner});
-
-        const {contract: ftContract} = await createFungibleCollection(helper, owner, testCase.mode);
-
-        const mintingTargetTokenIdResult = await targetContract.methods.mint(owner).send({from: owner});
-        const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;
-        const targetTokenAddress = helper.ethAddress.fromTokenId(targetCollectionId, targetTokenId);
-
-        if(testCase.mode === 'ft') {
-          await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');
-        } else {
-          await expect(ftContract.methods.transfer(targetTokenAddress, 10n).call({from: owner})).to.be.not.rejected;
-        }
-      });
-    });
-  });
-});
deletedjs-packages/tests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/nonFungible.test.ts
+++ /dev/null
@@ -1,1127 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {itEth, usingEthPlaygrounds, expect} from './util/index.js';
-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/src/types.js';
-import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js';
-
-describe('Check ERC721 token URI for NFT', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    const result = await contract.methods.mint(receiver).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-    expect(tokenId).to.be.equal('1');
-
-    if(propertyKey && propertyValue) {
-      // Set URL or suffix
-      await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();
-    }
-
-    const event = result.events.Transfer;
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.tokenId).to.be.equal(tokenId);
-
-    return {contract, nextTokenId: tokenId};
-  }
-
-  itEth('Empty tokenURI', async ({helper}) => {
-    const {contract, nextTokenId} = await setup(helper, '');
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
-  });
-
-  itEth('TokenURI from url', async ({helper}) => {
-    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
-  });
-
-  itEth('TokenURI from baseURI', async ({helper}) => {
-    const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');
-  });
-
-  itEth('TokenURI from baseURI + suffix', async ({helper}) => {
-    const suffix = '/some/suffix';
-    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
-  });
-});
-
-describe('NFT: Plain calls', () => {
-  let donor: IKeyringPair;
-  let minter: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  // TODO combine all minting tests in one place
-  [
-    'substrate' as const,
-    'ethereum' as const,
-  ].map(testCase => {
-    itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
-      const collectionAdmin = await helper.eth.createAccountWithBalance(donor);
-
-      const receiverEth = helper.eth.createAccount();
-      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-      const receiverSub = bob;
-      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
-
-      // const receiverCross = helper.ethCrossAccount.fromKeyringPair(bob);
-      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
-      const permissions: ITokenPropertyPermission[] = properties
-        .map(p => ({
-          key: p.key, permission: {
-            tokenOwner: false,
-            collectionAdmin: true,
-            mutable: false,
-          },
-        }));
-
-      const collection = await helper.nft.mintCollection(minter, {
-        tokenPrefix: 'ethp',
-        tokenPropertyPermissions: permissions,
-      });
-      await collection.addAdmin(minter, {Ethereum: collectionAdmin});
-
-      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', collectionAdmin, true);
-      let expectedTokenId = await contract.methods.nextTokenId().call();
-      let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();
-      let tokenId = result.events.Transfer.returnValues.tokenId;
-      expect(tokenId).to.be.equal(expectedTokenId);
-
-      let event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
-      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
-
-      expectedTokenId = await contract.methods.nextTokenId().call();
-      result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();
-      event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
-      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
-
-      tokenId = result.events.Transfer.returnValues.tokenId;
-
-      expect(tokenId).to.be.equal(expectedTokenId);
-
-      expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
-        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
-
-      expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
-        .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
-    });
-  });
-
-  itEth('Non-owner and non admin cannot mintCross', async ({helper}) => {
-    const nonOwner = await helper.eth.createAccountWithBalance(donor);
-    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
-
-    const collection = await helper.nft.mintCollection(minter);
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    await expect(collectionEvm.methods.mintCross(nonOwnerCross, []).call({from: nonOwner}))
-      .to.be.rejectedWith('PublicMintingNotAllowed');
-  });
-
-  //TODO: CORE-302 add eth methods
-  itEth.skip('Can perform mintBulk()', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(minter);
-    await collection.addAdmin(minter, {Ethereum: caller});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
-    {
-      const bulkSize = 3;
-      const nextTokenId = await contract.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-      const result = await contract.methods.mintBulkWithTokenURI(
-        receiver,
-        Array.from({length: bulkSize}, (_, i) => (
-          [+nextTokenId + i, `Test URI ${i}`]
-        )),
-      ).send({from: caller});
-
-      const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
-      for(let i = 0; i < bulkSize; i++) {
-        const event = events[i];
-        expect(event.address).to.equal(collectionAddress);
-        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-        expect(event.returnValues.to).to.equal(receiver);
-        expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
-
-        expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);
-      }
-    }
-  });
-
-  itEth('Can perform mintBulkCross()', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const callerCross = helper.ethCrossAccount.fromAddress(caller);
-    const receiver = helper.eth.createAccount();
-    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
-
-    const permissions = [
-      {code: TokenPermissionField.Mutable, value: true},
-      {code: TokenPermissionField.TokenOwner, value: true},
-      {code: TokenPermissionField.CollectionAdmin, value: true},
-    ];
-    const {collectionAddress} = await helper.eth.createCollection(
-      caller,
-      {
-        ...CREATE_COLLECTION_DATA_DEFAULTS,
-        name: 'A',
-        description: 'B',
-        tokenPrefix: 'C',
-        collectionMode: 'nft',
-        adminList: [callerCross],
-        tokenPropertyPermissions: [
-          {key: 'key_0_0', permissions},
-          {key: 'key_1_0', permissions},
-          {key: 'key_1_1', permissions},
-          {key: 'key_2_0', permissions},
-          {key: 'key_2_1', permissions},
-          {key: 'key_2_2', permissions},
-        ],
-      },
-    ).send();
-
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
-    {
-      const nextTokenId = await contract.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-      const result = await contract.methods.mintBulkCross([
-        {
-          owner: receiverCross,
-          properties: [
-            {key: 'key_0_0', value: Buffer.from('value_0_0')},
-          ],
-        },
-        {
-          owner: receiverCross,
-          properties: [
-            {key: 'key_1_0', value: Buffer.from('value_1_0')},
-            {key: 'key_1_1', value: Buffer.from('value_1_1')},
-          ],
-        },
-        {
-          owner: receiverCross,
-          properties: [
-            {key: 'key_2_0', value: Buffer.from('value_2_0')},
-            {key: 'key_2_1', value: Buffer.from('value_2_1')},
-            {key: 'key_2_2', value: Buffer.from('value_2_2')},
-          ],
-        },
-      ]).send({from: caller});
-      const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
-      const bulkSize = 3;
-      for(let i = 0; i < bulkSize; i++) {
-        const event = events[i];
-        expect(event.address).to.equal(collectionAddress);
-        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-        expect(event.returnValues.to).to.equal(receiver);
-        expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
-      }
-
-      const properties = [
-        await contract.methods.properties(+nextTokenId, []).call(),
-        await contract.methods.properties(+nextTokenId + 1, []).call(),
-        await contract.methods.properties(+nextTokenId + 2, []).call(),
-      ];
-      expect(properties).to.be.deep.equal([
-        [
-          ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],
-        ],
-        [
-          ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],
-          ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],
-        ],
-        [
-          ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
-          ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
-          ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
-        ],
-      ]);
-    }
-  });
-
-  itEth('Can perform burn()', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-
-    const collection = await helper.nft.mintCollection(minter, {});
-    const {tokenId} = await collection.mintToken(minter, {Ethereum: caller});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
-
-    {
-      const result = await contract.methods.burn(tokenId).send({from: caller});
-
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(caller);
-      expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
-    }
-  });
-
-  itEth('Can perform approve()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(minter, {});
-    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    {
-      const badTokenId = await contract.methods.nextTokenId().call() + 1;
-      await expect(contract.methods.getApproved(badTokenId).call()).to.be.rejectedWith('revert TokenNotFound');
-    }
-    {
-      const approved = await contract.methods.getApproved(tokenId).call();
-      expect(approved).to.be.equal('0x0000000000000000000000000000000000000000');
-    }
-    {
-      const result = await contract.methods.approve(spender, tokenId).send({from: owner});
-
-      const event = result.events.Approval;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.approved).to.be.equal(spender);
-      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
-    }
-    {
-      const approved = await contract.methods.getApproved(tokenId).call();
-      expect(approved).to.be.equal(spender);
-    }
-  });
-
-  itEth('Can perform setApprovalForAll()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const operator = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(minter, {});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();
-    expect(approvedBefore).to.be.equal(false);
-
-    {
-      const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});
-
-      expect(result.events.ApprovalForAll).to.be.like({
-        address: collectionAddress,
-        event: 'ApprovalForAll',
-        returnValues: {
-          owner,
-          operator,
-          approved: true,
-        },
-      });
-
-      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
-      expect(approvedAfter).to.be.equal(true);
-    }
-
-    {
-      const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});
-
-      expect(result.events.ApprovalForAll).to.be.like({
-        address: collectionAddress,
-        event: 'ApprovalForAll',
-        returnValues: {
-          owner,
-          operator,
-          approved: false,
-        },
-      });
-
-      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
-      expect(approvedAfter).to.be.equal(false);
-    }
-  });
-
-  itEth('Can perform burn with ApprovalForAll', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const operator = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft');
-
-    {
-      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});
-      const events = result.events.Transfer;
-
-      expect(events).to.be.like({
-        address,
-        event: 'Transfer',
-        returnValues: {
-          from: owner,
-          to: '0x0000000000000000000000000000000000000000',
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await helper.nft.doesTokenExist(collection.collectionId, token.tokenId)).to.be.false;
-  });
-
-  itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const operator = await helper.eth.createAccountWithBalance(donor);
-    const receiver = charlie;
-
-    const token = await collection.mintToken(minter, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft');
-
-    {
-      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
-      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});
-      const event = result.events.Transfer;
-      expect(event).to.be.like({
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: owner,
-          to: helper.address.substrateToEth(receiver.address),
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
-  });
-
-  itEth('Can perform burnFromCross()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const ownerSub = bob;
-    const ownerCrossSub = helper.ethCrossAccount.fromKeyringPair(ownerSub);
-    const ownerEth = await helper.eth.createAccountWithBalance(donor);
-    const ownerCrossEth = helper.ethCrossAccount.fromAddress(ownerEth);
-
-    const burnerEth = await helper.eth.createAccountWithBalance(donor);
-    const burnerCrossEth = helper.ethCrossAccount.fromAddress(burnerEth);
-
-    const token1 = await collection.mintToken(minter, {Substrate: ownerSub.address});
-    const token2 = await collection.mintToken(minter, {Ethereum: ownerEth});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    // Approve tokens from substrate and ethereum:
-    await token1.approve(ownerSub, {Ethereum: burnerEth});
-    await collectionEvm.methods.approveCross(burnerCrossEth, token2.tokenId).send({from: ownerEth});
-
-    // can burnFromCross:
-    const result1 = await collectionEvm.methods.burnFromCross(ownerCrossSub, token1.tokenId).send({from: burnerEth});
-    const result2 = await collectionEvm.methods.burnFromCross(ownerCrossEth, token2.tokenId).send({from: burnerEth});
-    const events1 = result1.events.Transfer;
-    const events2 = result2.events.Transfer;
-
-    // Check events for burnFromCross (substrate and ethereum):
-    [
-      [events1, token1, helper.address.substrateToEth(ownerSub.address)],
-      [events2, token2, ownerEth],
-    ].map(burnData => {
-      expect(burnData[0]).to.be.like({
-        address: collectionAddress,
-        event: 'Transfer',
-        returnValues: {
-          from: burnData[2],
-          to: '0x0000000000000000000000000000000000000000',
-          tokenId: burnData[1].tokenId.toString(),
-        },
-      });
-    });
-
-    expect(await token1.doesExist()).to.be.false;
-    expect(await token2.doesExist()).to.be.false;
-  });
-
-  // TODO combine all approve tests in one place
-  itEth('Can perform approveCross()', async ({helper}) => {
-    // arrange: create accounts
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-    const receiverSub = charlie;
-    const recieverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
-    const receiverEth = await helper.eth.createAccountWithBalance(donor);
-    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-
-    // arrange: create collection and tokens:
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const token1 = await collection.mintToken(minter, {Ethereum: owner});
-    const token2 = await collection.mintToken(minter, {Ethereum: owner});
-
-    const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
-
-    // Can approveCross substrate and ethereum address:
-    const resultSub = await collectionEvm.methods.approveCross(recieverCrossSub, token1.tokenId).send({from: owner});
-    const resultEth = await collectionEvm.methods.approveCross(receiverCrossEth, token2.tokenId).send({from: owner});
-    const eventSub = resultSub.events.Approval;
-    const eventEth = resultEth.events.Approval;
-    expect(eventSub).to.be.like({
-      address: helper.ethAddress.fromCollectionId(collection.collectionId),
-      event: 'Approval',
-      returnValues: {
-        owner,
-        approved: helper.address.substrateToEth(receiverSub.address),
-        tokenId: token1.tokenId.toString(),
-      },
-    });
-    expect(eventEth).to.be.like({
-      address: helper.ethAddress.fromCollectionId(collection.collectionId),
-      event: 'Approval',
-      returnValues: {
-        owner,
-        approved: receiverEth,
-        tokenId: token2.tokenId.toString(),
-      },
-    });
-
-    // Substrate address can transferFrom approved tokens:
-    await helper.nft.transferTokenFrom(receiverSub, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiverSub.address});
-    expect(await helper.nft.getTokenOwner(collection.collectionId, token1.tokenId)).to.deep.eq({Substrate: receiverSub.address});
-    // Ethereum address can transferFromCross approved tokens:
-    await collectionEvm.methods.transferFromCross(ownerCross, receiverCrossEth, token2.tokenId).send({from: receiverEth});
-    expect(await helper.nft.getTokenOwner(collection.collectionId, token2.tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});
-  });
-
-  itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
-    const nonOwner = await helper.eth.createAccountWithBalance(donor);
-    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
-    const token = await collection.mintToken(minter, {Ethereum: owner});
-
-    await expect(collectionEvm.methods.approveCross(nonOwnerCross, token.tokenId).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
-  });
-
-  itEth('Can reaffirm approved address', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const ownerCrossEth = helper.ethCrossAccount.fromAddress(owner);
-    const [receiver1, receiver2] = await helper.arrange.createAccounts([100n, 100n], donor);
-    const receiver1Cross = helper.ethCrossAccount.fromKeyringPair(receiver1);
-    const receiver2Cross = helper.ethCrossAccount.fromKeyringPair(receiver2);
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const token1 = await collection.mintToken(minter, {Ethereum: owner});
-    const token2 = await collection.mintToken(minter, {Ethereum: owner});
-    const collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(collection.collectionId), 'nft');
-
-    // Can approve and reaffirm approved address:
-    await collectionEvm.methods.approveCross(receiver1Cross, token1.tokenId).send({from: owner});
-    await collectionEvm.methods.approveCross(receiver2Cross, token1.tokenId).send({from: owner});
-
-    // receiver1 cannot transferFrom:
-    await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;
-    // receiver2 can transferFrom:
-    await helper.nft.transferTokenFrom(receiver2, collection.collectionId, token1.tokenId, {Ethereum: owner}, {Substrate: receiver2.address});
-
-    // can set approved address to self address to remove approval:
-    await collectionEvm.methods.approveCross(receiver1Cross, token2.tokenId).send({from: owner});
-    await collectionEvm.methods.approveCross(ownerCrossEth, token2.tokenId).send({from: owner});
-
-    // receiver1 cannot transfer token anymore:
-    await expect(helper.nft.transferTokenFrom(receiver1, collection.collectionId, token2.tokenId, {Ethereum: owner}, {Substrate: receiver1.address})).to.be.rejected;
-  });
-
-  itEth('Can perform transferFrom()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(minter, {});
-    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    await contract.methods.approve(spender, tokenId).send({from: owner});
-
-    {
-      const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: spender});
-
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(owner);
-      expect(event.returnValues.to).to.be.equal(receiver);
-      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(1);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(owner).call();
-      expect(+balance).to.equal(0);
-    }
-  });
-
-  itEth('Can perform transferFromCross()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const [owner, receiver] = await helper.arrange.createAccounts([100n, 100n], donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, {Substrate: owner.address});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft');
-
-    await token.approve(owner, {Ethereum: spender});
-
-    {
-      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
-      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
-      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
-      const event = result.events.Transfer;
-      expect(event).to.be.like({
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: helper.address.substrateToEth(owner.address),
-          to: helper.address.substrateToEth(receiver.address),
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
-  });
-
-  itEth('Can perform transfer()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {});
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    {
-      const result = await contract.methods.transfer(receiver, tokenId).send({from: owner});
-
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(owner);
-      expect(event.returnValues.to).to.be.equal(receiver);
-      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(owner).call();
-      expect(+balance).to.equal(0);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(1);
-    }
-  });
-
-  itEth('Can perform transferCross()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {});
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiverEth = await helper.eth.createAccountWithBalance(donor);
-    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
-
-    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-
-    {
-      // Can transferCross to ethereum address:
-      const result = await collectionEvm.methods.transferCross(receiverCrossEth, tokenId).send({from: owner});
-      // Check events:
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(owner);
-      expect(event.returnValues.to).to.be.equal(receiverEth);
-      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
-
-      // owner has balance = 0:
-      const ownerBalance = await collectionEvm.methods.balanceOf(owner).call();
-      expect(+ownerBalance).to.equal(0);
-      // receiver owns token:
-      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
-      expect(+receiverBalance).to.equal(1);
-      expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId)).to.deep.eq({Ethereum: receiverEth.toLowerCase()});
-    }
-
-    {
-      // Can transferCross to substrate address:
-      const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, tokenId).send({from: receiverEth});
-      // Check events:
-      const event = substrateResult.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(receiverEth);
-      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));
-      expect(event.returnValues.tokenId).to.be.equal(`${tokenId}`);
-
-      // owner has balance = 0:
-      const ownerBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
-      expect(+ownerBalance).to.equal(0);
-      // receiver owns token:
-      const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});
-      expect(receiverBalance).to.contain(tokenId);
-    }
-  });
-
-  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-    const tokenOwner = await helper.eth.createAccountWithBalance(donor);
-    const receiverSub = minter;
-    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
-
-    const collection = await helper.nft.mintCollection(minter, {});
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', sender);
-
-    await collection.mintToken(minter, {Ethereum: sender});
-    const nonSendersToken = await collection.mintToken(minter, {Ethereum: tokenOwner});
-
-    // Cannot transferCross someone else's token:
-    const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;
-    await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;
-    // Cannot transfer token if it does not exist:
-    await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;
-  }));
-
-  itEth('Check balanceOfCross()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {});
-    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
-
-    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
-
-    for(let i = 1; i < 10; i++) {
-      await collection.mintToken(minter, {Ethereum: owner.eth});
-      expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
-    }
-  });
-
-  itEth('Check ownerOfCross()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {});
-    let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
-    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner.eth});
-
-    for(let i = 1n; i < 10n; i++) {
-      const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
-      expect(ownerCross.eth).to.be.eq(owner.eth);
-      expect(ownerCross.sub).to.be.eq(owner.sub);
-
-      const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-      await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
-      owner = newOwner;
-    }
-  });
-});
-
-describe('NFT: Fees', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    });
-  });
-
-  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(alice, {});
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
-
-    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
-
-    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, tokenId).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-
-  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const collection = await helper.nft.mintCollection(alice, {});
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
-
-    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
-
-    await contract.methods.approve(spender, tokenId).send({from: owner});
-
-    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, tokenId).send({from: spender}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-
-  itEth('Can perform transferFromCross()', async ({helper}) => {
-    const collectionMinter = alice;
-    const owner = bob;
-    const receiver = charlie;
-    const collection = await helper.nft.mintCollection(collectionMinter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(collectionMinter, {Substrate: owner.address});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft');
-
-    await token.approve(owner, {Ethereum: spender});
-
-    {
-      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
-      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
-      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
-      const event = result.events.Transfer;
-      expect(event).to.be.like({
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: helper.address.substrateToEth(owner.address),
-          to: helper.address.substrateToEth(receiver.address),
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await token.getOwner()).to.be.like({Substrate: receiver.address});
-  });
-
-  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(alice, {});
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
-
-    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', owner);
-
-    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, tokenId).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-});
-
-describe('NFT: Substrate calls', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([20n], donor);
-    });
-  });
-
-  itEth('Events emitted for mint()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    const {tokenId} = await collection.mintToken(alice);
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.event).to.be.equal('Transfer');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.tokenId).to.be.equal(tokenId.toString());
-  });
-
-  itEth('Events emitted for burn()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice);
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await token.burn(alice);
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.event).to.be.equal('Transfer');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
-  });
-
-  itEth('Events emitted for approve()', async ({helper}) => {
-    const receiver = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice);
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await token.approve(alice, {Ethereum: receiver});
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.event).to.be.equal('Approval');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.approved).to.be.equal(receiver);
-    expect(event.returnValues.tokenId).to.be.equal(token.tokenId.toString());
-  });
-
-  itEth('Events emitted for transferFrom()', async ({helper}) => {
-    const [bob] = await helper.arrange.createAccounts([10n], donor);
-    const receiver = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice);
-    await token.approve(alice, {Substrate: bob.address});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver});
-
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
-  });
-
-  itEth('Events emitted for transfer()', async ({helper}) => {
-    const receiver = helper.eth.createAccount();
-
-    const collection = await helper.nft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice);
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await token.transfer(alice, {Ethereum: receiver});
-
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
-  });
-});
-
-describe('Common metadata', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([20n], donor);
-    });
-  });
-
-  itEth('Returns collection name', async ({helper}) => {
-    const caller = helper.eth.createAccount();
-    const tokenPropertyPermissions = [{
-      key: 'URI',
-      permission: {
-        mutable: true,
-        collectionAdmin: true,
-        tokenOwner: false,
-      },
-    }];
-    const collection = await helper.nft.mintCollection(
-      alice,
-      {
-        name: 'oh River',
-        tokenPrefix: 'CHANGE',
-        properties: [{key: 'ERC721Metadata', value: '1'}],
-        tokenPropertyPermissions,
-      },
-    );
-
-    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
-    const name = await contract.methods.name().call();
-    expect(name).to.equal('oh River');
-  });
-
-  itEth('Returns symbol name', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const tokenPropertyPermissions = [{
-      key: 'URI',
-      permission: {
-        mutable: true,
-        collectionAdmin: true,
-        tokenOwner: false,
-      },
-    }];
-    const collection = await helper.nft.mintCollection(
-      alice,
-      {
-        name: 'oh River',
-        tokenPrefix: 'CHANGE',
-        properties: [{key: 'ERC721Metadata', value: '1'}],
-        tokenPropertyPermissions,
-      },
-    );
-
-    const contract = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller);
-    const symbol = await contract.methods.symbol().call();
-    expect(symbol).to.equal('CHANGE');
-  });
-});
-
-describe('Negative tests', () => {
-  let donor: IKeyringPair;
-  let minter: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itEth('[negative] Cant perform burn without approval', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft');
-
-    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
-
-    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
-    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
-
-    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
-  });
-
-  itEth('[negative] Cant perform transfer without approval', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const receiver = alice;
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft');
-
-    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-    const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
-
-    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
-
-    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
-    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
-
-    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
-  });
-});
deletedjs-packages/tests/src/eth/payable.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/payable.test.ts
+++ /dev/null
@@ -1,273 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-
-import {itEth, expect, usingEthPlaygrounds} from './util/index.js';
-import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-import {makeNames} from '../util/index.js';
-
-const {dirname} = makeNames(import.meta.url);
-
-describe('EVM payable contracts', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Evm contract can receive wei from eth account', async ({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const contract = await helper.eth.deployCollectorContract(deployer);
-
-    const web3 = helper.getWeb3();
-
-    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', gas: helper.eth.DEFAULT_GAS});
-
-    expect(await contract.methods.getCollected().call()).to.be.equal('10000');
-  });
-
-  itEth('Evm contract can receive wei from substrate account', async ({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const contract = await helper.eth.deployCollectorContract(deployer);
-    const [alice] = await helper.arrange.createAccounts([40n], donor);
-
-    const weiCount = '10000';
-
-    // Transaction fee/value will be payed from subToEth(sender) evm balance,
-    // which is backed by evmToAddress(subToEth(sender)) substrate balance
-    await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address), 5n);
-
-
-    await helper.eth.sendEVM(alice, contract.options.address, contract.methods.giveMoney().encodeABI(), weiCount);
-
-    expect(await contract.methods.getCollected().call()).to.be.equal(weiCount);
-  });
-
-  // We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible
-  itEth('Wei sent directly to backing storage of evm contract balance is unaccounted', async({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const contract = await helper.eth.deployCollectorContract(deployer);
-    const [alice] = await helper.arrange.createAccounts([10n], donor);
-
-    const weiCount = 10_000n;
-
-    await helper.eth.transferBalanceFromSubstrate(alice, contract.options.address, weiCount, false);
-
-    expect(await contract.methods.getUnaccounted().call()).to.be.equal(weiCount.toString());
-  });
-
-  itEth('Balance can be retrieved from evm contract', async({helper}) => {
-    const FEE_BALANCE = 10n * helper.balance.getOneTokenNominal();
-    const CONTRACT_BALANCE = 1n * helper.balance.getOneTokenNominal();
-
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const contract = await helper.eth.deployCollectorContract(deployer);
-    const [alice] = await helper.arrange.createAccounts([20n], donor);
-
-    const web3 = helper.getWeb3();
-
-    await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: CONTRACT_BALANCE.toString(), gas: helper.eth.DEFAULT_GAS});
-
-    const [receiver] = await helper.arrange.createAccounts([0n], donor);
-
-    // First receive balance on eth balance of bob
-    {
-      const ethReceiver = helper.address.substrateToEth(receiver.address);
-      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal('0');
-      await contract.methods.withdraw(ethReceiver).send({from: deployer});
-      expect(await web3.eth.getBalance(ethReceiver)).to.be.equal(CONTRACT_BALANCE.toString());
-    }
-
-    // Some balance is required to pay fee for evm.withdraw call
-    await helper.balance.transferToSubstrate(alice, receiver.address, FEE_BALANCE);
-    // await transferBalanceExpectSuccess(api, alice, receiver.address, FEE_BALANCE.toString());
-
-    // Withdraw balance from eth to substrate
-    {
-      const initialReceiverBalance = await helper.balance.getSubstrate(receiver.address);
-      await helper.executeExtrinsic(receiver, 'api.tx.evm.withdraw', [helper.address.substrateToEth(receiver.address), CONTRACT_BALANCE.toString()], true);
-      const finalReceiverBalance = await helper.balance.getSubstrate(receiver.address);
-
-      expect(finalReceiverBalance > initialReceiverBalance).to.be.true;
-    }
-  });
-});
-
-describe('EVM transaction fees', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Fee is withdrawn from the user', async({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const contract = await helper.eth.deployFlipper(deployer);
-
-    const initialCallerBalance = await helper.balance.getEthereum(caller);
-    await contract.methods.flip().send({from: caller});
-    const finalCallerBalance = await helper.balance.getEthereum(caller);
-    expect(finalCallerBalance < initialCallerBalance).to.be.true;
-  });
-
-  itEth('Fee for nested calls is withdrawn from the user', async({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const contract = await deployProxyContract(helper, deployer);
-
-    const initialCallerBalance = await helper.balance.getEthereum(caller);
-    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
-    await contract.methods.flip().send({from: caller});
-    const finalCallerBalance = await helper.balance.getEthereum(caller);
-    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
-    expect(finalCallerBalance < initialCallerBalance).to.be.true;
-    expect(finalContractBalance == initialContractBalance).to.be.true;
-  });
-
-  itEth('Fee for nested calls to native methods is withdrawn from the user', async({helper}) => {
-    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();
-
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const contract = await deployProxyContract(helper, deployer);
-
-    const collectionAddress = (await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)})).events.CollectionCreated.returnValues.collection;
-    const initialCallerBalance = await helper.balance.getEthereum(caller);
-    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
-    await contract.methods.mintNftToken(collectionAddress).send({from: caller});
-    const finalCallerBalance = await helper.balance.getEthereum(caller);
-    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
-    expect(finalCallerBalance < initialCallerBalance).to.be.true;
-    expect(finalContractBalance == initialContractBalance).to.be.true;
-  });
-
-  itEth('Fee for nested calls to create*Collection methods is withdrawn from the user and from the contract', async({helper}) => {
-    const CONTRACT_BALANCE = 2n * helper.balance.getOneTokenNominal();
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const contract = await deployProxyContract(helper, deployer);
-
-    const initialCallerBalance = await helper.balance.getEthereum(caller);
-    const initialContractBalance = await helper.balance.getEthereum(contract.options.address);
-    await contract.methods.createNFTCollection().send({from: caller, value: Number(CONTRACT_BALANCE)});
-    const finalCallerBalance = await helper.balance.getEthereum(caller);
-    const finalContractBalance = await helper.balance.getEthereum(contract.options.address);
-    expect(finalCallerBalance < initialCallerBalance).to.be.true;
-    expect(finalContractBalance == initialContractBalance).to.be.true;
-  });
-
-  itEth('Negative test: call createNFTCollection with wrong fee', async({helper}) => {
-    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();
-    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);
-
-    await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-    await expect(collectionHelper.methods.createNFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-  });
-
-  itEth('Negative test: call createRFTCollection with wrong fee', async({helper}) => {
-    const SMALL_FEE = 1n * helper.balance.getOneTokenNominal();
-    const BIG_FEE = 3n * helper.balance.getOneTokenNominal();
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(caller);
-
-    await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(SMALL_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-    await expect(collectionHelper.methods.createRFTCollection('A', 'B', 'C').call({value: Number(BIG_FEE)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
-  });
-
-  itEth('Get collection creation fee', async({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    expect(await helper.eth.getCollectionCreationFee(deployer)).to.be.equal(String(2n * helper.balance.getOneTokenNominal()));
-  });
-
-  async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {
-    return await helper.ethContract.deployByCode(
-      deployer,
-      'ProxyContract',
-      `
-      // SPDX-License-Identifier: UNLICENSED
-      pragma solidity ^0.8.6;
-
-      import {CollectionHelpers} from "../api/CollectionHelpers.sol";
-      import {UniqueNFT} from "../api/UniqueNFT.sol";
-
-      error Value(uint256 value);
-
-      contract ProxyContract {
-        bool value = false;
-        address innerContract;
-
-        event CollectionCreated(address collection);
-        event TokenMinted(uint256 tokenId);
-
-        receive() external payable {}
-
-        constructor() {
-          innerContract = address(new InnerContract());
-        }
-
-        function flip() public {
-          value = !value;
-          InnerContract(innerContract).flip();
-        }
-
-        function createNFTCollection() external payable {
-          address collectionHelpers = 0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F;
-		      address nftCollection = CollectionHelpers(collectionHelpers).createNFTCollection{value: msg.value}("A", "B", "C");
-          emit CollectionCreated(nftCollection);
-        }
-
-        function mintNftToken(address collectionAddress) external  {
-          UniqueNFT collection = UniqueNFT(collectionAddress);
-          uint256 tokenId = collection.mint(msg.sender);
-          emit TokenMinted(tokenId);
-        }
-
-        function getValue() external view returns (bool) {
-          return InnerContract(innerContract).getValue();
-        }
-      }
-
-      contract InnerContract {
-        bool value = false;
-        function flip() external {
-          value = !value;
-        }
-        function getValue() external view returns (bool) {
-          return value;
-        }
-      }
-      `,
-      [
-        {
-          solPath: 'api/CollectionHelpers.sol',
-          fsPath: `${dirname}/api/CollectionHelpers.sol`,
-        },
-        {
-          solPath: 'api/UniqueNFT.sol',
-          fsPath: `${dirname}/api/UniqueNFT.sol`,
-        },
-      ],
-    );
-  }
-});
deletedjs-packages/tests/src/eth/precompile.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/precompile.test.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-
-import {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-
-describe('Precompiles', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('ecrecover is supported', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const ecrecoverCompiledСontract = await helper.ethContract.compile(
-      'ECRECOVER',
-      `
-      // SPDX-License-Identifier: MIT
-      pragma solidity ^0.8.17;
-
-      contract ECRECOVER{
-        address addressTest = 0x12Cb274aAD8251C875c0bf6872b67d9983E53fDd;
-        bytes32 msgHash1 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d50;
-        bytes32 msgHash2 = 0xc51dac836bc7841a01c4b631fa620904fc8724d7f9f1d3c420f0e02adf229d51;
-        uint8 v = 0x1b;
-        bytes32 r = 0x44287513919034a471a7dc2b2ed121f95984ae23b20f9637ba8dff471b6719ef;
-        bytes32 s = 0x7d7dc30309a3baffbfd9342b97d0e804092c0aeb5821319aa732bc09146eafb4;
-
-
-        function verifyValid() public view returns(bool) {
-           // Use ECRECOVER to verify address
-           return ecrecover(msgHash1, v, r, s) == (addressTest);
-        }
-
-        function verifyInvalid() public view returns(bool) {
-          // Use ECRECOVER to verify address
-          return ecrecover(msgHash2, v, r, s) == (addressTest);
-        }
-      }
-      `,
-    );
-
-    const ecrecoverСontract = await helper.ethContract.deployByAbi(owner, ecrecoverCompiledСontract.abi, ecrecoverCompiledСontract.object);
-    expect(await ecrecoverСontract.methods.verifyValid().call({from: owner})).to.be.true;
-    expect(await ecrecoverСontract.methods.verifyInvalid().call({from: owner})).to.be.false;
-  });
-
-  itEth('sr25519 is supported', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sr25519CompiledСontract = await helper.ethContract.compile(
-      'SR25519Contract',
-      `
-      // SPDX-License-Identifier: MIT
-      pragma solidity ^0.8.17;
-
-      /**
-       * @title SR25519 signature interface.
-       */
-      interface SR25519 {
-          /**
-           * @dev Verify signed message using SR25519 crypto.
-           * @return A boolean confirming whether the public key is signer for the message. 
-           */
-          function verify(
-            bytes32 public_key,
-            bytes calldata signature,
-            bytes calldata message
-          ) external view returns (bool);
-      }
-
-      contract SR25519Contract{
-        SR25519 public constant sr25519 = SR25519(0x0000000000000000000000000000000000005002);
-
-        bytes32 public_key = 0x96b2f9237ed0890fbeed891ebb81b91ac0d5d5b6e3afcdbc95df1b68d9f14036;
-        bytes signature = hex"4a5d733d7c568f2e88abf0467fd497f87c1be3e940ed897efdf9da72eaad394ef9918cb574ee99c54485775b17a0deaf46ff7a1f10346cea39fff0e4ede97689";
-        bytes message1 = hex"7372323535313920697320737570706f72746564";
-        bytes message2 = hex"7372323535313920697320737570706f7274656401";
-
-
-        function verifyValid() public view returns(bool) {
-          return sr25519.verify(public_key, signature, message1);
-        }
-
-        function verifyInvalid() public view returns(bool) {
-          return sr25519.verify(public_key, signature, message2);
-        }
-      }
-      `,
-    );
-
-    const sr25519Сontract = await helper.ethContract.deployByAbi(owner, sr25519CompiledСontract.abi, sr25519CompiledСontract.object);
-    expect(await sr25519Сontract.methods.verifyValid().call({from: owner})).to.be.true;
-    expect(await sr25519Сontract.methods.verifyInvalid().call({from: owner})).to.be.false;
-  });
-});
deletedjs-packages/tests/src/eth/proxy/UniqueFungibleProxy.abidiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/UniqueFungibleProxy.abi
+++ /dev/null
@@ -1 +0,0 @@
-[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"interfaceId","type":"uint32"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
deletedjs-packages/tests/src/eth/proxy/UniqueFungibleProxy.bindiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/UniqueFungibleProxy.bin
+++ /dev/null
@@ -1 +0,0 @@
-608060405234801561001057600080fd5b5060405161098038038061098083398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b6108ed806100936000396000f3fe608060405234801561001057600080fd5b506004361061009e5760003560e01c806370a082311161006657806370a082311461012757806395d89b411461013a578063a9059cbb14610142578063dd62ed3e14610155578063f4f4b5001461016857600080fd5b806306fdde03146100a3578063095ea7b3146100c157806318160ddd146100e457806323b872dd146100fa578063313ce5671461010d575b600080fd5b6100ab61017b565b6040516100b8919061083e565b60405180910390f35b6100d46100cf3660046106e3565b610200565b60405190151581526020016100b8565b6100ec61028f565b6040519081526020016100b8565b6100d46101083660046106a7565b610316565b6101156103ad565b60405160ff90911681526020016100b8565b6100ec610135366004610659565b610434565b6100ab6104b8565b6100d46101503660046106e3565b6104fc565b6100ec610163366004610674565b610536565b6100d46101763660046107f5565b6105bc565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde039260048082019391829003018186803b1580156101bf57600080fd5b505afa1580156101d3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526101fb919081019061072f565b905090565b6000805460405163095ea7b360e01b81526001600160a01b038581166004830152602482018590529091169063095ea7b3906044015b602060405180830381600087803b15801561025057600080fd5b505af1158015610264573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610288919061070d565b9392505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156102de57600080fd5b505afa1580156102f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb91906107dc565b600080546040516323b872dd60e01b81526001600160a01b038681166004830152858116602483015260448201859052909116906323b872dd90606401602060405180830381600087803b15801561036d57600080fd5b505af1158015610381573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103a5919061070d565b949350505050565b60008060009054906101000a90046001600160a01b03166001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b1580156103fc57600080fd5b505afa158015610410573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101fb919061081b565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240160206040518083038186803b15801561047a57600080fd5b505afa15801561048e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b291906107dc565b92915050565b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b419260048082019391829003018186803b1580156101bf57600080fd5b6000805460405163a9059cbb60e01b81526001600160a01b038581166004830152602482018590529091169063a9059cbb90604401610236565b60008054604051636eb1769f60e11b81526001600160a01b03858116600483015284811660248301529091169063dd62ed3e9060440160206040518083038186803b15801561058457600080fd5b505afa158015610598573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061028891906107dc565b6000805460405162f4f4b560e81b815263ffffffff841660048201526001600160a01b039091169063f4f4b5009060240160206040518083038186803b15801561060557600080fd5b505afa158015610619573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104b2919061070d565b80356001600160a01b038116811461065457600080fd5b919050565b60006020828403121561066b57600080fd5b6102888261063d565b6000806040838503121561068757600080fd5b6106908361063d565b915061069e6020840161063d565b90509250929050565b6000806000606084860312156106bc57600080fd5b6106c58461063d565b92506106d36020850161063d565b9150604084013590509250925092565b600080604083850312156106f657600080fd5b6106ff8361063d565b946020939093013593505050565b60006020828403121561071f57600080fd5b8151801515811461028857600080fd5b60006020828403121561074157600080fd5b815167ffffffffffffffff8082111561075957600080fd5b818401915084601f83011261076d57600080fd5b81518181111561077f5761077f6108a1565b604051601f8201601f19908116603f011681019083821181831017156107a7576107a76108a1565b816040528281528760208487010111156107c057600080fd5b6107d1836020830160208801610871565b979650505050505050565b6000602082840312156107ee57600080fd5b5051919050565b60006020828403121561080757600080fd5b813563ffffffff8116811461028857600080fd5b60006020828403121561082d57600080fd5b815160ff8116811461028857600080fd5b602081526000825180602084015261085d816040850160208701610871565b601f01601f19169190910160400192915050565b60005b8381101561088c578181015183820152602001610874565b8381111561089b576000848401525b50505050565b634e487b7160e01b600052604160045260246000fdfea2646970667358221220c05e4cb7ab439b86c0b6ac8a84eec6a230b592fa63d1ab2e3a7f1474c27338f364736f6c63430008070033
\ No newline at end of file
deletedjs-packages/tests/src/eth/proxy/UniqueFungibleProxy.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/UniqueFungibleProxy.sol
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: OTHER
-
-pragma solidity >=0.8.0 <0.9.0;
-
-import "../api/UniqueFungible.sol";
-
-contract UniqueFungibleProxy is UniqueFungible {
-    UniqueFungible proxied;
-
-    constructor(address _proxied) UniqueFungible() {
-        proxied = UniqueFungible(_proxied);
-    }
-
-    function name() external view override returns (string memory) {
-        return proxied.name();
-    }
-
-    function symbol() external view override returns (string memory) {
-        return proxied.symbol();
-    }
-
-    function totalSupply() external view override returns (uint256) {
-        return proxied.totalSupply();
-    }
-
-    function supportsInterface(uint32 interfaceId)
-        external
-        view
-        override
-        returns (bool)
-    {
-        return proxied.supportsInterface(interfaceId);
-    }
-
-    function decimals() external view override returns (uint8) {
-        return proxied.decimals();
-    }
-
-    function balanceOf(address owner) external view override returns (uint256) {
-        return proxied.balanceOf(owner);
-    }
-
-    function transfer(address to, uint256 amount)
-        external
-        override
-        returns (bool)
-    {
-        return proxied.transfer(to, amount);
-    }
-
-    function transferFrom(
-        address from,
-        address to,
-        uint256 amount
-    ) external override returns (bool) {
-        return proxied.transferFrom(from, to, amount);
-    }
-
-    function approve(address spender, uint256 amount)
-        external
-        override
-        returns (bool)
-    {
-        return proxied.approve(spender, amount);
-    }
-
-    function allowance(address owner, address spender)
-        external
-        view
-        override
-        returns (uint256)
-    {
-        return proxied.allowance(owner, spender);
-    }
-}
deletedjs-packages/tests/src/eth/proxy/UniqueNFTProxy.abidiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/UniqueNFTProxy.abi
+++ /dev/null
@@ -1 +0,0 @@
-[{"inputs":[{"internalType":"address","name":"_proxied","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[],"name":"MintingFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"approved","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"}],"name":"deleteProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finishMinting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"}],"name":"mintBulk","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"components":[{"internalType":"uint256","name":"field_0","type":"uint256"},{"internalType":"string","name":"field_1","type":"string"}],"internalType":"struct Tuple0[]","name":"tokens","type":"tuple[]"}],"name":"mintBulkWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"tokenUri","type":"string"}],"name":"mintWithTokenURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintingFinished","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextTokenId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFromWithData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"key","type":"string"},{"internalType":"string","name":"value","type":"string"}],"name":"setProperty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]
\ No newline at end of file
deletedjs-packages/tests/src/eth/proxy/UniqueNFTProxy.bindiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/UniqueNFTProxy.bin
+++ /dev/null
@@ -1 +0,0 @@
-608060405234801561001057600080fd5b506040516116bb3803806116bb83398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b611628806100936000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80634f6ccce7116100f957806379cc679011610097578063a22cb46511610071578063a22cb46514610399578063a9059cbb146103ac578063c87b56dd146103bf578063e985e9c5146103d257600080fd5b806379cc6790146103765780637d64bcb41461038957806395d89b411461039157600080fd5b806362d9491f116100d357806362d9491f146103355780636352211e1461034857806370a082311461035b57806375794a3c1461036e57600080fd5b80634f6ccce7146102fc57806350bb4e7f1461030f57806360a116721461032257600080fd5b80632f745c591161016657806340c10f191161014057806340c10f19146102b057806342842e0e146102c357806342966c68146102d657806344a9945e146102e957600080fd5b80632f745c5914610277578063342419141461028a578063365430061461029d57600080fd5b8063081812fc116101a2578063081812fc1461020e578063095ea7b31461023957806318160ddd1461024e57806323b872dd1461026457600080fd5b806301ffc9a7146101c957806305d2035b146101f157806306fdde03146101f9575b600080fd5b6101dc6101d7366004610dca565b6103e5565b60405190151581526020015b60405180910390f35b6101dc610462565b6102016104df565b6040516101e89190610e4c565b61022161021c366004610e5f565b610550565b6040516001600160a01b0390911681526020016101e8565b61024c610247366004610e90565b6105bf565b005b61025661062a565b6040519081526020016101e8565b61024c610272366004610ebc565b6106a2565b610256610285366004610e90565b610716565b61024c610298366004610ff3565b610793565b6101dc6102ab36600461104c565b6107f8565b6101dc6102be366004610e90565b61086e565b61024c6102d1366004610ebc565b6108a8565b61024c6102e4366004610e5f565b6108e9565b6101dc6102f736600461114f565b61091a565b61025661030a366004610e5f565b61094d565b6101dc61031d3660046111f5565b6109bc565b61024c61033036600461124e565b610a3c565b61024c6103433660046112ce565b610aab565b610221610356366004610e5f565b610add565b610256610369366004611332565b610b0f565b610256610b42565b61024c610384366004610e90565b610b96565b6101dc610bcf565b610201610c25565b61024c6103a736600461135d565b610c6e565b61024c6103ba366004610e90565b610ca8565b6102016103cd366004610e5f565b610ce1565b6102216103e0366004611396565b610d53565b600080546040516301ffc9a760e01b81526001600160e01b0319841660048201526001600160a01b03909116906301ffc9a790602401602060405180830381865afa158015610438573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c91906113c4565b92915050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166305d2035b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da91906113c4565b905090565b60008054604080516306fdde0360e01b815290516060936001600160a01b03909316926306fdde0392600480820193918290030181865afa158015610528573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104da91908101906113e1565b6000805460405163020604bf60e21b8152600481018490526001600160a01b039091169063081812fc906024015b602060405180830381865afa15801561059b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611458565b60005460405163095ea7b360e01b81526001600160a01b038481166004830152602482018490529091169063095ea7b3906044015b600060405180830381600087803b15801561060e57600080fd5b505af1158015610622573d6000803e3d6000fd5b505050505050565b60008060009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104da9190611475565b6000546040516323b872dd60e01b81526001600160a01b038581166004830152848116602483015260448201849052909116906323b872dd906064015b600060405180830381600087803b1580156106f957600080fd5b505af115801561070d573d6000803e3d6000fd5b50505050505050565b60008054604051632f745c5960e01b81526001600160a01b0385811660048301526024820185905290911690632f745c5990604401602060405180830381865afa158015610768573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611475565b9392505050565b600054604051630d09064560e21b81526001600160a01b03909116906334241914906107c3908490600401610e4c565b600060405180830381600087803b1580156107dd57600080fd5b505af11580156107f1573d6000803e3d6000fd5b5050505050565b60008054604051631b2a180360e11b81526001600160a01b039091169063365430069061082b908690869060040161148e565b6020604051808303816000875af115801561084a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c91906113c4565b600080546040516340c10f1960e01b81526001600160a01b03858116600483015260248201859052909116906340c10f199060440161082b565b600054604051632142170760e11b81526001600160a01b038581166004830152848116602483015260448201849052909116906342842e0e906064016106df565b600054604051630852cd8d60e31b8152600481018390526001600160a01b03909116906342966c68906024016107c3565b60008054604051632254ca2f60e11b81526001600160a01b03909116906344a9945e9061082b9086908690600401611513565b60008054604051634f6ccce760e01b8152600481018490526001600160a01b0390911690634f6ccce7906024015b602060405180830381865afa158015610998573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045c9190611475565b600080546040516350bb4e7f60e01b81526001600160a01b03909116906350bb4e7f906109f190879087908790600401611569565b6020604051808303816000875af1158015610a10573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a3491906113c4565b949350505050565b6000546040516330508b3960e11b81526001600160a01b03909116906360a1167290610a72908790879087908790600401611590565b600060405180830381600087803b158015610a8c57600080fd5b505af1158015610aa0573d6000803e3d6000fd5b505050505b50505050565b6000546040516362d9491f60e01b81526001600160a01b03909116906362d9491f906105f490859085906004016115cd565b600080546040516331a9108f60e11b8152600481018490526001600160a01b0390911690636352211e9060240161057e565b600080546040516370a0823160e01b81526001600160a01b038481166004830152909116906370a082319060240161097b565b60008060009054906101000a90046001600160a01b03166001600160a01b03166375794a3c6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561067e573d6000803e3d6000fd5b60005460405163079cc67960e41b81526001600160a01b03848116600483015260248201849052909116906379cc6790906044016105f4565b60008060009054906101000a90046001600160a01b03166001600160a01b0316637d64bcb46040518163ffffffff1660e01b81526004016020604051808303816000875af11580156104b6573d6000803e3d6000fd5b60008054604080516395d89b4160e01b815290516060936001600160a01b03909316926395d89b4192600480820193918290030181865afa158015610528573d6000803e3d6000fd5b60005460405163a22cb46560e01b81526001600160a01b03848116600483015283151560248301529091169063a22cb465906044016105f4565b60005460405163a9059cbb60e01b81526001600160a01b038481166004830152602482018490529091169063a9059cbb906044016105f4565b60005460405163c87b56dd60e01b8152600481018390526060916001600160a01b03169063c87b56dd90602401600060405180830381865afa158015610d2b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261045c91908101906113e1565b6000805460405163e985e9c560e01b81526001600160a01b03858116600483015284811660248301529091169063e985e9c590604401602060405180830381865afa158015610da6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078c9190611458565b600060208284031215610ddc57600080fd5b81356001600160e01b03198116811461078c57600080fd5b60005b83811015610e0f578181015183820152602001610df7565b83811115610aa55750506000910152565b60008151808452610e38816020860160208601610df4565b601f01601f19169290920160200192915050565b60208152600061078c6020830184610e20565b600060208284031215610e7157600080fd5b5035919050565b6001600160a01b0381168114610e8d57600080fd5b50565b60008060408385031215610ea357600080fd5b8235610eae81610e78565b946020939093013593505050565b600080600060608486031215610ed157600080fd5b8335610edc81610e78565b92506020840135610eec81610e78565b929592945050506040919091013590565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610f3657610f36610efd565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610f6557610f65610efd565b604052919050565b600067ffffffffffffffff821115610f8757610f87610efd565b50601f01601f191660200190565b6000610fa8610fa384610f6d565b610f3c565b9050828152838383011115610fbc57600080fd5b828260208301376000602084830101529392505050565b600082601f830112610fe457600080fd5b61078c83833560208501610f95565b60006020828403121561100557600080fd5b813567ffffffffffffffff81111561101c57600080fd5b610a3484828501610fd3565b600067ffffffffffffffff82111561104257611042610efd565b5060051b60200190565b600080604080848603121561106057600080fd5b833561106b81610e78565b925060208481013567ffffffffffffffff8082111561108957600080fd5b818701915087601f83011261109d57600080fd5b81356110ab610fa382611028565b81815260059190911b8301840190848101908a8311156110ca57600080fd5b8585015b8381101561113d578035858111156110e65760008081fd5b8601808d03601f19018913156110fc5760008081fd5b611104610f13565b888201358152898201358781111561111c5760008081fd5b61112a8f8b83860101610fd3565b828b0152508452509186019186016110ce565b50809750505050505050509250929050565b6000806040838503121561116257600080fd5b823561116d81610e78565b915060208381013567ffffffffffffffff81111561118a57600080fd5b8401601f8101861361119b57600080fd5b80356111a9610fa382611028565b81815260059190911b820183019083810190888311156111c857600080fd5b928401925b828410156111e6578335825292840192908401906111cd565b80955050505050509250929050565b60008060006060848603121561120a57600080fd5b833561121581610e78565b925060208401359150604084013567ffffffffffffffff81111561123857600080fd5b61124486828701610fd3565b9150509250925092565b6000806000806080858703121561126457600080fd5b843561126f81610e78565b9350602085013561127f81610e78565b925060408501359150606085013567ffffffffffffffff8111156112a257600080fd5b8501601f810187136112b357600080fd5b6112c287823560208401610f95565b91505092959194509250565b600080604083850312156112e157600080fd5b823567ffffffffffffffff808211156112f957600080fd5b61130586838701610fd3565b9350602085013591508082111561131b57600080fd5b5061132885828601610fd3565b9150509250929050565b60006020828403121561134457600080fd5b813561078c81610e78565b8015158114610e8d57600080fd5b6000806040838503121561137057600080fd5b823561137b81610e78565b9150602083013561138b8161134f565b809150509250929050565b600080604083850312156113a957600080fd5b82356113b481610e78565b9150602083013561138b81610e78565b6000602082840312156113d657600080fd5b815161078c8161134f565b6000602082840312156113f357600080fd5b815167ffffffffffffffff81111561140a57600080fd5b8201601f8101841361141b57600080fd5b8051611429610fa382610f6d565b81815285602083850101111561143e57600080fd5b61144f826020830160208601610df4565b95945050505050565b60006020828403121561146a57600080fd5b815161078c81610e78565b60006020828403121561148757600080fd5b5051919050565b6001600160a01b0383168152604060208083018290528351828401819052600092916060600583901b860181019290860190878301865b8281101561150457888603605f190184528151805187528501518587018890526114f188880182610e20565b96505092840192908401906001016114c5565b50939998505050505050505050565b6001600160a01b038316815260406020808301829052835191830182905260009184820191906060850190845b8181101561155c57845183529383019391830191600101611540565b5090979650505050505050565b60018060a01b038416815282602082015260606040820152600061144f6060830184610e20565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906115c390830184610e20565b9695505050505050565b6040815260006115e06040830185610e20565b828103602084015261144f8185610e2056fea26469706673582212205bdf4275f4b714a1029ccfe77c0f9a0e20e121a932e1e5840cace97dfa886da964736f6c634300080d0033
\ No newline at end of file
deletedjs-packages/tests/src/eth/proxy/UniqueNFTProxy.soldiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/UniqueNFTProxy.sol
+++ /dev/null
@@ -1,186 +0,0 @@
-// SPDX-License-Identifier: OTHER
-
-pragma solidity >=0.8.0 <0.9.0;
-
-import "../api/UniqueNFT.sol";
-
-contract UniqueNFTProxy is UniqueNFT {
-    UniqueNFT proxied;
-
-    constructor(address _proxied) UniqueNFT() {
-        proxied = UniqueNFT(_proxied);
-    }
-
-    function name() external view override returns (string memory) {
-        return proxied.name();
-    }
-
-    function symbol() external view override returns (string memory) {
-        return proxied.symbol();
-    }
-
-    function totalSupply() external view override returns (uint256) {
-        return proxied.totalSupply();
-    }
-
-    function balanceOf(address owner) external view override returns (uint256) {
-        return proxied.balanceOf(owner);
-    }
-
-    function ownerOf(uint256 tokenId) external view override returns (address) {
-        return proxied.ownerOf(tokenId);
-    }
-
-    function safeTransferFromWithData(
-        address from,
-        address to,
-        uint256 tokenId,
-        bytes memory data
-    ) external override {
-        return proxied.safeTransferFromWithData(from, to, tokenId, data);
-    }
-
-    function safeTransferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) external override {
-        return proxied.safeTransferFrom(from, to, tokenId);
-    }
-
-    function transferFrom(
-        address from,
-        address to,
-        uint256 tokenId
-    ) external override {
-        return proxied.transferFrom(from, to, tokenId);
-    }
-
-    function approve(address approved, uint256 tokenId) external override {
-        return proxied.approve(approved, tokenId);
-    }
-
-    function setApprovalForAll(address operator, bool approved)
-        external
-        override
-    {
-        return proxied.setApprovalForAll(operator, approved);
-    }
-
-    function getApproved(uint256 tokenId)
-        external
-        view
-        override
-        returns (address)
-    {
-        return proxied.getApproved(tokenId);
-    }
-
-    function isApprovedForAll(address owner, address operator)
-        external
-        view
-        override
-        returns (address)
-    {
-        return proxied.isApprovedForAll(owner, operator);
-    }
-
-    function burn(uint256 tokenId) external override {
-        return proxied.burn(tokenId);
-    }
-
-    function tokenByIndex(uint256 index)
-        external
-        view
-        override
-        returns (uint256)
-    {
-        return proxied.tokenByIndex(index);
-    }
-
-    function tokenOfOwnerByIndex(address owner, uint256 index)
-        external
-        view
-        override
-        returns (uint256)
-    {
-        return proxied.tokenOfOwnerByIndex(owner, index);
-    }
-
-    function tokenURI(uint256 tokenId)
-        external
-        view
-        override
-        returns (string memory)
-    {
-        return proxied.tokenURI(tokenId);
-    }
-
-    function mintingFinished() external view override returns (bool) {
-        return proxied.mintingFinished();
-    }
-
-    function mint(address to)
-        external
-        override
-        returns (uint256)
-    {
-        return proxied.mint(to);
-    }
-
-    function mintWithTokenURI(
-        address to,
-        string memory tokenUri
-    ) external override returns (uint256) {
-        return proxied.mintWithTokenURI(to, tokenUri);
-    }
-
-    function finishMinting() external override returns (bool) {
-        return proxied.finishMinting();
-    }
-
-    function transfer(address to, uint256 tokenId) external override {
-        return proxied.transfer(to, tokenId);
-    }
-
-    function nextTokenId() external view override returns (uint256) {
-        return proxied.nextTokenId();
-    }
-
-    function burnFrom(address from, uint256 tokenId) external override {
-        return proxied.burnFrom(from, tokenId);
-    }
-
-    function supportsInterface(bytes4 interfaceId)
-        external
-        view
-        override
-        returns (bool)
-    {
-        return proxied.supportsInterface(interfaceId);
-    }
-
-    function mintBulk(address to, uint256[] memory tokenIds)
-        external
-        override
-        returns (bool)
-    {
-        return proxied.mintBulk(to, tokenIds);
-    }
-
-    function mintBulkWithTokenURI(address to, TokenUri[] memory tokens)
-        external
-        override
-        returns (bool)
-    {
-        return proxied.mintBulkWithTokenURI(to, tokens);
-    }
-
-    function setProperty(string memory key, string memory value) external override {
-        return proxied.setProperty(key, value);
-    }
-
-	function deleteProperty(string memory key) external override {
-        return proxied.deleteProperty(key);
-    }
-}
deletedjs-packages/tests/src/eth/proxy/fungibleProxy.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/fungibleProxy.test.ts
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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} from 'chai';
-import {readFile} from 'fs/promises';
-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';
-
-const {dirname} = makeNames(import.meta.url);
-
-async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) {
-  // Proxy owner has no special privilegies, we don't need to reuse them
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const web3 = helper.getWeb3();
-  const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueFungibleProxy.abi`)).toString()), undefined, {
-    from: owner,
-    gas: helper.eth.DEFAULT_GAS,
-  });
-  const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueFungibleProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner});
-  return proxy;
-}
-
-describe('Fungible (Via EVM proxy): Information getting', () => {
-  let alice: IKeyringPair;
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itEth('totalSupply', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    await collection.mint(alice, 200n, {Substrate: alice.address});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const totalSupply = await contract.methods.totalSupply().call();
-
-    expect(totalSupply).to.equal('200');
-  });
-
-  itEth('balanceOf', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-
-    await collection.mint(alice, 200n, {Ethereum: caller});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const balance = await contract.methods.balanceOf(caller).call();
-
-    expect(balance).to.equal('200');
-  });
-});
-
-describe('Fungible (Via EVM proxy): Plain calls', () => {
-  let alice: IKeyringPair;
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itEth('Can perform approve()', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    await collection.mint(alice, 200n, {Ethereum: contract.options.address});
-
-    {
-      const result = await contract.methods.approve(spender, 100).send({from: caller});
-      const events = helper.eth.normalizeEvents(result.events);
-
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Approval',
-          args: {
-            owner: contract.options.address,
-            spender,
-            value: '100',
-          },
-        },
-      ]);
-    }
-
-    {
-      const allowance = await contract.methods.allowance(contract.options.address, spender).call();
-      expect(+allowance).to.equal(100);
-    }
-  });
-
-  itEth('Can perform transferFrom()', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    await collection.mint(alice, 200n, {Ethereum: owner});
-    const receiver = helper.eth.createAccount();
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-
-    await evmCollection.methods.approve(contract.options.address, 100).send({from: owner});
-
-    {
-      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: caller});
-      const events = helper.eth.normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: owner,
-            to: receiver,
-            value: '49',
-          },
-        },
-        {
-          address,
-          event: 'Approval',
-          args: {
-            owner,
-            spender: contract.options.address,
-            value: '51',
-          },
-        },
-      ]);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(49);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(owner).call();
-      expect(+balance).to.equal(151);
-    }
-  });
-
-  itEth('Can perform transfer()', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'ft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    await collection.mint(alice, 200n, {Ethereum: contract.options.address});
-
-    {
-      const result = await contract.methods.transfer(receiver, 50).send({from: caller});
-      const events = helper.eth.normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: contract.options.address,
-            to: receiver,
-            value: '50',
-          },
-        },
-      ]);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(contract.options.address).call();
-      expect(+balance).to.equal(150);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(50);
-    }
-  });
-});
deletedjs-packages/tests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxy/nonFungibleProxy.test.ts
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {readFile} from 'fs/promises';
-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';
-
-const {dirname} = makeNames(import.meta.url);
-
-
-async function proxyWrap(helper: EthUniqueHelper, wrapped: any, donor: IKeyringPair) {
-  // Proxy owner has no special privilegies, we don't need to reuse them
-  const owner = await helper.eth.createAccountWithBalance(donor);
-  const web3 = helper.getWeb3();
-  const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${dirname}/UniqueNFTProxy.abi`)).toString()), undefined, {
-    from: owner,
-    gas: helper.eth.DEFAULT_GAS,
-  });
-  const proxy = await proxyContract.deploy({data: (await readFile(`${dirname}/UniqueNFTProxy.bin`)).toString(), arguments: [wrapped.options.address]}).send({from: owner});
-  return proxy;
-}
-
-describe('NFT (Via EVM proxy): Information getting', () => {
-  let alice: IKeyringPair;
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itEth('totalSupply', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    await collection.mintToken(alice, {Substrate: alice.address});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const totalSupply = await contract.methods.totalSupply().call();
-
-    expect(totalSupply).to.equal('1');
-  });
-
-  itEth('balanceOf', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    await collection.mintMultipleTokens(alice, [
-      {owner: {Ethereum: caller}},
-      {owner: {Ethereum: caller}},
-      {owner: {Ethereum: caller}},
-    ]);
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const balance = await contract.methods.balanceOf(caller).call();
-
-    expect(balance).to.equal('3');
-  });
-
-  itEth('ownerOf', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: caller});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const owner = await contract.methods.ownerOf(tokenId).call();
-
-    expect(owner).to.equal(caller);
-  });
-});
-
-describe('NFT (Via EVM proxy): Plain calls', () => {
-  let alice: IKeyringPair;
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  // Soft-deprecated
-  itEth('[eth] Can perform mint()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', '');
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller, true);
-    const contract = await proxyWrap(helper, collectionEvm, donor);
-    await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();
-
-    {
-      const nextTokenId = await contract.methods.nextTokenId().call();
-      const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller});
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-      expect(tokenId).to.be.equal('1');
-
-      const event = helper.eth.normalizeEvents(result.events)
-        .find(event => event.event === 'Transfer')!;
-      event.address = event.address.toLocaleLowerCase();
-
-      expect(event).to.be.deep.equal({
-        address: collectionAddress.toLocaleLowerCase(),
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId,
-        },
-      });
-
-      expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
-    }
-  });
-
-  itEth('[cross] Can perform mint()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'A', 'A', '');
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const collectionEvmOwned = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
-    const contract = await proxyWrap(helper, collectionEvm, donor);
-    const contractAddressCross = helper.ethCrossAccount.fromAddress(contract.options.address);
-    await collectionEvmOwned.methods.addCollectionAdminCross(contractAddressCross).send();
-
-    {
-      const nextTokenId = await contract.methods.nextTokenId().call();
-      const result = await contract.methods.mintWithTokenURI(receiver, nextTokenId, 'Test URI').send({from: caller});
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-      expect(tokenId).to.be.equal('1');
-
-      const event = helper.eth.normalizeEvents(result.events)
-        .find(event => event.event === 'Transfer')!;
-      event.address = event.address.toLocaleLowerCase();
-
-      expect(event).to.be.deep.equal({
-        address: collectionAddress.toLocaleLowerCase(),
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: receiver,
-          tokenId,
-        },
-      });
-
-      expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
-    }
-  });
-
-  //TODO: CORE-302 add eth methods
-  itEth.skip('Can perform mintBulk()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(donor, {name: 'New', description: 'New collection', tokenPrefix: 'NEW'});
-
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    await collection.addAdmin(donor, {Ethereum: contract.options.address});
-
-    {
-      const nextTokenId = await contract.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-      const result = await contract.methods.mintBulkWithTokenURI(
-        receiver,
-        [
-          [nextTokenId, 'Test URI 0'],
-          [+nextTokenId + 1, 'Test URI 1'],
-          [+nextTokenId + 2, 'Test URI 2'],
-        ],
-      ).send({from: caller});
-      const events = helper.eth.normalizeEvents(result.events);
-
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: nextTokenId,
-          },
-        },
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: String(+nextTokenId + 1),
-          },
-        },
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: String(+nextTokenId + 2),
-          },
-        },
-      ]);
-
-      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
-      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
-      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
-    }
-  });
-
-  itEth('Can perform burn()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const caller = await helper.eth.createAccountWithBalance(donor);
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});
-    await collection.addAdmin(alice, {Ethereum: contract.options.address});
-
-    {
-      const result = await contract.methods.burn(tokenId).send({from: caller});
-      const events = helper.eth.normalizeEvents(result.events);
-
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: contract.options.address,
-            to: '0x0000000000000000000000000000000000000000',
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
-    }
-  });
-
-  itEth('Can perform approve()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});
-
-    {
-      const result = await contract.methods.approve(spender, tokenId).send({from: caller, gas: helper.eth.DEFAULT_GAS});
-      const events = helper.eth.normalizeEvents(result.events);
-
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Approval',
-          args: {
-            owner: contract.options.address,
-            approved: spender,
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
-    }
-  });
-
-  itEth('Can perform transferFrom()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const receiver = helper.eth.createAccount();
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: owner});
-
-    await evmCollection.methods.approve(contract.options.address, tokenId).send({from: owner});
-
-    {
-      const result = await contract.methods.transferFrom(owner, receiver, tokenId).send({from: caller});
-      const events = helper.eth.normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: owner,
-            to: receiver,
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(1);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(contract.options.address).call();
-      expect(+balance).to.equal(0);
-    }
-  });
-
-  itEth('Can perform transfer()', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const evmCollection = await helper.ethNativeContract.collection(address, 'nft', caller);
-    const contract = await proxyWrap(helper, evmCollection, donor);
-    const {tokenId} = await collection.mintToken(alice, {Ethereum: contract.options.address});
-
-    {
-      const result = await contract.methods.transfer(receiver, tokenId).send({from: caller});
-      const events = helper.eth.normalizeEvents(result.events);
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: contract.options.address,
-            to: receiver,
-            tokenId: tokenId.toString(),
-          },
-        },
-      ]);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(contract.options.address).call();
-      expect(+balance).to.equal(0);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(1);
-    }
-  });
-});
deletedjs-packages/tests/src/eth/proxyContract.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/proxyContract.test.ts
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-
-import {itEth, expect, usingEthPlaygrounds} from './util/index.js';
-import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-
-describe('EVM payable contracts', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Update proxy contract', async({helper}) => {
-    const deployer = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const proxyContract = await deployProxyContract(helper, deployer);
-
-    const realContractV1 = await deployRealContractV1(helper, deployer);
-    const realContractV1proxy = new helper.web3!.eth.Contract(realContractV1.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS});
-    await proxyContract.methods.updateVersion(realContractV1.options.address).send();
-
-    await realContractV1proxy.methods.flip().send();
-    await realContractV1proxy.methods.flip().send();
-    await realContractV1proxy.methods.flip().send();
-    const value1 = await realContractV1proxy.methods.getValue().call();
-    const flipCount1 = await realContractV1proxy.methods.getFlipCount().call();
-    expect(flipCount1).to.be.equal('3');
-    expect(value1).to.be.equal(true);
-
-    const realContractV2 = await deployRealContractV2(helper, deployer);
-    const realContractV2proxy = new helper.web3!.eth.Contract(realContractV2.options.jsonInterface, proxyContract.options.address, {from: caller, gas: helper.eth.DEFAULT_GAS});
-    await proxyContract.methods.updateVersion(realContractV2.options.address).send();
-
-    await realContractV2proxy.methods.flip().send();
-    await realContractV2proxy.methods.flip().send();
-    await realContractV2proxy.methods.setStep(5).send();
-    await realContractV2proxy.methods.increaseFlipCount().send();
-    const value2 = await realContractV2proxy.methods.getValue().call();
-    const flipCount2 = await realContractV2proxy.methods.getFlipCount().call();
-    expect(value2).to.be.equal(true);
-    expect(flipCount2).to.be.equal('6');
-  });
-
-  async function deployProxyContract(helper: EthUniqueHelper, deployer: string) {
-    return await helper.ethContract.deployByCode(deployer, 'ProxyContract', `
-      // SPDX-License-Identifier: UNLICENSED
-      pragma solidity ^0.8.6;
-      
-      contract ProxyContract {
-        event NewEvent(uint data);
-        receive() external payable {}
-        bytes32 private constant implementationSlot =  bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1);
-        constructor() {}
-        function updateVersion(address newContractAddress) external {
-          bytes32 slot = implementationSlot;
-          assembly {
-            sstore(slot, newContractAddress)
-          }
-        }
-        fallback() external {
-          bytes32 slot = implementationSlot;
-          assembly {
-            let ptr := mload(0x40)
-            let contractAddress := sload(slot)
-            
-            calldatacopy(ptr, 0, calldatasize())
-            
-            let result := delegatecall(gas(), contractAddress, ptr, calldatasize(), 0, 0)
-            let size := returndatasize()
-            
-            returndatacopy(ptr, 0, size)
-            
-            switch result
-            case 0 { revert(ptr, size) }
-            default { return(ptr, size) }
-          }
-        }
-      }
-      
-      interface RealContract {
-        function flip() external;
-        function getValue() external view returns (bool);
-        function getFlipCount() external view returns (uint);
-      }`);
-  }
-
-  async function deployRealContractV1(helper: EthUniqueHelper, deployer: string) {
-    return await helper.ethContract.deployByCode(deployer, 'RealContractV1', `
-      // SPDX-License-Identifier: UNLICENSED
-      pragma solidity ^0.8.6;
-  
-      contract RealContractV1 {
-        bool value = false;
-        uint flipCount = 0;
-        function flip() external {
-          value = !value;
-          flipCount++;
-        }
-        function getValue() external view returns (bool) {
-          return value;
-        }
-        function getFlipCount() external view returns (uint) {
-          return flipCount;
-        }
-      }`);
-  }
-
-  async function deployRealContractV2(helper: EthUniqueHelper, deployer: string) {
-    return await helper.ethContract.deployByCode(deployer, 'RealContractV2', `
-      // SPDX-License-Identifier: UNLICENSED
-      pragma solidity ^0.8.6;
-  
-      contract RealContractV2 {
-        bool value = false;
-        uint flipCount = 10;
-        uint step = 1;
-        function flip() external {
-          value = !value;
-          flipCount--;
-        }
-        function setStep(uint value) external {
-          step = value;
-        }
-        function increaseFlipCount() external {
-          flipCount = flipCount + step;
-        }
-        function getValue() external view returns (bool) {
-          return value;
-        }
-        function getFlipCount() external view returns (uint) {
-          return flipCount;
-        }
-      }`);
-  }
-});
deletedjs-packages/tests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/reFungible.test.ts
+++ /dev/null
@@ -1,1019 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import type {ITokenPropertyPermission} from '@unique/playgrounds/src/types.js';
-import {CREATE_COLLECTION_DATA_DEFAULTS, TokenPermissionField} from './util/playgrounds/types.js';
-
-describe('Refungible: Plain calls', () => {
-  let donor: IKeyringPair;
-  let minter: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [minter, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  [
-    'substrate' as const,
-    'ethereum' as const,
-  ].map(testCase => {
-    itEth(`Can perform mintCross() for ${testCase} address`, async ({helper}) => {
-      const collectionAdmin = await helper.eth.createAccountWithBalance(donor);
-
-      const receiverEth = helper.eth.createAccount();
-      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-      const receiverSub = bob;
-      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
-
-      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
-      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {
-        tokenOwner: false,
-        collectionAdmin: true,
-        mutable: false}}));
-
-
-      const collection = await helper.rft.mintCollection(minter, {
-        tokenPrefix: 'ethp',
-        tokenPropertyPermissions: permissions,
-      });
-      await collection.addAdmin(minter, {Ethereum: collectionAdmin});
-
-      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', collectionAdmin, true);
-      let expectedTokenId = await contract.methods.nextTokenId().call();
-      let result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, []).send();
-      let tokenId = result.events.Transfer.returnValues.tokenId;
-      expect(tokenId).to.be.equal(expectedTokenId);
-
-      let event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
-      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
-
-      expectedTokenId = await contract.methods.nextTokenId().call();
-      result = await contract.methods.mintCross(testCase === 'ethereum' ? receiverCrossEth : receiverCrossSub, properties).send();
-      event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.be.equal(testCase === 'ethereum' ? receiverEth : helper.address.substrateToEth(bob.address));
-      expect(await contract.methods.properties(tokenId, []).call()).to.be.like([]);
-
-      tokenId = result.events.Transfer.returnValues.tokenId;
-
-      expect(tokenId).to.be.equal(expectedTokenId);
-
-      expect(await contract.methods.properties(tokenId, []).call()).to.be.like(properties
-        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
-
-      expect(await helper.nft.getTokenOwner(collection.collectionId, tokenId))
-        .to.deep.eq(testCase === 'ethereum' ? {Ethereum: receiverEth.toLowerCase()} : {Substrate: receiverSub.address});
-    });
-  });
-
-  itEth.skip('Can perform mintBulk()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'MintBulky', '6', '6', '');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-    {
-      const nextTokenId = await contract.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-      const result = await contract.methods.mintBulkWithTokenURI(
-        receiver,
-        [
-          [nextTokenId, 'Test URI 0'],
-          [+nextTokenId + 1, 'Test URI 1'],
-          [+nextTokenId + 2, 'Test URI 2'],
-        ],
-      ).send();
-
-      const events = result.events.Transfer;
-      for(let i = 0; i < 2; i++) {
-        const event = events[i];
-        expect(event.address).to.equal(collectionAddress);
-        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-        expect(event.returnValues.to).to.equal(receiver);
-        expect(event.returnValues.tokenId).to.equal(String(+nextTokenId + i));
-      }
-
-      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
-      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
-      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
-    }
-  });
-
-  itEth('Can perform mintBulkCross() with multiple tokens', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const callerCross = helper.ethCrossAccount.fromAddress(caller);
-    const receiver = helper.eth.createAccount();
-    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
-
-    const permissions = [
-      {code: TokenPermissionField.Mutable, value: true},
-      {code: TokenPermissionField.TokenOwner, value: true},
-      {code: TokenPermissionField.CollectionAdmin, value: true},
-    ];
-    const {collectionAddress} = await helper.eth.createCollection(
-      caller,
-      {
-        ...CREATE_COLLECTION_DATA_DEFAULTS,
-        name: 'A',
-        description: 'B',
-        tokenPrefix: 'C',
-        collectionMode: 'rft',
-        adminList: [callerCross],
-        tokenPropertyPermissions: [
-          {key: 'key_0_0', permissions},
-          {key: 'key_1_0', permissions},
-          {key: 'key_1_1', permissions},
-          {key: 'key_2_0', permissions},
-          {key: 'key_2_1', permissions},
-          {key: 'key_2_2', permissions},
-        ],
-      },
-    ).send();
-
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-    const nextTokenId = await contract.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-    const result = await contract.methods.mintBulkCross([
-      {
-        owners: [{
-          owner: receiverCross,
-          pieces: 1,
-        }],
-        properties: [
-          {key: 'key_0_0', value: Buffer.from('value_0_0')},
-        ],
-      },
-      {
-        owners: [{
-          owner: receiverCross,
-          pieces: 2,
-        }],
-        properties: [
-          {key: 'key_1_0', value: Buffer.from('value_1_0')},
-          {key: 'key_1_1', value: Buffer.from('value_1_1')},
-        ],
-      },
-      {
-        owners: [{
-          owner: receiverCross,
-          pieces: 1,
-        }],
-        properties: [
-          {key: 'key_2_0', value: Buffer.from('value_2_0')},
-          {key: 'key_2_1', value: Buffer.from('value_2_1')},
-          {key: 'key_2_2', value: Buffer.from('value_2_2')},
-        ],
-      },
-    ]).send({from: caller});
-    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
-    const bulkSize = 3;
-    for(let i = 0; i < bulkSize; i++) {
-      const event = events[i];
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.equal(receiver);
-      expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
-    }
-
-    const properties = [
-      await contract.methods.properties(+nextTokenId, []).call(),
-      await contract.methods.properties(+nextTokenId + 1, []).call(),
-      await contract.methods.properties(+nextTokenId + 2, []).call(),
-    ];
-    expect(properties).to.be.deep.equal([
-      [
-        ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],
-      ],
-      [
-        ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],
-        ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],
-      ],
-      [
-        ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
-        ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
-        ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
-      ],
-    ]);
-  });
-
-  itEth('Can perform mintBulkCross() with multiple owners', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const callerCross = helper.ethCrossAccount.fromAddress(caller);
-    const receiver = helper.eth.createAccount();
-    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
-    const receiver2 = helper.eth.createAccount();
-    const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);
-
-    const permissions = [
-      {code: TokenPermissionField.Mutable, value: true},
-      {code: TokenPermissionField.TokenOwner, value: true},
-      {code: TokenPermissionField.CollectionAdmin, value: true},
-    ];
-    const {collectionAddress} = await helper.eth.createCollection(
-      caller,
-      {
-        ...CREATE_COLLECTION_DATA_DEFAULTS,
-        name: 'A',
-        description: 'B',
-        tokenPrefix: 'C',
-        collectionMode: 'rft',
-        adminList: [callerCross],
-        tokenPropertyPermissions: [
-          {key: 'key_2_0', permissions},
-          {key: 'key_2_1', permissions},
-          {key: 'key_2_2', permissions},
-        ],
-      },
-    ).send();
-
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-    const nextTokenId = await contract.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-    const result = await contract.methods.mintBulkCross([{
-      owners: [
-        {
-          owner: receiverCross,
-          pieces: 1,
-        },
-        {
-          owner: receiver2Cross,
-          pieces: 2,
-        },
-      ],
-      properties: [
-        {key: 'key_2_0', value: Buffer.from('value_2_0')},
-        {key: 'key_2_1', value: Buffer.from('value_2_1')},
-        {key: 'key_2_2', value: Buffer.from('value_2_2')},
-      ],
-    }]).send({from: caller});
-    const event = result.events.Transfer;
-    expect(event.address).to.equal(collectionAddress);
-    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
-    expect(event.returnValues.tokenId).to.equal(`${+nextTokenId}`);
-
-    const properties = [
-      await contract.methods.properties(+nextTokenId, []).call(),
-    ];
-    expect(properties).to.be.deep.equal([[
-      ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
-      ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
-      ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
-    ]]);
-  });
-
-  itEth('Can perform setApprovalForAll()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const operator = helper.eth.createAccount();
-
-    const collection = await helper.rft.mintCollection(minter, {});
-
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-    const approvedBefore = await contract.methods.isApprovedForAll(owner, operator).call();
-    expect(approvedBefore).to.be.equal(false);
-
-    {
-      const result = await contract.methods.setApprovalForAll(operator, true).send({from: owner});
-
-      expect(result.events.ApprovalForAll).to.be.like({
-        address: collectionAddress,
-        event: 'ApprovalForAll',
-        returnValues: {
-          owner,
-          operator,
-          approved: true,
-        },
-      });
-
-      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
-      expect(approvedAfter).to.be.equal(true);
-    }
-
-    {
-      const result = await contract.methods.setApprovalForAll(operator, false).send({from: owner});
-
-      expect(result.events.ApprovalForAll).to.be.like({
-        address: collectionAddress,
-        event: 'ApprovalForAll',
-        returnValues: {
-          owner,
-          operator,
-          approved: false,
-        },
-      });
-
-      const approvedAfter = await contract.methods.isApprovedForAll(owner, operator).call();
-      expect(approvedAfter).to.be.equal(false);
-    }
-  });
-
-  itEth('Can perform burn with ApprovalForAll', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const operator = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft');
-
-    {
-      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: operator});
-      const events = result.events.Transfer;
-
-      expect(events).to.be.like({
-        address,
-        event: 'Transfer',
-        returnValues: {
-          from: owner,
-          to: '0x0000000000000000000000000000000000000000',
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-  });
-
-  itEth('Can perform burn with approve and approvalForAll', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const operator = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft');
-
-    const rftToken = await helper.ethNativeContract.rftTokenById(token.collectionId, token.tokenId, owner, true);
-
-    {
-      await rftToken.methods.approve(operator, 15n).send({from: owner});
-      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
-      await rftToken.methods.burnFrom(owner, 10n).send({from: operator});
-    }
-    {
-      const allowance = await rftToken.methods.allowance(owner, operator).call();
-      expect(+allowance).to.be.equal(5);
-    }
-    {
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const operatorCross = helper.ethCrossAccount.fromAddress(operator);
-      const allowance = await rftToken.methods.allowanceCross(ownerCross, operatorCross).call();
-      expect(+allowance).to.equal(5);
-    }
-  });
-
-  itEth('Can perform transfer with ApprovalForAll', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const operator = await helper.eth.createAccountWithBalance(donor);
-    const receiver = charlie;
-
-    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft');
-
-    {
-      await contract.methods.setApprovalForAll(operator, true).send({from: owner});
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
-      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: operator});
-      const event = result.events.Transfer;
-      expect(event).to.be.like({
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: owner,
-          to: helper.address.substrateToEth(receiver.address),
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);
-  });
-
-  itEth('Can perform burn()', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Burny', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-    {
-      const result = await contract.methods.burn(tokenId).send();
-      const event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal(caller);
-      expect(event.returnValues.to).to.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
-    }
-  });
-
-  itEth('Can perform transferFrom()', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'TransferFromy', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
-
-    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller);
-    await tokenContract.methods.repartition(15).send();
-
-    {
-      const tokenEvents: any = [];
-      tokenContract.events.allEvents((_: any, event: any) => {
-        tokenEvents.push(event);
-      });
-      const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();
-      if(tokenEvents.length == 0) await helper.wait.newBlocks(1);
-
-      let event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal(caller);
-      expect(event.returnValues.to).to.equal(receiver);
-      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
-
-      event = tokenEvents[0];
-      expect(event.address).to.equal(tokenAddress);
-      expect(event.returnValues.from).to.equal(caller);
-      expect(event.returnValues.to).to.equal(receiver);
-      expect(event.returnValues.value).to.equal('15');
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(1);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(caller).call();
-      expect(+balance).to.equal(0);
-    }
-  });
-
-  // Soft-deprecated
-  itEth('Can perform burnFrom()', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft', spender, true);
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
-    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-    await tokenContract.methods.repartition(15).send();
-    await tokenContract.methods.approve(spender, 15).send();
-
-    {
-      const result = await contract.methods.burnFrom(owner, token.tokenId).send();
-      const event = result.events.Transfer;
-      expect(event).to.be.like({
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: owner,
-          to: '0x0000000000000000000000000000000000000000',
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await collection.getTokenBalance(token.tokenId, {Ethereum: owner})).to.be.eq(0n);
-  });
-
-  itEth('Can perform burnFromCross()', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = bob;
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft');
-
-    await token.repartition(owner, 15n);
-    await token.approve(owner, {Ethereum: spender}, 15n);
-
-    {
-      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
-      const result = await contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender});
-      const event = result.events.Transfer;
-      expect(event).to.be.like({
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: helper.address.substrateToEth(owner.address),
-          to: '0x0000000000000000000000000000000000000000',
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await collection.getTokenBalance(token.tokenId, {Substrate: owner.address})).to.be.eq(0n);
-  });
-
-  itEth('Can perform transferFromCross()', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = bob;
-    const spender = await helper.eth.createAccountWithBalance(donor);
-    const receiver = charlie;
-
-    const token = await collection.mintToken(minter, 100n, {Substrate: owner.address});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft');
-
-    await token.repartition(owner, 15n);
-    await token.approve(owner, {Ethereum: spender}, 15n);
-
-    {
-      const ownerCross = helper.ethCrossAccount.fromKeyringPair(owner);
-      const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
-      const result = await contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender});
-      const event = result.events.Transfer;
-      expect(event).to.be.like({
-        address: helper.ethAddress.fromCollectionId(collection.collectionId),
-        event: 'Transfer',
-        returnValues: {
-          from: helper.address.substrateToEth(owner.address),
-          to: helper.address.substrateToEth(receiver.address),
-          tokenId: token.tokenId.toString(),
-        },
-      });
-    }
-
-    expect(await token.getTop10Owners()).to.be.like([{Substrate: receiver.address}]);
-  });
-
-  itEth('Can perform transfer()', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    {
-      const result = await contract.methods.transfer(receiver, tokenId).send();
-
-      const event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal(caller);
-      expect(event.returnValues.to).to.equal(receiver);
-      expect(event.returnValues.tokenId).to.equal(tokenId.toString());
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(caller).call();
-      expect(+balance).to.equal(0);
-    }
-
-    {
-      const balance = await contract.methods.balanceOf(receiver).call();
-      expect(+balance).to.equal(1);
-    }
-  });
-
-  itEth('Can perform transferCross()', async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-    const receiverEth = await helper.eth.createAccountWithBalance(donor);
-    const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
-
-    const collection = await helper.rft.mintCollection(minter, {});
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender);
-
-    const token = await collection.mintToken(minter, 50n, {Ethereum: sender});
-
-    {
-      // Can transferCross to ethereum address:
-      const result = await collectionEvm.methods.transferCross(receiverCrossEth, token.tokenId).send({from: sender});
-      // Check events:
-      const event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal(sender);
-      expect(event.returnValues.to).to.equal(receiverEth);
-      expect(event.returnValues.tokenId).to.equal(token.tokenId.toString());
-      // Sender's balance decreased:
-      const senderBalance = await collectionEvm.methods.balanceOf(sender).call();
-      expect(+senderBalance).to.equal(0);
-      expect(await token.getBalance({Ethereum: sender})).to.eq(0n);
-      // Receiver's balance increased:
-      const receiverBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
-      expect(+receiverBalance).to.equal(1);
-      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(50n);
-    }
-
-    {
-      // Can transferCross to substrate address:
-      const substrateResult = await collectionEvm.methods.transferCross(receiverCrossSub, token.tokenId).send({from: receiverEth});
-      // Check events:
-      const event = substrateResult.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal(receiverEth);
-      expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(minter.address));
-      expect(event.returnValues.tokenId).to.be.equal(`${token.tokenId}`);
-      // Sender's balance decreased:
-      const senderBalance = await collectionEvm.methods.balanceOf(receiverEth).call();
-      expect(+senderBalance).to.equal(0);
-      expect(await token.getBalance({Ethereum: receiverEth})).to.eq(0n);
-      // Receiver's balance increased:
-      const receiverBalance = await helper.nft.getTokensByAddress(collection.collectionId, {Substrate: minter.address});
-      expect(receiverBalance).to.contain(token.tokenId);
-      expect(await token.getBalance({Substrate: minter.address})).to.eq(50n);
-    }
-  });
-
-  ['transfer', 'transferCross'].map(testCase => itEth(`Cannot ${testCase} non-owned token`, async ({helper}) => {
-    const sender = await helper.eth.createAccountWithBalance(donor);
-    const tokenOwner = await helper.eth.createAccountWithBalance(donor);
-    const receiverSub = minter;
-    const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(minter);
-
-    const collection = await helper.rft.mintCollection(minter, {});
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', sender);
-
-    await collection.mintToken(minter, 50n, {Ethereum: sender});
-    const nonSendersToken = await collection.mintToken(minter, 50n, {Ethereum: tokenOwner});
-
-    // Cannot transferCross someone else's token:
-    const receiver = testCase === 'transfer' ? helper.address.substrateToEth(receiverSub.address) : receiverCrossSub;
-    await expect(collectionEvm.methods[testCase](receiver, nonSendersToken.tokenId).send({from: sender})).to.be.rejected;
-    // Cannot transfer token if it does not exist:
-    await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;
-  }));
-
-  itEth('transfer event on transfer from partial ownership to full ownership', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Partial-to-Full', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
-
-    await tokenContract.methods.repartition(2).send();
-    await tokenContract.methods.transfer(receiver, 1).send();
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await tokenContract.methods.transfer(receiver, 1).send();
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.address).to.equal(collectionAddress);
-    expect(event.returnValues.from).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
-    expect(event.returnValues.to).to.equal(receiver);
-    expect(event.returnValues.tokenId).to.equal(tokenId.toString());
-  });
-
-  itEth('transfer event on transfer from full ownership to partial ownership', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Transferry-Full-to-Partial', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
-
-    await tokenContract.methods.repartition(2).send();
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    await tokenContract.methods.transfer(receiver, 1).send();
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.address).to.equal(collectionAddress);
-    expect(event.returnValues.from).to.equal(caller);
-    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
-    expect(event.returnValues.tokenId).to.equal(tokenId.toString());
-  });
-
-  itEth('Check balanceOfCross()', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {});
-    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
-
-    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
-
-    for(let i = 1n; i < 10n; i++) {
-      await collection.mintToken(minter, 100n, {Ethereum: owner.eth});
-      expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
-    }
-  });
-
-  itEth('Check ownerOfCross()', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {});
-    let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
-    const {tokenId} = await collection.mintToken(minter, 100n,{Ethereum: owner.eth});
-
-    for(let i = 1n; i < 10n; i++) {
-      const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
-      expect(ownerCross.eth).to.be.eq(owner.eth);
-      expect(ownerCross.sub).to.be.eq(owner.sub);
-
-      const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-      await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
-      owner = newOwner;
-    }
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth, true);
-    const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    await tokenContract.methods.transferCross(newOwner, 50).send({from: owner.eth});
-    const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
-    expect(ownerCross.eth.toUpperCase()).to.be.eq('0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
-    expect(ownerCross.sub).to.be.eq('0');
-  });
-});
-
-describe('RFT: Fees', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer-From', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-    expect(cost > 0n);
-  });
-
-  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionAddress} = await helper.eth.createRFTCollection(caller, 'Feeful-Transfer', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    const cost = await helper.eth.recordCallFee(caller, () => contract.methods.transfer(receiver, tokenId).send());
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-    expect(cost > 0n);
-  });
-});
-
-describe('Common metadata', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([1000n], donor);
-    });
-  });
-
-  itEth('Returns collection name', async ({helper}) => {
-    const caller = helper.eth.createAccount();
-    const tokenPropertyPermissions = [{
-      key: 'URI',
-      permission: {
-        mutable: true,
-        collectionAdmin: true,
-        tokenOwner: false,
-      },
-    }];
-    const collection = await helper.rft.mintCollection(
-      alice,
-      {
-        name: 'Leviathan',
-        tokenPrefix: '11',
-        properties: [{key: 'ERC721Metadata', value: '1'}],
-        tokenPropertyPermissions,
-      },
-    );
-
-    const contract = helper.ethNativeContract.collectionById(collection.collectionId, 'rft', caller);
-    const name = await contract.methods.name().call();
-    expect(name).to.equal('Leviathan');
-  });
-
-  itEth('Returns symbol name', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const tokenPropertyPermissions = [{
-      key: 'URI',
-      permission: {
-        mutable: true,
-        collectionAdmin: true,
-        tokenOwner: false,
-      },
-    }];
-    const {collectionId} = await helper.rft.mintCollection(
-      alice,
-      {
-        name: 'Leviathan',
-        tokenPrefix: '12',
-        properties: [{key: 'ERC721Metadata', value: '1'}],
-        tokenPropertyPermissions,
-      },
-    );
-
-    const contract = await helper.ethNativeContract.collectionById(collectionId, 'rft', caller);
-    const symbol = await contract.methods.symbol().call();
-    expect(symbol).to.equal('12');
-  });
-});
-
-describe('Negative tests', () => {
-  let donor: IKeyringPair;
-  let minter: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [minter, alice] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itEth('[negative] Cant perform burn without approval', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft');
-
-    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-
-    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
-
-    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
-    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
-
-    await expect(contract.methods.burnFromCross(ownerCross, token.tokenId).send({from: spender})).to.be.rejected;
-  });
-
-  itEth('[negative] Cant perform transfer without approval', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(minter, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = alice;
-
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const token = await collection.mintToken(minter, 100n, {Ethereum: owner});
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'rft');
-
-    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-    const recieverCross = helper.ethCrossAccount.fromKeyringPair(receiver);
-
-    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
-
-    await contract.methods.setApprovalForAll(spender, true).send({from: owner});
-    await contract.methods.setApprovalForAll(spender, false).send({from: owner});
-
-    await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
-  });
-
-  itEth('[negative] Can perform mintBulkCross() with multiple owners and multiple tokens', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const callerCross = helper.ethCrossAccount.fromAddress(caller);
-    const receiver = helper.eth.createAccount();
-    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
-    const receiver2 = helper.eth.createAccount();
-    const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);
-
-    const permissions = [
-      {code: TokenPermissionField.Mutable, value: true},
-      {code: TokenPermissionField.TokenOwner, value: true},
-      {code: TokenPermissionField.CollectionAdmin, value: true},
-    ];
-    const {collectionAddress} = await helper.eth.createCollection(
-      caller,
-      {
-        ...CREATE_COLLECTION_DATA_DEFAULTS,
-        name: 'A',
-        description: 'B',
-        tokenPrefix: 'C',
-        collectionMode: 'rft',
-        adminList: [callerCross],
-        tokenPropertyPermissions: [
-          {key: 'key_0_0', permissions},
-          {key: 'key_2_0', permissions},
-          {key: 'key_2_1', permissions},
-          {key: 'key_2_2', permissions},
-        ],
-      },
-    ).send();
-
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-    const nextTokenId = await contract.methods.nextTokenId().call();
-    expect(nextTokenId).to.be.equal('1');
-    const createData = [
-      {
-        owners: [{
-          owner: receiverCross,
-          pieces: 1,
-        }],
-        properties: [
-          {key: 'key_0_0', value: Buffer.from('value_0_0')},
-        ],
-      },
-      {
-        owners: [
-          {
-            owner: receiverCross,
-            pieces: 1,
-          },
-          {
-            owner: receiver2Cross,
-            pieces: 2,
-          },
-        ],
-        properties: [
-          {key: 'key_2_0', value: Buffer.from('value_2_0')},
-          {key: 'key_2_1', value: Buffer.from('value_2_1')},
-          {key: 'key_2_2', value: Buffer.from('value_2_2')},
-        ],
-      },
-    ];
-
-    await expect(contract.methods.mintBulkCross(createData).call({from: caller})).to.be.rejectedWith('creation of multiple tokens supported only if they have single owner each');
-  });
-});
deletedjs-packages/tests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/reFungibleToken.test.ts
+++ /dev/null
@@ -1,677 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {expect, itEth, usingEthPlaygrounds} from './util/index.js';
-import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {Contract} from 'web3-eth-contract';
-
-// FIXME: Need erc721 for ReFubgible.
-describe('Check ERC721 token URI for ReFungible', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-    const result = await contract.methods.mint(receiver).send();
-
-    const event = result.events.Transfer;
-    const tokenId = event.returnValues.tokenId;
-    expect(tokenId).to.be.equal('1');
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.to).to.be.equal(receiver);
-
-    if(propertyKey && propertyValue) {
-      // Set URL or suffix
-
-      await contract.methods.setProperties(tokenId, [{key: propertyKey, value: Buffer.from(propertyValue)}]).send();
-    }
-
-    return {contract, nextTokenId: tokenId};
-  }
-
-  itEth('Empty tokenURI', async ({helper}) => {
-    const {contract, nextTokenId} = await setup(helper, '');
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');
-  });
-
-  itEth('TokenURI from url', async ({helper}) => {
-    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');
-  });
-
-  itEth('TokenURI from baseURI', async ({helper}) => {
-    const {contract, nextTokenId} = await setup(helper, 'BaseURI_');
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');
-  });
-
-  itEth('TokenURI from baseURI + suffix', async ({helper}) => {
-    const suffix = '/some/suffix';
-    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);
-    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);
-  });
-});
-
-describe('Refungible: Plain calls', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([50n], donor);
-    });
-  });
-
-  itEth('Can perform approve()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    {
-      const result = await contract.methods.approve(spender, 100).send({from: owner});
-      const event = result.events.Approval;
-      expect(event.address).to.be.equal(tokenAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.spender).to.be.equal(spender);
-      expect(event.returnValues.value).to.be.equal('100');
-    }
-
-    {
-      const allowance = await contract.methods.allowance(owner, spender).call();
-      expect(+allowance).to.equal(100);
-    }
-  });
-
-  itEth('Can perform approveCross()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-    const spenderSub = (await helper.arrange.createAccounts([1n], donor))[0];
-    const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
-    const spenderCrossSub = helper.ethCrossAccount.fromKeyringPair(spenderSub);
-
-
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    {
-      const result = await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
-      const event = result.events.Approval;
-      expect(event.address).to.be.equal(tokenAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.spender).to.be.equal(spender);
-      expect(event.returnValues.value).to.be.equal('100');
-    }
-
-    {
-      const allowance = await contract.methods.allowance(owner, spender).call();
-      expect(+allowance).to.equal(100);
-    }
-
-
-    {
-      const result = await contract.methods.approveCross(spenderCrossSub, 100).send({from: owner});
-      const event = result.events.Approval;
-      expect(event.address).to.be.equal(tokenAddress);
-      expect(event.returnValues.owner).to.be.equal(owner);
-      expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(spenderSub.address));
-      expect(event.returnValues.value).to.be.equal('100');
-    }
-
-    {
-      const allowance = await collection.getTokenApprovedPieces(tokenId, {Ethereum: owner}, {Substrate: spenderSub.address});
-      expect(allowance).to.equal(100n);
-    }
-
-    {
-      //TO-DO expect with future allowanceCross(owner, spenderCrossEth).call()
-    }
-  });
-
-  itEth('Non-owner and non admin cannot approveCross', async ({helper}) => {
-    const nonOwner = await helper.eth.createAccountWithBalance(donor);
-    const nonOwnerCross = helper.ethCrossAccount.fromAddress(nonOwner);
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.rft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
-    const token = await collection.mintToken(alice, 100n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
-    const tokenEvm = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    await expect(tokenEvm.methods.approveCross(nonOwnerCross, 20).call({from: nonOwner})).to.be.rejectedWith('CantApproveMoreThanOwned');
-  });
-
-  [
-    'transferFrom',
-    'transferFromCross',
-  ].map(testCase =>
-    itEth(`Can perform ${testCase}`, async ({helper}) => {
-      const isCross = testCase === 'transferFromCross';
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const spender = await helper.eth.createAccountWithBalance(donor);
-      const receiverEth = helper.eth.createAccount();
-      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-      const [receiverSub] = await helper.arrange.createAccounts([1n], donor);
-      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
-
-      const collection = await helper.rft.mintCollection(alice);
-      const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
-
-      const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-      const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-      await contract.methods.approve(spender, 100).send({from: owner});
-
-      // 1. Can transfer from
-      // 1.1 Plain ethereum or cross address:
-      {
-        const result = await contract.methods[testCase](
-          isCross ? ownerCross : owner,
-          isCross ? receiverCrossEth : receiverEth,
-          49,
-        ).send({from: spender});
-
-        // Check events:
-        const transferEvent = result.events.Transfer;
-        expect(transferEvent.address).to.be.equal(tokenAddress);
-        expect(transferEvent.returnValues.from).to.be.equal(owner);
-        expect(transferEvent.returnValues.to).to.be.equal(receiverEth);
-        expect(transferEvent.returnValues.value).to.be.equal('49');
-
-        const approvalEvent = result.events.Approval;
-        expect(approvalEvent.address).to.be.equal(tokenAddress);
-        expect(approvalEvent.returnValues.owner).to.be.equal(owner);
-        expect(approvalEvent.returnValues.spender).to.be.equal(spender);
-        expect(approvalEvent.returnValues.value).to.be.equal('51');
-
-        // Check balances:
-        const receiverBalance = await contract.methods.balanceOf(receiverEth).call();
-        const ownerBalance = await contract.methods.balanceOf(owner).call();
-
-        expect(+receiverBalance).to.equal(49);
-        expect(+ownerBalance).to.equal(151);
-      }
-
-      // 1.2 Cross substrate address:
-      if(testCase === 'transferFromCross') {
-        const result = await contract.methods.transferFromCross(ownerCross, receiverCrossSub, 51).send({from: spender});
-        // Check events:
-        const transferEvent = result.events.Transfer;
-        expect(transferEvent.address).to.be.equal(tokenAddress);
-        expect(transferEvent.returnValues.from).to.be.equal(owner);
-        expect(transferEvent.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));
-        expect(transferEvent.returnValues.value).to.be.equal('51');
-
-        const approvalEvent = result.events.Approval;
-        expect(approvalEvent.address).to.be.equal(tokenAddress);
-        expect(approvalEvent.returnValues.owner).to.be.equal(owner);
-        expect(approvalEvent.returnValues.spender).to.be.equal(spender);
-        expect(approvalEvent.returnValues.value).to.be.equal('0');
-
-        // Check balances:
-        const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
-        const ownerBalance = await contract.methods.balanceOf(owner).call();
-        expect(receiverBalance).to.equal(51n);
-        expect(+ownerBalance).to.equal(100);
-      }
-    }));
-
-  [
-    'transfer',
-    'transferCross',
-  ].map(testCase =>
-    itEth(`Can perform ${testCase}()`, async ({helper}) => {
-      const isCross = testCase === 'transferCross';
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const receiverEth = helper.eth.createAccount();
-      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-      const [receiverSub] = await helper.arrange.createAccounts([1n], donor);
-      const receiverCrossSub = helper.ethCrossAccount.fromKeyringPair(receiverSub);
-      const collection = await helper.rft.mintCollection(alice);
-      const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
-
-      const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-      const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-      // 1. Can transfer to plain ethereum or cross-ethereum account:
-      {
-        const result = await contract.methods[testCase](isCross ? receiverCrossEth : receiverEth, 50).send({from: owner});
-        // Check events
-        const transferEvent = result.events.Transfer;
-        expect(transferEvent.address).to.be.equal(tokenAddress);
-        expect(transferEvent.returnValues.from).to.be.equal(owner);
-        expect(transferEvent.returnValues.to).to.be.equal(receiverEth);
-        expect(transferEvent.returnValues.value).to.be.equal('50');
-        // Check balances:
-        const ownerBalance = await contract.methods.balanceOf(owner).call();
-        const receiverBalance = await contract.methods.balanceOf(receiverEth).call();
-        expect(+ownerBalance).to.equal(150);
-        expect(+receiverBalance).to.equal(50);
-      }
-
-      // 2. Can transfer to cross-substrate account:
-      if(isCross) {
-        const result = await contract.methods.transferCross(receiverCrossSub, 50).send({from: owner});
-        // Check events:
-        const event = result.events.Transfer;
-        expect(event.address).to.be.equal(tokenAddress);
-        expect(event.returnValues.from).to.be.equal(owner);
-        expect(event.returnValues.to).to.be.equal(helper.address.substrateToEth(receiverSub.address));
-        expect(event.returnValues.value).to.be.equal('50');
-        // Check balances:
-        const ownerBalance = await contract.methods.balanceOf(owner).call();
-        const receiverBalance = await collection.getTokenBalance(tokenId, {Substrate: receiverSub.address});
-        expect(+ownerBalance).to.equal(100);
-        expect(receiverBalance).to.equal(50n);
-      }
-    }));
-
-  [
-    'transfer',
-    'transferCross',
-  ].map(testCase =>
-    itEth(`Cannot ${testCase}() non-owned token`, async ({helper}) => {
-      const isCross = testCase === 'transferCross';
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-      const receiverEth = await helper.eth.createAccountWithBalance(donor);
-      const receiverCrossEth = helper.ethCrossAccount.fromAddress(receiverEth);
-      const collection = await helper.rft.mintCollection(alice);
-      const rftOwner = await collection.mintToken(alice, 10n, {Ethereum: owner});
-      const rftReceiver = await collection.mintToken(alice, 10n, {Ethereum: receiverEth});
-      const tokenIdNonExist = 9999999;
-
-      const tokenAddress1 = helper.ethAddress.fromTokenId(collection.collectionId, rftOwner.tokenId);
-      const tokenAddress2 = helper.ethAddress.fromTokenId(collection.collectionId, rftReceiver.tokenId);
-      const tokenAddressNonExist = helper.ethAddress.fromTokenId(collection.collectionId, tokenIdNonExist);
-      const tokenEvmOwner = await helper.ethNativeContract.rftToken(tokenAddress1, owner);
-      const tokenEvmReceiver = await helper.ethNativeContract.rftToken(tokenAddress2, owner);
-      const tokenEvmNonExist = await helper.ethNativeContract.rftToken(tokenAddressNonExist, owner);
-
-      // 1. Can transfer zero amount (EIP-20):
-      await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 0).send({from: owner});
-      // 2. Cannot transfer non-owned token:
-      await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected;
-      await expect(tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected;
-      // 3. Cannot transfer non-existing token:
-      await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 0).send({from: owner})).to.be.rejected;
-      await expect(tokenEvmNonExist.methods[testCase](isCross ? ownerCross : owner, 5).send({from: owner})).to.be.rejected;
-
-      // 4. Storage is not corrupted:
-      expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);
-      expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]);
-      expect(await helper.rft.getTokenTop10Owners(collection.collectionId, tokenIdNonExist)).to.deep.eq([]);
-
-      // 4.1 Tokens can be transferred:
-      await tokenEvmOwner.methods[testCase](isCross ? receiverCrossEth : receiverEth, 10).send({from: owner});
-      await tokenEvmReceiver.methods[testCase](isCross ? ownerCross : owner, 10).send({from: receiverEth});
-      expect(await rftOwner.getTop10Owners()).to.deep.eq([{Ethereum: receiverEth.toLowerCase()}]);
-      expect(await rftReceiver.getTop10Owners()).to.deep.eq([{Ethereum: owner.toLowerCase()}]);
-    }));
-
-  itEth('Can perform repartition()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    await contract.methods.repartition(200).send({from: owner});
-    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);
-    await contract.methods.transfer(receiver, 110).send({from: owner});
-    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);
-    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);
-
-    await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted
-
-    await contract.methods.transfer(receiver, 90).send({from: owner});
-    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);
-    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);
-
-    await contract.methods.repartition(150).send({from: receiver});
-    await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted
-    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);
-  });
-
-  itEth('Can repartition with increased amount', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    const result = await contract.methods.repartition(200).send();
-
-    const event = result.events.Transfer;
-    expect(event.address).to.be.equal(tokenAddress);
-    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.to).to.be.equal(owner);
-    expect(event.returnValues.value).to.be.equal('100');
-  });
-
-  itEth('Can repartition with decreased amount', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    const result = await contract.methods.repartition(50).send();
-    const event = result.events.Transfer;
-    expect(event.address).to.be.equal(tokenAddress);
-    expect(event.returnValues.from).to.be.equal(owner);
-    expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');
-    expect(event.returnValues.value).to.be.equal('50');
-  });
-
-  itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
-    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, caller, true);
-
-    await tokenContract.methods.repartition(2).send();
-    await tokenContract.methods.transfer(receiver, 1).send();
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-    await tokenContract.methods.burnFrom(caller, 1).send();
-
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-    expect(event.address).to.be.equal(collectionAddress);
-    expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.tokenId).to.be.equal(tokenId);
-  });
-
-  itEth('Can perform burnFromCross()', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const ownerSub = (await helper.arrange.createAccounts([10n], donor))[0];
-    const ownerCross = helper.ethCrossAccount.fromAddress(owner);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-
-    const spenderCrossEth = helper.ethCrossAccount.fromAddress(spender);
-    const ownerSubCross = helper.ethCrossAccount.fromKeyringPair(ownerSub);
-
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
-
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    {
-      await contract.methods.approveCross(spenderCrossEth, 100).send({from: owner});
-
-      await expect(contract.methods.burnFromCross(ownerCross, 50).send({from: spender})).to.be.fulfilled;
-      await expect(contract.methods.burnFromCross(ownerCross, 100).send({from: spender})).to.be.rejected;
-      expect(await contract.methods.balanceOf(owner).call({from: owner})).to.be.equal('150');
-    }
-    {
-      const {tokenId} = await collection.mintToken(alice, 200n, {Substrate: ownerSub.address});
-      await collection.approveToken(ownerSub, tokenId, {Ethereum: spender}, 100n);
-      const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-      const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-      await expect(contract.methods.burnFromCross(ownerSubCross, 50).send({from: spender})).to.be.fulfilled;
-      await expect(contract.methods.burnFromCross(ownerSubCross, 100).send({from: spender})).to.be.rejected;
-      expect(await collection.getTokenBalance(tokenId, {Substrate: ownerSub.address})).to.be.equal(150n);
-    }
-  });
-
-  itEth('Check balanceOfCross()', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const other = await helper.ethCrossAccount.createAccountWithBalance(donor);
-    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner.eth});
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth);
-
-    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('200');
-    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
-
-    await tokenContract.methods.repartition(100n).send({from: owner.eth});
-    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
-    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
-
-    await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
-    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
-    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
-
-    await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
-    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
-    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
-
-    await tokenContract.methods.repartition(1000n).send({from: other.eth});
-    await tokenContract.methods.transferCross(owner, 500n).send({from: other.eth});
-    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('500');
-    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('500');
-  });
-});
-
-describe('Refungible: Fees', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([50n], donor);
-    });
-  });
-
-  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-
-  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const spender = await helper.eth.createAccountWithBalance(donor);
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    await contract.methods.approve(spender, 100).send({from: owner});
-
-    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-
-  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice);
-    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));
-    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));
-  });
-});
-
-describe('Refungible: Substrate calls', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([50n], donor);
-    });
-  });
-
-  itEth('Events emitted for approve()', async ({helper}) => {
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice);
-    const token = await collection.mintToken(alice, 200n);
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress);
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true;
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.event).to.be.equal('Approval');
-    expect(event.address).to.be.equal(tokenAddress);
-    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.spender).to.be.equal(receiver);
-    expect(event.returnValues.value).to.be.equal('100');
-  });
-
-  itEth('Events emitted for transferFrom()', async ({helper}) => {
-    const [bob] = await helper.arrange.createAccounts([10n], donor);
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice);
-    const token = await collection.mintToken(alice, 200n);
-    await token.approve(alice, {Substrate: bob.address}, 100n);
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress);
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver},  51n)).to.be.true;
-    if(events.length == 0) await helper.wait.newBlocks(1);
-
-    let event = events[0];
-    expect(event.event).to.be.equal('Transfer');
-    expect(event.address).to.be.equal(tokenAddress);
-    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.value).to.be.equal('51');
-
-    event = events[1];
-    expect(event.event).to.be.equal('Approval');
-    expect(event.address).to.be.equal(tokenAddress);
-    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));
-    expect(event.returnValues.value).to.be.equal('49');
-  });
-
-  itEth('Events emitted for transfer()', async ({helper}) => {
-    const receiver = helper.eth.createAccount();
-    const collection = await helper.rft.mintCollection(alice);
-    const token = await collection.mintToken(alice, 200n);
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);
-    const contract = await helper.ethNativeContract.rftToken(tokenAddress);
-
-    const events: any = [];
-    contract.events.allEvents((_: any, event: any) => {
-      events.push(event);
-    });
-
-    expect(await token.transfer(alice, {Ethereum: receiver},  51n)).to.be.true;
-    if(events.length == 0) await helper.wait.newBlocks(1);
-    const event = events[0];
-
-    expect(event.event).to.be.equal('Transfer');
-    expect(event.address).to.be.equal(tokenAddress);
-    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));
-    expect(event.returnValues.to).to.be.equal(receiver);
-    expect(event.returnValues.value).to.be.equal('51');
-  });
-});
-
-describe('ERC 1633 implementation', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Default parent token address and id', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN');
-    const collectionContract = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
-
-    const result = await collectionContract.methods.mint(owner).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);
-    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner);
-
-    expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress);
-    expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId);
-  });
-});
deletedjs-packages/tests/src/eth/scheduling.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/scheduling.test.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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} from 'chai';
-import {itSchedEth} from './util/index.js';
-import {EthUniqueHelper} from './util/playgrounds/unique.dev.js';
-import {Pallets, usingPlaygrounds} from '../util/index.js';
-
-describe('Scheduing EVM smart contracts', () => {
-
-  before(async () => {
-    await usingPlaygrounds(async (helper) => {
-      await helper.testUtils.enable(Pallets.TestUtils);
-    });
-  });
-
-  itSchedEth.ifWithPallets('Successfully schedules and periodically executes an EVM contract', [Pallets.UniqueScheduler], async (scheduleKind, {helper, privateKey}) => {
-    const donor = await privateKey({url: import.meta.url});
-    const [alice] = await helper.arrange.createAccounts([1000n], donor);
-
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-
-    const deployer = await helper.eth.createAccountWithBalance(alice);
-    const flipper = await helper.eth.deployFlipper(deployer);
-
-    const initialValue = await flipper.methods.getValue().call();
-    await helper.eth.transferBalanceFromSubstrate(alice, helper.address.substrateToEth(alice.address));
-
-    const waitForBlocks = 4;
-    const periodic = {
-      period: 2,
-      repetitions: 2,
-    };
-
-    await helper.scheduler.scheduleAfter<EthUniqueHelper>(waitForBlocks, {scheduledId, periodic})
-      .eth.sendEVM(
-        alice,
-        flipper.options.address,
-        flipper.methods.flip().encodeABI(),
-        '0',
-      );
-
-    expect(await flipper.methods.getValue().call()).to.be.equal(initialValue);
-
-    await helper.wait.newBlocks(waitForBlocks + 1);
-    expect(await flipper.methods.getValue().call()).to.be.not.equal(initialValue);
-
-    await helper.wait.newBlocks(periodic.period);
-    expect(await flipper.methods.getValue().call()).to.be.equal(initialValue);
-  });
-});
deletedjs-packages/tests/src/eth/sponsoring.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/sponsoring.test.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itEth, expect, SponsoringMode} from './util/index.js';
-import {usingPlaygrounds} from '../util/index.js';
-
-describe('EVM sponsoring', () => {
-  let donor: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Fee is deducted from contract if sponsoring is enabled', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const caller = helper.eth.createAccount();
-    const originalCallerBalance = await helper.balance.getEthereum(caller);
-
-    expect(originalCallerBalance).to.be.equal(0n);
-
-    const flipper = await helper.eth.deployFlipper(owner);
-
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-
-    await helpers.methods.toggleAllowlist(flipper.options.address, true).send({from: owner});
-    await helpers.methods.toggleAllowed(flipper.options.address, caller, true).send({from: owner});
-
-    await helpers.methods.setSponsor(flipper.options.address, sponsor).send({from: owner});
-    await helpers.methods.confirmSponsorship(flipper.options.address).send({from: sponsor});
-
-    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
-    await helpers.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Allowlisted).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: owner});
-    expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
-
-    const originalSponsorBalance = await helper.balance.getEthereum(sponsor);
-    expect(originalSponsorBalance).to.be.not.equal(0n);
-
-    await flipper.methods.flip().send({from: caller});
-    expect(await flipper.methods.getValue().call()).to.be.true;
-
-    // Balance should be taken from flipper instead of caller
-    expect(await helper.balance.getEthereum(caller)).to.be.equal(originalCallerBalance);
-    expect(await helper.balance.getEthereum(sponsor)).to.be.not.equal(originalSponsorBalance);
-  });
-
-  itEth('...but this doesn\'t applies to payable value', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const originalCallerBalance = await helper.balance.getEthereum(caller);
-
-    expect(originalCallerBalance).to.be.not.equal(0n);
-
-    const collector = await helper.eth.deployCollectorContract(owner);
-
-    const helpers = await helper.ethNativeContract.contractHelpers(owner);
-
-    await helpers.methods.toggleAllowlist(collector.options.address, true).send({from: owner});
-    await helpers.methods.toggleAllowed(collector.options.address, caller, true).send({from: owner});
-
-    expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.false;
-    await helpers.methods.setSponsoringMode(collector.options.address, SponsoringMode.Allowlisted).send({from: owner});
-    await helpers.methods.setSponsoringRateLimit(collector.options.address, 0).send({from: owner});
-    expect(await helpers.methods.sponsoringEnabled(collector.options.address).call()).to.be.true;
-
-    await helpers.methods.setSponsor(collector.options.address, sponsor).send({from: owner});
-    await helpers.methods.confirmSponsorship(collector.options.address).send({from: sponsor});
-
-    const originalSponsorBalance = await helper.balance.getEthereum(sponsor);
-    expect(originalSponsorBalance).to.be.not.equal(0n);
-
-    await collector.methods.giveMoney().send({from: caller, value: '10000'});
-
-    // Balance will be taken from both caller (value) and from collector (fee)
-    expect(await helper.balance.getEthereum(caller)).to.be.equals((originalCallerBalance - 10000n));
-    expect(await helper.balance.getEthereum(sponsor)).to.be.not.equals(originalSponsorBalance);
-    expect(await collector.methods.getCollected().call()).to.be.equal('10000');
-  });
-});
deletedjs-packages/tests/src/eth/tokenProperties.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/tokenProperties.test.ts
+++ /dev/null
@@ -1,625 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 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/src/types.js';
-import {Pallets} from '../util/index.js';
-import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js';
-import {CreateCollectionData, TokenPermissionField} from './util/playgrounds/types.js';
-
-describe('EVM token properties', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([1000n], donor);
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
-      for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
-        const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-        await collection.methods.addCollectionAdminCross(caller).send({from: owner});
-
-        await collection.methods.setTokenPropertyPermissions([
-          ['testKey', [
-            [TokenPermissionField.Mutable, mutable],
-            [TokenPermissionField.TokenOwner, tokenOwner],
-            [TokenPermissionField.CollectionAdmin, collectionAdmin]],
-          ],
-        ]).send({from: caller.eth});
-
-        expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
-          key: 'testKey',
-          permission: {mutable, collectionAdmin, tokenOwner},
-        }]);
-
-        expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
-          ['testKey', [
-            [TokenPermissionField.Mutable.toString(), mutable],
-            [TokenPermissionField.TokenOwner.toString(), tokenOwner],
-            [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]],
-          ],
-        ]);
-      }
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-      await collection.methods.setTokenPropertyPermissions([
-        ['testKey_0', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-        ['testKey_1', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, false],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-        ['testKey_2', [
-          [TokenPermissionField.Mutable, false],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, false]],
-        ],
-      ]).send({from: owner});
-
-      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
-        {
-          key: 'testKey_0',
-          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
-        },
-        {
-          key: 'testKey_1',
-          permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
-        },
-        {
-          key: 'testKey_2',
-          permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
-        },
-      ]);
-
-      expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([
-        ['testKey_0', [
-          [TokenPermissionField.Mutable.toString(), true],
-          [TokenPermissionField.TokenOwner.toString(), true],
-          [TokenPermissionField.CollectionAdmin.toString(), true]],
-        ],
-        ['testKey_1', [
-          [TokenPermissionField.Mutable.toString(), true],
-          [TokenPermissionField.TokenOwner.toString(), false],
-          [TokenPermissionField.CollectionAdmin.toString(), true]],
-        ],
-        ['testKey_2', [
-          [TokenPermissionField.Mutable.toString(), false],
-          [TokenPermissionField.TokenOwner.toString(), true],
-          [TokenPermissionField.CollectionAdmin.toString(), false]],
-        ],
-      ]);
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
-
-      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-      await collection.methods.addCollectionAdminCross(caller).send({from: owner});
-
-      await collection.methods.setTokenPropertyPermissions([
-        ['testKey_0', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-        ['testKey_1', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, false],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-        ['testKey_2', [
-          [TokenPermissionField.Mutable, false],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, false]],
-        ],
-      ]).send({from: caller.eth});
-
-      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
-        {
-          key: 'testKey_0',
-          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
-        },
-        {
-          key: 'testKey_1',
-          permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
-        },
-        {
-          key: 'testKey_2',
-          permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
-        },
-      ]);
-
-      expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
-        ['testKey_0', [
-          [TokenPermissionField.Mutable.toString(), true],
-          [TokenPermissionField.TokenOwner.toString(), true],
-          [TokenPermissionField.CollectionAdmin.toString(), true]],
-        ],
-        ['testKey_1', [
-          [TokenPermissionField.Mutable.toString(), true],
-          [TokenPermissionField.TokenOwner.toString(), false],
-          [TokenPermissionField.CollectionAdmin.toString(), true]],
-        ],
-        ['testKey_2', [
-          [TokenPermissionField.Mutable.toString(), false],
-          [TokenPermissionField.TokenOwner.toString(), true],
-          [TokenPermissionField.CollectionAdmin.toString(), false]],
-        ],
-      ]);
-
-    }));
-
-  [
-    {
-      method: 'setProperties',
-      methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]],
-      expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}],
-    },
-    {
-      method: 'setProperty' /*Soft-deprecated*/,
-      methodParams: ['testKey1', Buffer.from('testValue1')],
-      expectedProps: [{key: 'testKey1', value: 'testValue1'}],
-    },
-  ].map(testCase =>
-    itEth(`[${testCase.method}] Can be set`, async({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const collection = await helper.nft.mintCollection(alice, {
-        tokenPropertyPermissions: [{
-          key: 'testKey1',
-          permission: {
-            collectionAdmin: true,
-          },
-        }, {
-          key: 'testKey2',
-          permission: {
-            collectionAdmin: true,
-          },
-        }],
-      });
-
-      await collection.addAdmin(alice, {Ethereum: caller});
-      const token = await collection.mintToken(alice);
-
-      const collectionEvm = await helper.ethNativeContract.collectionById(collection.collectionId, 'nft', caller, testCase.method === 'setProperty');
-
-      await collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller});
-
-      const properties = await token.getProperties();
-      expect(properties).to.deep.equal(testCase.expectedProps);
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`Can be multiple set/read for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-
-      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
-      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true,
-        collectionAdmin: true,
-        mutable: true}}));
-
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPrefix: 'ethp',
-        tokenPropertyPermissions: permissions,
-      }) as UniqueNFTCollection | UniqueRFTCollection;
-
-      const token = await collection.mintToken(alice);
-
-      const valuesBefore = await token.getProperties(properties.map(p => p.key));
-      expect(valuesBefore).to.be.deep.equal([]);
-
-
-      await collection.addAdmin(alice, {Ethereum: caller});
-
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
-
-      expect(await contract.methods.properties(token.tokenId, []).call()).to.be.deep.equal([]);
-
-      await contract.methods.setProperties(token.tokenId, properties).send({from: caller});
-
-      const values = await token.getProperties(properties.map(p => p.key));
-      expect(values).to.be.deep.equal(properties.map(p => ({key: p.key, value: p.value.toString()})));
-
-      expect(await contract.methods.properties(token.tokenId, []).call()).to.be.like(properties
-        .map(p => helper.ethProperty.property(p.key, p.value.toString())));
-
-      expect(await contract.methods.properties(token.tokenId, [properties[0].key]).call())
-        .to.be.like([helper.ethProperty.property(properties[0].key, properties[0].value.toString())]);
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPropertyPermissions: [{
-          key: 'testKey',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-          },
-        },
-        {
-          key: 'testKey_1',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-          },
-        }],
-      });
-
-      const token = await collection.mintToken(alice);
-      await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}, {key: 'testKey_1', value: 'testValue_1'}]);
-      expect(await token.getProperties()).to.has.length(2);
-
-      await collection.addAdmin(alice, {Ethereum: caller});
-
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
-
-      await contract.methods.deleteProperties(token.tokenId, ['testKey', 'testKey_1']).send({from: caller});
-
-      const result = await token.getProperties(['testKey', 'testKey_1']);
-      expect(result.length).to.equal(0);
-    }));
-
-  itEth('Can be read', async({helper}) => {
-    const caller = helper.eth.createAccount();
-    const collection = await helper.nft.mintCollection(alice, {
-      tokenPropertyPermissions: [{
-        key: 'testKey',
-        permission: {
-          collectionAdmin: true,
-        },
-      }],
-    });
-
-    const token = await collection.mintToken(alice);
-    await token.setProperties(alice, [{key: 'testKey', value: 'testValue'}]);
-
-    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = await helper.ethNativeContract.collection(address, 'nft', caller);
-
-    const value = await contract.methods.property(token.tokenId, 'testKey').call();
-    expect(value).to.equal(helper.getWeb3().utils.toHex('testValue'));
-  });
-});
-
-describe('EVM token properties negative', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let caller: string;
-  let aliceCollection: UniqueNFTCollection;
-  let token: UniqueNFToken;
-  const tokenProps = [{key: 'testKey_1', value: 'testValue_1'}, {key: 'testKey_2', value: 'testValue_2'}];
-  let collectionEvm: Contract;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  beforeEach(async () => {
-    // 1. create collection with props: testKey_1, testKey_2
-    // 2. create token and set props testKey_1, testKey_2
-    await usingEthPlaygrounds(async (helper) => {
-      aliceCollection = await helper.nft.mintCollection(alice, {
-        tokenPropertyPermissions: [{
-          key: 'testKey_1',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-          },
-        },
-        {
-          key: 'testKey_2',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-          },
-        }],
-      });
-      token = await aliceCollection.mintToken(alice);
-      await token.setProperties(alice, tokenProps);
-      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
-    });
-  });
-
-  [
-    {method: 'setProperty', methodParams: [tokenProps[1].key, Buffer.from('newValue')]},
-    {method: 'setProperties', methodParams: [[{key: tokenProps[1].key, value: Buffer.from('newValue')}]]},
-  ].map(testCase =>
-    itEth(`[${testCase.method}] Cannot set properties of non-owned collection`, async ({helper}) => {
-      caller = await helper.eth.createAccountWithBalance(donor);
-      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
-      // Caller not an owner and not an admin, so he cannot set properties:
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
-
-      // Props have not changed:
-      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
-      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
-      expect(actualProps).to.deep.eq(expectedProps);
-    }));
-
-  [
-    {method: 'setProperty', methodParams: ['testKey_3', Buffer.from('testValue3')]},
-    {method: 'setProperties', methodParams: [[{key: 'testKey_3', value: Buffer.from('testValue3')}]]},
-  ].map(testCase =>
-    itEth(`[${testCase.method}] Cannot set non-existing properties`, async ({helper}) => {
-      caller = await helper.eth.createAccountWithBalance(donor);
-      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, true);
-      await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller});
-
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
-
-      // Props have not changed:
-      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
-      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
-      expect(actualProps).to.deep.eq(expectedProps);
-    }));
-
-  [
-    {method: 'deleteProperty', methodParams: ['testKey_2']},
-    {method: 'deleteProperties', methodParams: [['testKey_2']]},
-  ].map(testCase =>
-    itEth(`[${testCase.method}] Cannot delete properties of non-owned collection`, async ({helper}) => {
-      caller = await helper.eth.createAccountWithBalance(donor);
-      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty');
-      // Caller not an owner and not an admin, so he cannot set properties:
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
-
-      // Props have not changed:
-      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
-      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
-      expect(actualProps).to.deep.eq(expectedProps);
-    }));
-
-  [
-    {method: 'deleteProperty', methodParams: ['testKey_3']},
-    {method: 'deleteProperties', methodParams: [['testKey_3']]},
-  ].map(testCase =>
-    itEth(`[${testCase.method}] Cannot delete non-existing properties`, async ({helper}) => {
-      caller = await helper.eth.createAccountWithBalance(donor);
-      collectionEvm = await helper.ethNativeContract.collection(helper.ethAddress.fromCollectionId(aliceCollection.collectionId), 'nft', caller, testCase.method == 'deleteProperty');
-      await helper.collection.addAdmin(alice, aliceCollection.collectionId, {Ethereum: caller});
-      // Caller cannot delete non-existing properties:
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).call({from: caller})).to.be.rejectedWith('NoPermission');
-      await expect(collectionEvm.methods[testCase.method](token.tokenId, ...testCase.methodParams).send({from: caller})).to.be.rejected;
-      // Props have not changed:
-      const expectedProps = tokenProps.map(p => helper.ethProperty.property(p.key, p.value.toString()));
-      const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
-      expect(actualProps).to.deep.eq(expectedProps);
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const caller = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-      await expect(collection.methods.setTokenPropertyPermissions([
-        ['testKey_0', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-      ]).call({from: caller})).to.be.rejectedWith('NoPermission');
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Cannot set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-      await expect(collection.methods.setTokenPropertyPermissions([
-        // "Space" is invalid character
-        ['testKey 0', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-      ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey');
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Can reconfigure token property permissions to stricter ones`, testCase.requiredPallets, async({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-      // 1. Owner sets strict property-permissions:
-      await collection.methods.setTokenPropertyPermissions([
-        ['testKey', [
-          [TokenPermissionField.Mutable, true],
-          [TokenPermissionField.TokenOwner, true],
-          [TokenPermissionField.CollectionAdmin, true]],
-        ],
-      ]).send({from: owner});
-
-      // 2. Owner can set stricter property-permissions:
-      for(const values of [[true, true, false], [true, false, false], [false, false, false]]) {
-        await collection.methods.setTokenPropertyPermissions([
-          ['testKey', [
-            [TokenPermissionField.Mutable, values[0]],
-            [TokenPermissionField.TokenOwner, values[1]],
-            [TokenPermissionField.CollectionAdmin, values[2]]],
-          ],
-        ]).send({from: owner});
-      }
-
-      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
-        key: 'testKey',
-        permission: {mutable: false, collectionAdmin: false, tokenOwner: false},
-      }]);
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Cannot reconfigure token property permissions to less strict ones`, testCase.requiredPallets, async({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-
-      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('A', 'B', 'C', testCase.mode)).send();
-      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-      // 1. Owner sets strict property-permissions:
-      await collection.methods.setTokenPropertyPermissions([
-        ['testKey', [
-          [TokenPermissionField.Mutable, false],
-          [TokenPermissionField.TokenOwner, false],
-          [TokenPermissionField.CollectionAdmin, false]],
-        ],
-      ]).send({from: owner});
-
-      // 2. Owner cannot set less strict property-permissions:
-      for(const values of [[true, false, false], [false, true, false], [false, false, true]]) {
-        await expect(collection.methods.setTokenPropertyPermissions([
-          ['testKey', [
-            [TokenPermissionField.Mutable, values[0]],
-            [TokenPermissionField.TokenOwner, values[1]],
-            [TokenPermissionField.CollectionAdmin, values[2]]],
-          ],
-        ]).call({from: owner})).to.be.rejectedWith('NoPermission');
-      }
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Can't be multiple set/read for non-existent token`, testCase.requiredPallets, async({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-
-      const properties = Array(5).fill(0).map((_, i) => ({key: `key_${i}`, value: Buffer.from(`value_${i}`)}));
-      const permissions: ITokenPropertyPermission[] = properties.map(p => ({key: p.key, permission: {tokenOwner: true,
-        collectionAdmin: true,
-        mutable: true}}));
-
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPrefix: 'ethp',
-        tokenPropertyPermissions: permissions,
-      }) as UniqueNFTCollection | UniqueRFTCollection;
-
-      await collection.addAdmin(alice, {Ethereum: caller});
-
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
-
-      await expect(contract.methods.setProperties(1, properties).call({from: caller})).to.be.rejectedWith('TokenNotFound');
-    }));
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itEth.ifWithPallets(`[${testCase.mode}] Can't be deleted for non-existent token`, testCase.requiredPallets, async({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPropertyPermissions: [{
-          key: 'testKey',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-          },
-        },
-        {
-          key: 'testKey_1',
-          permission: {
-            mutable: true,
-            collectionAdmin: true,
-          },
-        }],
-      });
-
-
-      await collection.addAdmin(alice, {Ethereum: caller});
-
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(address, testCase.mode, caller);
-
-      await expect(contract.methods.deleteProperties(1, ['testKey', 'testKey_1']).call({from: caller})).to.be.rejectedWith('TokenNotFound');
-    }));
-});
-
-
-type ElementOf<A> = A extends readonly (infer T)[] ? T : never;
-function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {
-  if(args.length === 0) {
-    yield internalRest as any;
-    return;
-  }
-  for(const value of args[0]) {
-    yield* cartesian([...internalRest, value], ...args.slice(1)) as any;
-  }
-}
deletedjs-packages/tests/src/eth/tokens/callMethodsERC20.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/tokens/callMethodsERC20.test.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {CreateCollectionData} from '../util/playgrounds/types.js';
-
-[
-  {mode: 'ft' as const, requiredPallets: []},
-  {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-].map(testCase => {
-  describe(`${testCase.mode.toUpperCase()}: ERC-20 call methods`, () => {
-    let donor: IKeyringPair;
-
-    before(async function() {
-      await usingEthPlaygrounds(async (helper, privateKey) => {
-        requirePalletsOrSkip(this, helper, testCase.requiredPallets);
-        donor = await privateKey({url: import.meta.url});
-      });
-    });
-
-    itEth('totalSupply', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
-
-      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send();
-      if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
-
-      // Use collection contract for FT or token contract for RFT:
-      const contract = testCase.mode === 'ft'
-        ? collection
-        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
-
-      // Mint tokens:
-      testCase.mode === 'ft'
-        ? await contract.methods.mint(...mintingParams).send({from: caller})
-        : await contract.methods.repartition(200).send({from: caller});
-
-      const totalSupply = await contract.methods.totalSupply().call();
-      expect(totalSupply).to.equal('200');
-    });
-
-    itEth('balanceOf', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const mintingParams = testCase.mode === 'ft' ? [caller, 200n] : [caller];
-
-      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send();
-      if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
-
-      // Use collection contract for FT or token contract for RFT:
-      const contract = testCase.mode === 'ft'
-        ? collection
-        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
-
-      // Mint tokens:
-      testCase.mode === 'ft'
-        ? await contract.methods.mint(...mintingParams).send({from: caller})
-        : await contract.methods.repartition(200).send({from: caller});
-
-      const balance = await contract.methods.balanceOf(caller).call();
-      expect(balance).to.equal('200');
-    });
-
-    itEth('decimals', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send();
-      if(testCase.mode === 'rft') await collection.methods.mint(caller).send({from: caller});
-
-      // Use collection contract for FT or token contract for RFT:
-      const contract = testCase.mode === 'ft'
-        ? collection
-        : await helper.ethNativeContract.rftTokenById(collectionId, 1, caller);
-
-      const decimals = await contract.methods.decimals().call();
-      expect(decimals).to.equal(testCase.mode === 'rft' ? '0' : '18');
-    });
-  });
-});
deletedjs-packages/tests/src/eth/tokens/callMethodsERC721.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/tokens/callMethodsERC721.test.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
-import type {IKeyringPair} from '@polkadot/types/types';
-import {CreateCollectionData} from '../util/playgrounds/types.js';
-
-
-describe('ERC-721 call methods', () => {
-  let donor: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (_, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  [
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'nft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: name/symbol/description`, testCase.requiredPallets, async ({helper}) => {
-      const callerEth = await helper.eth.createAccountWithBalance(donor);
-      const [callerSub] = await helper.arrange.createAccounts([100n], donor);
-      const [name, description, tokenPrefix] = ['Name', 'Description', 'Symbol'];
-
-      const {collection: collectionEth} = await helper.eth.createCollection(callerEth, new CreateCollectionData(name, description, tokenPrefix, testCase.mode)).send();
-      await collectionEth.methods.mint(callerEth).send({from: callerEth});
-      const {collectionId} = await helper[testCase.mode].mintCollection(callerSub, {name, description, tokenPrefix});
-      const collectionSub = await helper.ethNativeContract.collectionById(collectionId, testCase.mode, callerEth);
-
-      // Can get name/symbol/description for Eth collection
-      expect(await collectionEth.methods.name().call()).to.eq(name);
-      expect(await collectionEth.methods.symbol().call()).to.eq(tokenPrefix);
-      expect(await collectionEth.methods.description().call()).to.eq(description);
-      // Can get name/symbol/description for Sub collection
-      expect(await collectionSub.methods.name().call()).to.eq(name);
-      expect(await collectionSub.methods.symbol().call()).to.eq(tokenPrefix);
-      expect(await collectionSub.methods.description().call()).to.eq(description);
-    });
-  });
-
-  [
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'nft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: totalSupply`, testCase.requiredPallets, async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-
-      const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('TotalSupply', '6', '6', testCase.mode)).send();
-      await collection.methods.mint(caller).send({from: caller});
-
-      const totalSupply = await collection.methods.totalSupply().call();
-      expect(totalSupply).to.equal('1');
-    });
-  });
-
-  [
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'nft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: balanceOf`, testCase.requiredPallets, async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-
-      const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('BalanceOf', 'Descroption', 'Prefix', testCase.mode)).send();
-      await collection.methods.mint(caller).send({from: caller});
-      await collection.methods.mint(caller).send({from: caller});
-      await collection.methods.mint(caller).send({from: caller});
-
-      const balance = await collection.methods.balanceOf(caller).call();
-      expect(balance).to.equal('3');
-    });
-  });
-
-  [
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'nft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf`, testCase.requiredPallets, async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const {collection} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf', '6', '6', testCase.mode)).send();
-
-      const result = await collection.methods.mint(caller).send();
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-
-      const owner = await collection.methods.ownerOf(tokenId).call();
-      expect(owner).to.equal(caller);
-    });
-  });
-
-  [
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    // TODO {mode: 'nft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: ownerOf after burn`, testCase.requiredPallets, async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor);
-      const receiver = helper.eth.createAccount();
-      const {collection, collectionId} = await helper.eth.createCollection(caller, new CreateCollectionData('OwnerOf-AfterBurn', '6', '6', testCase.mode)).send();
-
-      const result = await collection.methods.mint(caller).send();
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-      const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller, true);
-
-      await tokenContract.methods.repartition(2).send();
-      await tokenContract.methods.transfer(receiver, 1).send();
-
-      await tokenContract.methods.burnFrom(caller, 1).send();
-
-      const owner = await collection.methods.ownerOf(tokenId).call();
-      expect(owner).to.equal(receiver);
-    });
-  });
-
-  itEth.ifWithPallets('RFT: ownerOf for partial ownership', [Pallets.ReFungible], async ({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    const receiver = helper.eth.createAccount();
-    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Partial-OwnerOf', '6', '6');
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-
-    const result = await contract.methods.mint(caller).send();
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-    const tokenContract = await helper.ethNativeContract.rftTokenById(collectionId, tokenId, caller);
-
-    await tokenContract.methods.repartition(2).send();
-    await tokenContract.methods.transfer(receiver, 1).send();
-
-    const owner = await contract.methods.ownerOf(tokenId).call();
-    expect(owner).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
-  });
-});
deletedjs-packages/tests/src/eth/tokens/minting.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/tokens/minting.test.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {Pallets} from '../../util/index.js';
-import {expect, itEth, usingEthPlaygrounds} from '../util/index.js';
-import {CreateCollectionData} from '../util/playgrounds/types.js';
-
-
-describe('Minting tokens', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([30n, 20n], donor);
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'ft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Substrate collection`, testCase.requiredPallets, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const receiver = helper.eth.createAccount();
-      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
-
-      const collection = await helper[testCase.mode].mintCollection(alice);
-      await collection.addAdmin(alice, {Ethereum: owner});
-
-      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-      const result = await contract.methods.mint(...mintingParams).send({from: owner});
-
-      // Check events:
-      const event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.equal(receiver);
-      if(testCase.mode === 'ft')
-        expect(event.returnValues.value).to.equal('100');
-
-      // Check token exist:
-      if(testCase.mode === 'ft') {
-        expect(await helper.ft.getBalance(collection.collectionId, {Ethereum: receiver})).to.eq(100n);
-      } else {
-        const tokenId = event.returnValues.tokenId;
-        expect(tokenId).to.be.equal('1');
-        expect(await helper.collection.getLastTokenId(collection.collectionId)).to.eq(1);
-        expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
-      }
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'ft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const receiver = helper.eth.createAccount();
-      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
-
-      const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send();
-
-      const result = await collection.methods.mint(...mintingParams).send({from: owner});
-
-      // Check events:
-      const event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.equal(receiver);
-      if(testCase.mode === 'ft')
-        expect(event.returnValues.value).to.equal('100');
-
-      // Check token exist:
-      if(testCase.mode === 'ft') {
-        expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n);
-      } else {
-        const tokenId = event.returnValues.tokenId;
-        expect(tokenId).to.be.equal('1');
-        expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
-        expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
-      }
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'ft' as const, requiredPallets: []},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mint() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const receiver = helper.eth.createAccount();
-      const mintingParams = testCase.mode === 'ft' ? [receiver, 100] : [receiver];
-
-      const {collection, collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Name', 'Desc', 'Prefix', testCase.mode)).send();
-
-      const result = await collection.methods.mint(...mintingParams).send({from: owner});
-
-      // Check events:
-      const event = result.events.Transfer;
-      expect(event.address).to.equal(collectionAddress);
-      expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.equal(receiver);
-      if(testCase.mode === 'ft')
-        expect(event.returnValues.value).to.equal('100');
-
-      // Check token exist:
-      if(testCase.mode === 'ft') {
-        expect(await helper.ft.getBalance(collectionId, {Ethereum: receiver})).to.eq(100n);
-      } else {
-        const tokenId = event.returnValues.tokenId;
-        expect(tokenId).to.be.equal('1');
-        expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
-        expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
-      }
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase => {
-    itEth.ifWithPallets(`${testCase.mode.toUpperCase()}: Can mintWithTokenURI() for Ethereum collection`, testCase.requiredPallets, async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const receiver = helper.eth.createAccount();
-
-      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', '6', '6', '');
-      const contract = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
-
-      const result = await contract.methods.mintWithTokenURI(receiver, 'Test URI').send();
-      const tokenId = result.events.Transfer.returnValues.tokenId;
-      expect(tokenId).to.be.equal('1');
-
-      const event = result.events.Transfer;
-      expect(event.address).to.be.equal(collectionAddress);
-      expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');
-      expect(event.returnValues.to).to.be.equal(receiver);
-
-      expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
-      expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
-      // TODO: this wont work right now, need release 919000 first
-      // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();
-      // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();
-      // expect(tokenUri).to.be.equal(`https://offchain-service.local/token-info/${nextTokenId}`);
-    });
-  });
-});
deletedjs-packages/tests/src/eth/transferValue.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/transferValue.test.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itEth, usingEthPlaygrounds} from './util/index.js';
-import {expect} from 'chai';
-
-describe('Send value to contract', () => {
-  let donor: IKeyringPair;
-
-  before(async () => {
-    await usingEthPlaygrounds(async (_helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itEth('Send to and from contract', async ({helper}) => {
-    const contractOwner = await helper.eth.createAccountWithBalance(donor, 600n);
-    const [buyer, receiver] = await helper.arrange.createAccounts([600n, 600n], donor);
-    const receiverMirror = helper.address.substrateToEth(receiver.address);
-    const contract = await helper.ethContract.deployByCode(
-      contractOwner,
-      'Test',
-      `
-      // SPDX-License-Identifier: UNLICENSED
-      pragma solidity ^0.8.18;
-      
-      contract Test {
-        function send() public payable {
-          if (msg.value < 1000000000000000000)
-            revert("Not enough gold");
-        }
-      
-        function withdraw(address to) public {
-          payable(to).transfer(1000000000000000000);
-        }
-      }
-      `,
-    );
-
-    const balanceBefore = await helper.balance.getSubstrate(buyer.address);
-    await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.send().encodeABI(), '2000000000000000000');
-    const balanceAfter = await helper.balance.getSubstrate(buyer.address);
-    expect(balanceBefore - balanceAfter > 2000000000000000000n).to.be.true;
-    expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(2000000000000000000n);
-
-    await helper.eth.sendEVM(buyer, contract.options.address, contract.methods.withdraw(receiverMirror).encodeABI(), '0');
-    expect(await helper.balance.getEthereum(receiverMirror)).to.be.equal(1000000000000000000n);
-    expect(await helper.balance.getEthereum(contract.options.address)).to.be.equal(1000000000000000000n);
-  });
-});
deletedjs-packages/tests/src/eth/util/index.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/util/index.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-import * as path from 'path';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-import config from '../../config.js';
-
-import {EthUniqueHelper} from './playgrounds/unique.dev.js';
-import {SilentLogger, SilentConsole} from '@unique/playgrounds/src/unique.dev.js';
-import {makeNames} from '../../util/index.js';
-import type {SchedKind} from '../../util/index.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';
-
-chai.use(chaiAsPromised);
-chai.use(chaiLike);
-export const expect = chai.expect;
-
-export enum SponsoringMode {
-  Disabled = 0,
-  Allowlisted = 1,
-  Generous = 2,
-}
-
-type PrivateKeyFn = (seed: string | {filename?: string, url?: string}) => Promise<IKeyringPair>;
-
-export const usingEthPlaygrounds = async (code: (helper: EthUniqueHelper, privateKey: PrivateKeyFn) => Promise<void>) => {
-  const silentConsole = new SilentConsole();
-  silentConsole.enable();
-
-  const helper = new EthUniqueHelper(new SilentLogger());
-
-  try {
-    await helper.connect(config.substrateUrl);
-    await helper.connectWeb3(config.substrateUrl);
-    const ss58Format = helper.chain.getChainProperties().ss58Format;
-    const privateKey: PrivateKeyFn = async (seed) => {
-      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);
-      if(await helper.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;
-    };
-    await code(helper, privateKey);
-  }
-  finally {
-    await helper.disconnect();
-    silentConsole.disable();
-  }
-};
-
-export function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
-  (opts.only ? it.only :
-    opts.skip ? it.skip : it)(name, async function() {
-    await usingEthPlaygrounds(async (helper, privateKey) => {
-      if(opts.requiredPallets) {
-        requirePalletsOrSkip(this, helper, opts.requiredPallets);
-      }
-
-      await cb({helper, privateKey});
-    });
-  });
-}
-
-export function itEthIfWithPallet(name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
-  return itEth(name, cb, {requiredPallets: required, ...opts});
-}
-
-itEth.only = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEth(name, cb, {only: true});
-itEth.skip = (name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any) => itEth(name, cb, {skip: true});
-
-itEthIfWithPallet.only = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {only: true});
-itEthIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any) => itEthIfWithPallet(name, required, cb, {skip: true});
-itEth.ifWithPallets = itEthIfWithPallet;
-
-export function itSchedEth(
-  name: string,
-  cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any,
-  opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {},
-) {
-  itEth(name + ' (anonymous scheduling)', (apis) => cb('anon', apis), opts);
-  itEth(name + ' (named scheduling)', (apis) => cb('named', apis), opts);
-}
-itSchedEth.only = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any) => itSchedEth(name, cb, {only: true});
-itSchedEth.skip = (name: string, cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any) => itSchedEth(name, cb, {skip: true});
-itSchedEth.ifWithPallets = itSchedIfWithPallets;
-
-function itSchedIfWithPallets(name: string, required: string[], cb: (schedKind: SchedKind, apis: { helper: EthUniqueHelper, privateKey: PrivateKeyFn }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
-  return itSchedEth(name, cb, {requiredPallets: required, ...opts});
-}
deletedjs-packages/tests/src/eth/util/playgrounds/types.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/util/playgrounds/types.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import {CollectionFlag} from '@unique/playgrounds/src/types.js';
-import type {TCollectionMode} from '@unique/playgrounds/src/types.js';
-
-export interface ContractImports {
-  solPath: string;
-  fsPath: string;
-}
-
-export interface CompiledContract {
-  abi: any;
-  object: string;
-}
-
-export type NormalizedEvent = {
-  address: string,
-  event: string,
-  args: { [key: string]: string }
-};
-
-export interface OptionUint {
-  status: boolean,
-  value: bigint,
-}
-
-export type EthAddress = string;
-
-export interface CrossAddress {
-  readonly eth: EthAddress,
-  readonly sub: string | Uint8Array,
-}
-
-export type EthProperty = string[];
-
-export enum TokenPermissionField {
-  Mutable,
-  TokenOwner,
-  CollectionAdmin
-}
-
-export enum CollectionLimitField {
-  AccountTokenOwnership,
-	SponsoredDataSize,
-	SponsoredDataRateLimit,
-	TokenLimit,
-	SponsorTransferTimeout,
-	SponsorApproveTimeout,
-	OwnerCanTransfer,
-	OwnerCanDestroy,
-	TransferEnabled
-}
-
-export interface CollectionLimit {
-  field: CollectionLimitField,
-  value: OptionUint,
-}
-
-export interface CollectionLimitValue {
-  field: CollectionLimitField,
-  value: bigint,
-}
-
-export enum CollectionMode {
-	Nonfungible,
-	Fungible,
-	Refungible,
-}
-
-export interface PropertyPermission {
-  code: TokenPermissionField,
-  value: boolean,
-}
-export interface TokenPropertyPermission {
-  key: string,
-  permissions: PropertyPermission[],
-}
-export interface CollectionNestingAndPermission {
-  token_owner: boolean,
-  collection_admin: boolean,
-  restricted: string[],
-}
-
-export const emptyAddress: [string, string] = [
-  '0x0000000000000000000000000000000000000000',
-  '0',
-];
-
-export const CREATE_COLLECTION_DATA_DEFAULTS = {
-  decimals: 0,
-  properties: [],
-  tokenPropertyPermissions: [],
-  adminList: [],
-  nestingSettings: {token_owner: false, collection_admin: false, restricted: []},
-  limits: [],
-  pendingSponsor: emptyAddress,
-  flags: 0,
-};
-
-export interface Property {
-  key: string;
-  value: Buffer;
-}
-
-export class CreateCollectionData {
-  name: string;
-  description: string;
-  tokenPrefix: string;
-  collectionMode: TCollectionMode;
-  decimals? = 0;
-  properties?: Property[] = [];
-  tokenPropertyPermissions?: TokenPropertyPermission[] = [];
-  adminList?: CrossAddress[] = [];
-  nestingSettings?: CollectionNestingAndPermission = {token_owner: false, collection_admin: false, restricted: []};
-  limits?: CollectionLimitValue[] = [];
-  pendingSponsor?: [string, string] = emptyAddress;
-  flags?: number | CollectionFlag[] = [0];
-
-  constructor(
-    name: string,
-    description: string,
-    tokenPrefix: string,
-    collectionMode: TCollectionMode,
-    decimals = 18,
-  ) {
-    this.name = name;
-    this.description = description;
-    this.tokenPrefix = tokenPrefix;
-    this.collectionMode = collectionMode;
-    if(collectionMode == 'ft')
-      this.decimals = decimals;
-    else
-      this.decimals = 0;
-  }
-}
deletedjs-packages/tests/src/eth/util/playgrounds/unique.dev.d.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/util/playgrounds/unique.dev.d.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-declare module 'solc';
\ No newline at end of file
deletedjs-packages/tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/js-packages/tests/src/eth/util/playgrounds/unique.dev.ts
+++ /dev/null
@@ -1,617 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// SPDX-License-Identifier: Apache-2.0
-
-/* eslint-disable function-call-argument-newline */
-// eslint-disable-next-line @typescript-eslint/triple-slash-reference
-/// <reference path="unique.dev.d.ts" />
-
-import {readFile} from 'fs/promises';
-
-import Web3 from 'web3';
-import {WebsocketProvider} from 'web3-core';
-import {Contract} from 'web3-eth-contract';
-
-import solc from 'solc';
-
-import {evmToAddress} from '@polkadot/util-crypto';
-import type {IKeyringPair} from '@polkadot/types/types';
-
-import {ArrangeGroup, DevUniqueHelper} from '@unique/playgrounds/src/unique.dev.js';
-
-import type {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty} from './types.js';
-import {CollectionMode, CreateCollectionData} from './types.js';
-
-// Native contracts ABI
-import collectionHelpersAbi from '../../abi/collectionHelpers.json' assert {type: 'json'};
-import nativeFungibleAbi from '../../abi/nativeFungible.json' assert {type: 'json'};
-import fungibleAbi from '../../abi/fungible.json' assert {type: 'json'};
-import fungibleDeprecatedAbi from '../../abi/fungibleDeprecated.json' assert {type: 'json'};
-import nonFungibleAbi from '../../abi/nonFungible.json' assert {type: 'json'};
-import nonFungibleDeprecatedAbi from '../../abi/nonFungibleDeprecated.json' assert {type: 'json'};
-import refungibleAbi from '../../abi/reFungible.json' assert {type: 'json'};
-import refungibleDeprecatedAbi from '../../abi/reFungibleDeprecated.json' assert {type: 'json'};
-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/src/types.js';
-
-class EthGroupBase {
-  helper: EthUniqueHelper;
-  gasPrice?: string;
-
-  constructor(helper: EthUniqueHelper) {
-    this.helper = helper;
-  }
-  async getGasPrice() {
-    if(this.gasPrice)
-      return this.gasPrice;
-    this.gasPrice = await this.helper.getWeb3().eth.getGasPrice();
-    return this.gasPrice;
-  }
-}
-
-class ContractGroup extends EthGroupBase {
-  async findImports(imports?: ContractImports[]) {
-    if(!imports) return function(path: string) {
-      return {error: `File not found: ${path}`};
-    };
-
-    const knownImports = {} as { [key: string]: string };
-    for(const imp of imports) {
-      knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString();
-    }
-
-    return function(path: string) {
-      if(path in knownImports) return {contents: knownImports[path]};
-      return {error: `File not found: ${path}`};
-    };
-  }
-
-  async compile(name: string, src: string, imports?: ContractImports[]): Promise<CompiledContract> {
-    const compiled = JSON.parse(solc.compile(JSON.stringify({
-      language: 'Solidity',
-      sources: {
-        [`${name}.sol`]: {
-          content: src,
-        },
-      },
-      settings: {
-        outputSelection: {
-          '*': {
-            '*': ['*'],
-          },
-        },
-      },
-    }), {import: await this.findImports(imports)}));
-
-    const hasErrors = compiled['errors']
-      && compiled['errors'].length > 0
-      && compiled.errors.some(function(err: any) {
-        return err.severity == 'error';
-      });
-
-    if(hasErrors) {
-      throw compiled.errors;
-    }
-    const out = compiled.contracts[`${name}.sol`][name];
-
-    return {
-      abi: out.abi,
-      object: '0x' + out.evm.bytecode.object,
-    };
-  }
-
-  async deployByCode(signer: string, name: string, src: string, imports?: ContractImports[], gas?: number, args?: any[]): Promise<Contract> {
-    const compiledContract = await this.compile(name, src, imports);
-    return this.deployByAbi(signer, compiledContract.abi, compiledContract.object, gas, args);
-  }
-
-  async deployByAbi(signer: string, abi: any, object: string, gas?: number, args?: any[]): Promise<Contract> {
-    const web3 = this.helper.getWeb3();
-    const contract = new web3.eth.Contract(abi, undefined, {
-      data: object,
-      from: signer,
-      gas: gas ?? this.helper.eth.DEFAULT_GAS,
-    });
-    return await contract.deploy({data: object, arguments: args}).send({from: signer});
-  }
-
-}
-
-class NativeContractGroup extends EthGroupBase {
-
-  contractHelpers(caller: string) {
-    const web3 = this.helper.getWeb3();
-    return new web3.eth.Contract(contractHelpersAbi as any, this.helper.getApi().consts.evmContractHelpers.contractAddress.toString(), {
-      from: caller,
-      gas: this.helper.eth.DEFAULT_GAS,
-    });
-  }
-
-  collectionHelpers(caller: string) {
-    const web3 = this.helper.getWeb3();
-    return new web3.eth.Contract(collectionHelpersAbi as any, this.helper.getApi().consts.common.contractAddress.toString(), {
-      from: caller,
-      gas: this.helper.eth.DEFAULT_GAS,
-    });
-  }
-
-  collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) {
-    let abi;
-    if(address === this.helper.ethAddress.fromCollectionId(0)) {
-      abi = nativeFungibleAbi;
-    } else {
-      abi ={
-        'nft': nonFungibleAbi,
-        'rft': refungibleAbi,
-        'ft': fungibleAbi,
-      }[mode];
-    }
-    if(mergeDeprecated) {
-      const deprecated = {
-        'nft': nonFungibleDeprecatedAbi,
-        'rft': refungibleDeprecatedAbi,
-        'ft': fungibleDeprecatedAbi,
-      }[mode];
-      abi = [...abi, ...deprecated];
-    }
-    const web3 = this.helper.getWeb3();
-    return new web3.eth.Contract(abi as any, address, {
-      gas: this.helper.eth.DEFAULT_GAS,
-      ...(caller ? {from: caller} : {}),
-    });
-  }
-
-  collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string, mergeDeprecated = false) {
-    return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller, mergeDeprecated);
-  }
-
-  rftToken(address: string, caller?: string, mergeDeprecated = false) {
-    const web3 = this.helper.getWeb3();
-    const abi = mergeDeprecated ? [...refungibleTokenAbi, ...refungibleTokenDeprecatedAbi] : refungibleTokenAbi;
-    return new web3.eth.Contract(abi as any, address, {
-      gas: this.helper.eth.DEFAULT_GAS,
-      ...(caller ? {from: caller} : {}),
-    });
-  }
-
-  rftTokenById(collectionId: number, tokenId: number, caller?: string, mergeDeprecated = false) {
-    return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller, mergeDeprecated);
-  }
-}
-
-class CreateCollectionTransaction {
-  signer: string;
-  data: CreateCollectionData;
-  mergeDeprecated: boolean;
-  helper: EthUniqueHelper;
-
-  constructor(helper: EthUniqueHelper, signer: string, data: CreateCollectionData, mergeDeprecated = false) {
-    this.helper = helper;
-    this.signer = signer;
-
-    let flags = 0;
-    // convert CollectionFlags to number and join them in one number
-    if(!data.flags) {
-      flags = 0;
-    } else if(typeof data.flags == 'number') {
-      flags = data.flags;
-    } else {
-      for(let i = 0; i < data.flags.length; i++){
-        const flag = data.flags[i];
-        flags = flags | flag;
-      }
-    }
-    data.flags = flags;
-
-    this.data = data;
-    this.mergeDeprecated = mergeDeprecated;
-  }
-
-  // eslint-disable-next-line require-await
-  private async createTransaction() {
-    const collectionHelper = this.helper.ethNativeContract.collectionHelpers(this.signer);
-    let collectionMode;
-    switch (this.data.collectionMode) {
-      case 'nft': collectionMode = CollectionMode.Nonfungible; break;
-      case 'rft': collectionMode = CollectionMode.Refungible; break;
-      case 'ft': collectionMode = CollectionMode.Fungible; break;
-    }
-
-    const tx = collectionHelper.methods.createCollection([
-      this.data.name,
-      this.data.description,
-      this.data.tokenPrefix,
-      collectionMode,
-      this.data.decimals,
-      this.data.properties,
-      this.data.tokenPropertyPermissions,
-      this.data.adminList,
-      this.data.nestingSettings,
-      this.data.limits,
-      this.data.pendingSponsor,
-      this.data.flags,
-    ]);
-    return tx;
-  }
-
-  async send(options?: any): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[], collection: Contract }> {
-    const collectionCreationPrice = {
-      value: Number(this.helper.balance.getCollectionCreationPrice()),
-    };
-    const tx = await this.createTransaction();
-    const result = await tx.send({...options, ...collectionCreationPrice});
-
-    const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
-    const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);
-    const events = this.helper.eth.normalizeEvents(result.events);
-    const collection = await this.helper.ethNativeContract.collectionById(collectionId, this.data.collectionMode, this.signer, this.mergeDeprecated);
-
-    return {collectionId, collectionAddress, events, collection};
-  }
-
-  async call(options?: any) {
-    const collectionCreationPrice = {
-      value: Number(this.helper.balance.getCollectionCreationPrice()),
-    };
-    const tx = await this.createTransaction();
-
-    return await tx.call({...options, ...collectionCreationPrice});
-  }
-}
-
-
-class EthGroup extends EthGroupBase {
-  DEFAULT_GAS = 2_500_000;
-
-  createAccount() {
-    const web3 = this.helper.getWeb3();
-    const account = web3.eth.accounts.create();
-    web3.eth.accounts.wallet.add(account.privateKey);
-    return account.address;
-  }
-
-  async createAccountWithBalance(donor: IKeyringPair, amount = 600n) {
-    const account = this.createAccount();
-    await this.transferBalanceFromSubstrate(donor, account, amount);
-
-    return account;
-  }
-
-  async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount = 100n, inTokens = true) {
-    return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n));
-  }
-
-  async getCollectionCreationFee(signer: string) {
-    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
-    return await collectionHelper.methods.collectionCreationFee().call();
-  }
-
-  async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) {
-    if(!gasLimit) gasLimit = this.DEFAULT_GAS;
-    const web3 = this.helper.getWeb3();
-    // FIXME: can't send legacy transaction using tx.evm.call
-    const gasPrice = await web3.eth.getGasPrice();
-    // TODO: check execution status
-    await this.helper.executeExtrinsic(
-      signer,
-      'api.tx.evm.call', [this.helper.address.substrateToEth(signer.address), contractAddress, abi, value, gasLimit, gasPrice, null, null, []],
-      true,
-    );
-  }
-
-  async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) {
-    return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);
-  }
-
-  createCollectionMethodName(mode: TCollectionMode) {
-    switch (mode) {
-      case 'ft':
-        return 'createFTCollection';
-      case 'nft':
-        return 'createNFTCollection';
-      case 'rft':
-        return 'createRFTCollection';
-    }
-  }
-
-  createCollection(signer: string, data: CreateCollectionData, mergeDeprecated = false): CreateCollectionTransaction {
-    return new CreateCollectionTransaction(this.helper, signer, data, mergeDeprecated);
-  }
-
-  createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
-    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();
-  }
-
-  async createERC721MetadataCompatibleCollection(signer: string, data: CreateCollectionData, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
-    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
-
-    const {collectionId, collectionAddress, events} = await this.createCollection(signer, data).send();
-
-    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
-
-    return {collectionId, collectionAddress, events};
-  }
-
-  async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
-    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
-
-    const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();
-
-    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
-
-    return {collectionId, collectionAddress, events};
-  }
-
-  createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
-    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();
-  }
-
-  createFungibleCollection(signer: string, name: string, _decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
-    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'ft')).send();
-  }
-
-  async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
-    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);
-
-    const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();
-
-    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();
-
-    return {collectionId, collectionAddress, events};
-  }
-
-  async deployCollectorContract(signer: string): Promise<Contract> {
-    return await this.helper.ethContract.deployByCode(signer, 'Collector', `
-    // SPDX-License-Identifier: UNLICENSED
-    pragma solidity ^0.8.6;
-
-    contract Collector {
-      uint256 collected;
-      fallback() external payable {
-        giveMoney();
-      }
-      function giveMoney() public payable {
-        collected += msg.value;
-      }
-      function getCollected() public view returns (uint256) {
-        return collected;
-      }
-      function getUnaccounted() public view returns (uint256) {
-        return address(this).balance - collected;
-      }
-
-      function withdraw(address payable target) public {
-        target.transfer(collected);
-        collected = 0;
-      }
-    }
-  `);
-  }
-
-  async deployFlipper(signer: string): Promise<Contract> {
-    return await this.helper.ethContract.deployByCode(signer, 'Flipper', `
-    // SPDX-License-Identifier: UNLICENSED
-    pragma solidity ^0.8.6;
-
-    contract Flipper {
-      bool value = false;
-      function flip() public {
-        value = !value;
-      }
-      function getValue() public view returns (bool) {
-        return value;
-      }
-    }
-  `);
-  }
-
-  async recordCallFee(user: string, call: () => Promise<any>): Promise<bigint> {
-    const before = await this.helper.balance.getEthereum(user);
-    await call();
-    // In dev mode, the transaction might not finish processing in time
-    await this.helper.wait.newBlocks(1);
-    const after = await this.helper.balance.getEthereum(user);
-
-    return before - after;
-  }
-
-  normalizeEvents(events: any): NormalizedEvent[] {
-    const output = [];
-    for(const key of Object.keys(events)) {
-      if(key.match(/^[0-9]+$/)) {
-        output.push(events[key]);
-      } else if(Array.isArray(events[key])) {
-        output.push(...events[key]);
-      } else {
-        output.push(events[key]);
-      }
-    }
-    output.sort((a, b) => a.logIndex - b.logIndex);
-    return output.map(({address, event, returnValues}) => {
-      const args: { [key: string]: string } = {};
-      for(const key of Object.keys(returnValues)) {
-        if(!key.match(/^[0-9]+$/)) {
-          args[key] = returnValues[key];
-        }
-      }
-      return {
-        address,
-        event,
-        args,
-      };
-    });
-  }
-
-  async calculateFee(address: ICrossAccountId, code: () => Promise<any>): Promise<bigint> {
-    const wrappedCode = async () => {
-      await code();
-      // In dev mode, the transaction might not finish processing in time
-      await this.helper.wait.newBlocks(1);
-    };
-    return await this.helper.arrange.calculcateFee(address, wrappedCode);
-  }
-}
-
-class EthAddressGroup extends EthGroupBase {
-  extractCollectionId(address: string): number {
-    if(!(address.length === 42 || address.length === 40)) throw new Error('address wrong format');
-    return parseInt(address.slice(address.length - 8), 16);
-  }
-
-  fromCollectionId(collectionId: number): string {
-    if(collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow');
-    return (Web3 as any).utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8, '0')}`);
-  }
-
-  extractTokenId(address: string): { collectionId: number, tokenId: number } {
-    if(!address.startsWith('0x'))
-      throw 'address not starts with "0x"';
-    if(address.length > 42)
-      throw 'address length is more than 20 bytes';
-    return {
-      collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)),
-      tokenId: Number('0x' + address.substring(address.length - 8)),
-    };
-  }
-
-  fromTokenId(collectionId: number, tokenId: number): string {
-    return this.helper.util.getTokenAddress({collectionId, tokenId});
-  }
-
-  normalizeAddress(address: string): string {
-    return '0x' + address.substring(address.length - 40);
-  }
-}
-export class EthPropertyGroup extends EthGroupBase {
-  property(key: string, value: string): EthProperty {
-    return [
-      key,
-      '0x' + Buffer.from(value).toString('hex'),
-    ];
-  }
-}
-export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper;
-
-export class EthCrossAccountGroup extends EthGroupBase {
-  createAccount(): CrossAddress {
-    return this.fromAddress(this.helper.eth.createAccount());
-  }
-
-  async createAccountWithBalance(donor: IKeyringPair, amount = 100n) {
-    return this.fromAddress(await this.helper.eth.createAccountWithBalance(donor, amount));
-  }
-
-  fromAddress(address: TEthereumAccount): CrossAddress {
-    return {
-      eth: address,
-      sub: '0',
-    };
-  }
-
-  fromAddr(address: TEthereumAccount): [string, string] {
-    return [
-      address,
-      '0',
-    ];
-  }
-
-  fromKeyringPair(keyring: IKeyringPair): CrossAddress {
-    return {
-      eth: '0x0000000000000000000000000000000000000000',
-      sub: keyring.addressRaw,
-    };
-  }
-}
-
-export class FeeGas {
-  fee: number | bigint = 0n;
-
-  gas: number | bigint = 0n;
-
-  public static async build(helper: EthUniqueHelper, fee: bigint): Promise<FeeGas> {
-    const instance = new FeeGas();
-    instance.fee = instance.convertToTokens(fee);
-    instance.gas = await instance.convertToGas(fee, helper);
-    return instance;
-  }
-
-  private async convertToGas(fee: bigint, helper: EthUniqueHelper): Promise<bigint> {
-    const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());
-    return fee / gasPrice;
-  }
-
-  private convertToTokens(value: bigint, nominal = 1_000_000_000_000_000_000n): number {
-    return Number((value * 1000n) / nominal) / 1000;
-  }
-}
-
-class EthArrangeGroup extends ArrangeGroup {
-  declare helper: EthUniqueHelper;
-
-  constructor(helper: EthUniqueHelper) {
-    super(helper);
-    this.helper = helper;
-  }
-
-  async calculcateFeeGas(payer: ICrossAccountId, promise: () => Promise<any>): Promise<FeeGas> {
-    const fee = await this.calculcateFee(payer, promise);
-    return await FeeGas.build(this.helper, fee);
-  }
-}
-export class EthUniqueHelper extends DevUniqueHelper {
-  web3: Web3.default | null = null;
-  web3Provider: WebsocketProvider | null = null;
-
-  eth: EthGroup;
-  ethAddress: EthAddressGroup;
-  ethCrossAccount: EthCrossAccountGroup;
-  ethNativeContract: NativeContractGroup;
-  ethContract: ContractGroup;
-  ethProperty: EthPropertyGroup;
-  declare arrange: EthArrangeGroup;
-  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: { [key: string]: any } = {}) {
-    options.helperBase = options.helperBase ?? EthUniqueHelper;
-
-    super(logger, options);
-    this.eth = new EthGroup(this);
-    this.ethAddress = new EthAddressGroup(this);
-    this.ethCrossAccount = new EthCrossAccountGroup(this);
-    this.ethNativeContract = new NativeContractGroup(this);
-    this.ethContract = new ContractGroup(this);
-    this.ethProperty = new EthPropertyGroup(this);
-    this.arrange = new EthArrangeGroup(this);
-    super.arrange = this.arrange;
-  }
-
-  getWeb3(): Web3.default {
-    if(this.web3 === null) throw Error('Web3 not connected');
-    return this.web3;
-  }
-
-  connectWeb3(wsEndpoint: string) {
-    if(this.web3 !== null) return;
-    this.web3Provider = new (Web3 as any).providers.WebsocketProvider(wsEndpoint);
-    this.web3 = new (Web3 as any)(this.web3Provider);
-  }
-
-  override async disconnect() {
-    if(this.web3 === null) return;
-    this.web3Provider?.connection.close();
-
-    await super.disconnect();
-  }
-
-  override clearApi() {
-    super.clearApi();
-    this.web3 = null;
-  }
-
-  override clone(helperCls: EthUniqueHelperConstructor, options?: { [key: string]: any; }): EthUniqueHelper {
-    const newHelper = super.clone(helperCls, options) as EthUniqueHelper;
-    newHelper.web3 = this.web3;
-    newHelper.web3Provider = this.web3Provider;
-
-    return newHelper;
-  }
-}
deletedjs-packages/tests/src/fungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/fungible.test.ts
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect, requirePalletsOrSkip, Pallets} from './util/index.js';
-
-const U128_MAX = (1n << 128n) - 1n;
-
-describe('integration test: Fungible functionality:', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
-    });
-  });
-
-  itSub('Create fungible collection and token', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'trest'});
-    const defaultTokenId = await collection.getLastTokenId();
-    expect(defaultTokenId).to.be.equal(0);
-
-    await collection.mint(alice, U128_MAX);
-    const aliceBalance = await collection.getBalance({Substrate: alice.address});
-    const itemCountAfter = await collection.getLastTokenId();
-
-    expect(itemCountAfter).to.be.equal(defaultTokenId);
-    expect(aliceBalance).to.be.equal(U128_MAX);
-  });
-
-  itSub('RPC method tokenOnewrs for fungible collection and token', async ({helper}) => {
-    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
-    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address}));
-
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    await collection.mint(alice, U128_MAX);
-
-    await collection.transfer(alice, {Substrate: bob.address}, 1000n);
-    await collection.transfer(alice, ethAcc, 900n);
-
-    for(let i = 0; i < 7; i++) {
-      await collection.transfer(alice, facelessCrowd[i], 1n);
-    }
-
-    const owners = await collection.getTop10Owners();
-
-    // What to expect
-    expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]);
-    expect(owners.length).to.be.equal(10);
-
-    const [eleven] = await helper.arrange.createAccounts([0n], donor);
-    expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true;
-    expect((await collection.getTop10Owners()).length).to.be.equal(10);
-  });
-
-  itSub('Transfer token', async ({helper}) => {
-    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    await collection.mint(alice, 500n);
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n);
-    expect(await collection.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true;
-    expect(await collection.transfer(alice, ethAcc, 140n)).to.be.true;
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(300n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(60n);
-    expect(await collection.getBalance(ethAcc)).to.be.equal(140n);
-
-    await expect(collection.transfer(alice, {Substrate: bob.address}, 350n)).to.eventually.be.rejectedWith(/common\.TokenValueTooLow/);
-  });
-
-  itSub('Tokens multiple creation', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    await collection.mintWithOneOwner(alice, [
-      {value: 500n},
-      {value: 400n},
-      {value: 300n},
-    ]);
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1200n);
-  });
-
-  itSub('Burn some tokens ', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    await collection.mint(alice, 500n);
-
-    expect(await collection.doesTokenExist(0)).to.be.true;
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(500n);
-    expect(await collection.burnTokens(alice, 499n)).to.be.true;
-    expect(await collection.doesTokenExist(0)).to.be.true;
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n);
-  });
-
-  itSub('Burn all tokens ', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    await collection.mint(alice, 500n);
-
-    expect(await collection.doesTokenExist(0)).to.be.true;
-    expect(await collection.burnTokens(alice, 500n)).to.be.true;
-    expect(await collection.doesTokenExist(0)).to.be.true;
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(0n);
-    expect(await collection.getTotalPieces()).to.be.equal(0n);
-  });
-
-  itSub('Set allowance for token', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
-    await collection.mint(alice, 100n);
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(100n);
-
-    expect(await collection.approveTokens(alice, {Substrate: bob.address}, 60n)).to.be.true;
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n);
-
-    expect(await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true;
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(80n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(20n);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n);
-
-    await collection.burnTokensFrom(bob, {Substrate: alice.address}, 10n);
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(70n);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(30n);
-    expect(await collection.transferFrom(bob, {Substrate: alice.address}, ethAcc, 10n)).to.be.true;
-    expect(await collection.getBalance(ethAcc)).to.be.equal(10n);
-  });
-});
-
-describe('Fungible negative tests', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Fungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub('Cannot transfer incorrect amount of tokens', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const nonExistingCollection = helper.ft.getCollectionObject(99999);
-    await collection.mint(alice, 10n, {Substrate: bob.address});
-
-    // 1. Alice cannot transfer more than 0 tokens if balance low:
-    await expect(collection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow');
-    await expect(collection.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow');
-
-    // 2. Alice cannot transfer non-existing token:
-    await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.CollectionNotFound');
-    await expect(nonExistingCollection.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.CollectionNotFound');
-
-    // 3. Zero transfer allowed (EIP-20):
-    await collection.transfer(bob, {Substrate: charlie.address}, 0n);
-    // 3.1 even if the balance = 0
-    await collection.transfer(alice, {Substrate: charlie.address}, 0n);
-
-    expect(await collection.getBalance({Substrate: alice.address})).to.eq(0n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.eq(10n);
-    expect(await collection.getBalance({Substrate: charlie.address})).to.eq(0n);
-  });
-});
deletedjs-packages/tests/src/getPropertiesRpc.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/getPropertiesRpc.test.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {UniqueHelper, UniqueNFTCollection} from '@unique/playgrounds/src/unique.js';
-
-const collectionProps = [
-  {key: 'col-0', value: 'col-0-value'},
-  {key: 'col-1', value: 'col-1-value'},
-];
-
-const tokenProps = [
-  {key: 'tok-0', value: 'tok-0-value'},
-  {key: 'tok-1', value: 'tok-1-value'},
-];
-
-const tokPropPermission = {
-  mutable: false,
-  tokenOwner: true,
-  collectionAdmin: false,
-};
-
-const tokenPropPermissions = [
-  {
-    key: 'tok-0',
-    permission: tokPropPermission,
-  },
-  {
-    key: 'tok-1',
-    permission: tokPropPermission,
-  },
-];
-
-describe('query properties RPC', () => {
-  let alice: IKeyringPair;
-
-  const mintCollection = async (helper: UniqueHelper) => await helper.nft.mintCollection(alice, {
-    tokenPrefix: 'prps',
-    properties: collectionProps,
-    tokenPropertyPermissions: tokenPropPermissions,
-  });
-
-  const mintToken = async (collection: UniqueNFTCollection) => await collection.mintToken(alice, {Substrate: alice.address}, tokenProps);
-
-
-  before(async () => {
-    await usingPlaygrounds(async (_, privateKey) => {
-      alice = await privateKey({url: import.meta.url});
-    });
-  });
-
-  itSub('query empty collection key set', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const props = await collection.getProperties([]);
-    expect(props).to.be.empty;
-  });
-
-  itSub('query empty token key set', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const token = await mintToken(collection);
-    const props = await token.getProperties([]);
-    expect(props).to.be.empty;
-  });
-
-  itSub('query empty token key permissions set', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const propPermissions = await collection.getPropertyPermissions([]);
-    expect(propPermissions).to.be.empty;
-  });
-
-  itSub('query all collection props by null arg', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const props = await collection.getProperties(null);
-    expect(props).to.be.deep.equal(collectionProps);
-  });
-
-  itSub('query all token props by null arg', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const token = await mintToken(collection);
-    const props = await token.getProperties(null);
-    expect(props).to.be.deep.equal(tokenProps);
-  });
-
-  itSub('query empty token key permissions by null arg', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const propPermissions = await collection.getPropertyPermissions(null);
-    expect(propPermissions).to.be.deep.equal(tokenPropPermissions);
-  });
-
-  itSub('query all collection props by undefined arg', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const props = await collection.getProperties();
-    expect(props).to.be.deep.equal(collectionProps);
-  });
-
-  itSub('query all token props by undefined arg', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const token = await mintToken(collection);
-    const props = await token.getProperties();
-    expect(props).to.be.deep.equal(tokenProps);
-  });
-
-  itSub('query empty token key permissions by undefined arg', async ({helper}) => {
-    const collection = await mintCollection(helper);
-    const propPermissions = await collection.getPropertyPermissions();
-    expect(propPermissions).to.be.deep.equal(tokenPropPermissions);
-  });
-});
-
-[
-  {mode: 'nft' as const},
-  {mode: 'rft' as const},
-].map(testCase =>
-  describe('negative properties', () => {
-    let alice: IKeyringPair;
-
-    before(async () => {
-      await usingPlaygrounds(async (_, privateKey) => {
-        alice = await privateKey({url: import.meta.url});
-      });
-    });
-
-    itSub(`[${testCase.mode}] set token property for non-existent token`, async ({helper}) => {
-      const collection = await helper[testCase.mode].mintCollection(alice);
-      await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]);
-      await expect(collection.setTokenProperties(alice, 1, [{key: 'key', value: 'value'}])).to.be.rejectedWith('common.TokenNotFound');
-      expect(await collection.getTokenProperties(1, ['key'])).to.be.empty;
-    });
-
-    itSub(`[${testCase.mode}] delete token property for non-existent token`, async ({helper}) => {
-      const collection = await helper[testCase.mode].mintCollection(alice);
-      await collection.setTokenPropertyPermissions(alice, [{key: 'key', permission: {mutable: true, tokenOwner: true, collectionAdmin: true}}]);
-      await expect(collection.deleteTokenProperties(alice, 1, ['key'])).to.be.rejectedWith('common.TokenNotFound');
-      expect(await collection.getTokenProperties(1, ['key'])).to.be.empty;
-    });
-  }));
\ No newline at end of file
deletedjs-packages/tests/src/governance/council.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/governance/council.test.ts
+++ /dev/null
@@ -1,457 +0,0 @@
-
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js';
-import {Event} from '@unique/playgrounds/src/unique.dev.js';
-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';
-
-describeGov('Governance: Council tests', () => {
-  let donor: IKeyringPair;
-  let counselors: ICounselors;
-  let sudoer: IKeyringPair;
-
-  const moreThanHalfCouncilThreshold = 3;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Council]);
-
-      donor = await privateKey({url: import.meta.url});
-      sudoer = await privateKey('//Alice');
-    });
-  });
-
-  beforeEach(async () => {
-    counselors = await initCouncil(donor, sudoer);
-  });
-
-  afterEach(async () => {
-    await clearCouncil(sudoer);
-    await clearTechComm(sudoer);
-  });
-
-  async function proposalFromMoreThanHalfCouncil(proposal: any) {
-    return await usingPlaygrounds(async (helper) => {
-      expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5);
-      const proposeResult = await helper.council.collective.propose(
-        counselors.filip,
-        proposal,
-        moreThanHalfCouncilThreshold,
-      );
-
-      const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
-      const proposalIndex = councilProposedEvent.proposalIndex;
-      const proposalHash = councilProposedEvent.proposalHash;
-
-
-      await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
-      await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);
-      await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);
-
-      return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
-    });
-  }
-
-  async function proposalFromAllCouncil(proposal: any) {
-    return await usingPlaygrounds(async (helper) => {
-      expect((await helper.callRpc('api.query.councilMembership.members')).toJSON().length).to.be.equal(5);
-      const proposeResult = await helper.council.collective.propose(
-        counselors.filip,
-        proposal,
-        moreThanHalfCouncilThreshold,
-      );
-
-      const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
-      const proposalIndex = councilProposedEvent.proposalIndex;
-      const proposalHash = councilProposedEvent.proposalHash;
-
-
-      await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
-      await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);
-      await helper.council.collective.vote(counselors.ildar, proposalHash, proposalIndex, true);
-      await helper.council.collective.vote(counselors.irina, proposalHash, proposalIndex, true);
-      await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);
-
-      return await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
-    });
-  }
-
-  itSub('>50% of Council can externally propose SuperMajorityAgainst', async ({helper}) => {
-    const forceSetBalanceReceiver = helper.arrange.createEmptyAccount();
-    const forceSetBalanceTestValue = 20n * 10n ** 25n;
-
-    const democracyProposal = await helper.constructApiCall('api.tx.balances.forceSetBalance', [
-      forceSetBalanceReceiver.address, forceSetBalanceTestValue,
-    ]);
-
-    const councilProposal = await helper.democracy.externalProposeDefaultCall(democracyProposal);
-
-    const proposeResult = await helper.council.collective.propose(
-      counselors.filip,
-      councilProposal,
-      moreThanHalfCouncilThreshold,
-    );
-
-    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
-    const proposalIndex = councilProposedEvent.proposalIndex;
-    const proposalHash = councilProposedEvent.proposalHash;
-
-    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
-    await helper.council.collective.vote(counselors.charu, proposalHash, proposalIndex, true);
-    await helper.council.collective.vote(counselors.filip, proposalHash, proposalIndex, true);
-
-    await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
-
-    const democracyStartedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-    const democracyReferendumIndex = democracyStartedEvent.referendumIndex;
-    const democracyThreshold = democracyStartedEvent.threshold;
-
-    expect(democracyThreshold).to.be.equal('SuperMajorityAgainst');
-
-    await helper.democracy.vote(counselors.filip, democracyReferendumIndex, {
-      Standard: {
-        vote: {
-          aye: true,
-          conviction: 1,
-        },
-        balance: 10_000n,
-      },
-    });
-
-    await helper.democracy.vote(counselors.charu, democracyReferendumIndex, {
-      Standard: {
-        vote: {
-          aye: false,
-          conviction: 1,
-        },
-        balance: 50_000n,
-      },
-    });
-
-    const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed);
-    expect(passedReferendumEvent.referendumIndex).to.be.equal(democracyReferendumIndex);
-
-    await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched);
-    const receiverBalance = await helper.balance.getSubstrate(forceSetBalanceReceiver.address);
-    expect(receiverBalance).to.be.equal(forceSetBalanceTestValue);
-  });
-
-  itSub('Council prime member vote is the default', async ({helper}) => {
-    const newTechCommMember = helper.arrange.createEmptyAccount();
-    const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address);
-    const proposeResult = await helper.council.collective.propose(
-      counselors.filip,
-      addMemberProposal,
-      moreThanHalfCouncilThreshold,
-    );
-
-    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
-    const proposalIndex = councilProposedEvent.proposalIndex;
-    const proposalHash = councilProposedEvent.proposalHash;
-
-    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
-
-    await helper.wait.newBlocks(councilMotionDuration);
-    const closeResult = await helper.council.collective.close(counselors.filip, proposalHash, proposalIndex);
-    const closeEvent = Event.Council.Closed.expect(closeResult);
-    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as string[];
-    expect(closeEvent.yes).to.be.equal(members.length);
-  });
-
-  itSub('Superuser can add a member', async ({helper}) => {
-    const newMember = helper.arrange.createEmptyAccount();
-    await expect(helper.getSudo().council.membership.addMember(sudoer, newMember.address)).to.be.fulfilled;
-
-    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
-    expect(members).to.contains(newMember.address);
-  });
-
-  itSub('Superuser can remove a member', async ({helper}) => {
-    await expect(helper.getSudo().council.membership.removeMember(sudoer, counselors.alex.address)).to.be.fulfilled;
-
-    const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
-    expect(members).to.not.contains(counselors.alex.address);
-  });
-
-  itSub('>50% Council can add TechComm member', async ({helper}) => {
-    const newTechCommMember = helper.arrange.createEmptyAccount();
-    const addMemberProposal = helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address);
-
-    await proposalFromMoreThanHalfCouncil(addMemberProposal);
-
-    const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
-    expect(techCommMembers).to.contains(newTechCommMember.address);
-  });
-
-  itSub('Council can remove TechComm member', async ({helper}) => {
-    const techComm = await initTechComm(donor, sudoer);
-    const removeMemberPrpoposal = helper.technicalCommittee.membership.removeMemberCall(techComm.andy.address);
-    await proposalFromMoreThanHalfCouncil(removeMemberPrpoposal);
-
-    const techCommMembers = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
-    expect(techCommMembers).to.not.contains(techComm.andy.address);
-  });
-
-  itSub.skip('Council member can add Fellowship member', async ({helper}) => {
-    const newFellowshipMember = helper.arrange.createEmptyAccount();
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.fellowship.collective.addMemberCall(newFellowshipMember.address),
-    )).to.be.fulfilled;
-    const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON();
-    expect(fellowshipMembers).to.contains(newFellowshipMember.address);
-  });
-
-  itSub('>50% Council can promote Fellowship member', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-    const memberWithZeroRank = fellowship[0][0];
-
-    const proposal = helper.fellowship.collective.promoteCall(memberWithZeroRank.address);
-    await proposalFromMoreThanHalfCouncil(proposal);
-    const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithZeroRank.address])).toJSON();
-    expect(record).to.be.deep.equal({rank: 1});
-
-    await clearFellowship(sudoer);
-  });
-
-  itSub('>50% Council can demote Fellowship member', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-    const memberWithRankOne = fellowship[1][0];
-
-    const proposal = helper.fellowship.collective.demoteCall(memberWithRankOne.address);
-    await proposalFromMoreThanHalfCouncil(proposal);
-
-    const record = (await helper.callRpc('api.query.fellowshipCollective.members', [memberWithRankOne.address])).toJSON();
-    expect(record).to.be.deep.equal({rank: 0});
-
-    await clearFellowship(sudoer);
-  });
-
-  itSub('>50% Council can add\remove Fellowship member', async ({helper}) => {
-    try {
-      const newMember = helper.arrange.createEmptyAccount();
-
-      const proposalAdd = helper.fellowship.collective.addMemberCall(newMember.address);
-      const proposalRemove = helper.fellowship.collective.removeMemberCall(newMember.address, fellowshipRankLimit);
-      await expect(proposalFromMoreThanHalfCouncil(proposalAdd)).to.be.fulfilled;
-      expect(await helper.fellowship.collective.getMembers()).to.be.deep.contain(newMember.address);
-      await expect(proposalFromMoreThanHalfCouncil(proposalRemove)).to.be.fulfilled;
-      expect(await helper.fellowship.collective.getMembers()).to.be.not.deep.contain(newMember.address);
-    }
-    finally {
-      await clearFellowship(sudoer);
-    }
-  });
-
-  itSub('Council can blacklist Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await expect(proposalFromAllCouncil(helper.democracy.blacklistCall(preimageHash, null))).to.be.fulfilled;
-  });
-
-  itSub('Sudo can blacklist Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await expect(helper.getSudo().democracy.blacklist(sudoer, preimageHash)).to.be.fulfilled;
-  });
-
-  itSub('[Negative] Council cannot add Council member', async ({helper}) => {
-    const newCouncilMember = helper.arrange.createEmptyAccount();
-    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
-
-    await expect(proposalFromAllCouncil(addMemberProposal)).to.be.rejected;
-  });
-
-  itSub('[Negative] Council cannot remove Council member', async ({helper}) => {
-    const removeMemberProposal = helper.council.membership.removeMemberCall(counselors.alex.address);
-
-    await expect(proposalFromAllCouncil(removeMemberProposal)).to.be.rejected;
-  });
-
-  itSub('[Negative] Council cannot submit regular democracy proposal', async ({helper}) => {
-    const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
-
-    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Council cannot externally propose SimpleMajority', async ({helper}) => {
-    const councilProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
-
-    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Council cannot externally propose SuperMajorityApprove', async ({helper}) => {
-    const councilProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
-
-    await expect(proposalFromAllCouncil(councilProposal)).to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Council member cannot add/remove a Council member', async ({helper}) => {
-    const newCouncilMember = helper.arrange.createEmptyAccount();
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.council.membership.addMemberCall(newCouncilMember.address),
-    )).to.be.rejectedWith('BadOrigin');
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.council.membership.removeMemberCall(counselors.charu.address),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] Council cannot set/clear Council prime member', async ({helper}) => {
-    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
-    const proposalForClear = await helper.council.membership.clearPrimeCall();
-
-    await expect(proposalFromAllCouncil(proposalForSet)).to.be.rejectedWith(/BadOrigin/);
-    await expect(proposalFromAllCouncil(proposalForClear)).to.be.rejectedWith(/BadOrigin/);
-
-  });
-
-  itSub('[Negative] Council member cannot set/clear Council prime member', async ({helper}) => {
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.council.membership.setPrimeCall(counselors.charu.address),
-    )).to.be.rejectedWith('BadOrigin');
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.council.membership.clearPrimeCall(),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] Council member cannot add/remove a TechComm member', async ({helper}) => {
-    const newTechCommMember = helper.arrange.createEmptyAccount();
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.technicalCommittee.membership.addMemberCall(newTechCommMember.address),
-    )).to.be.rejectedWith('BadOrigin');
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.technicalCommittee.membership.removeMemberCall(newTechCommMember.address),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] Council member cannot promote/demote a Fellowship member', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-    const memberWithRankOne = fellowship[1][0];
-
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.fellowship.collective.promoteCall(memberWithRankOne.address),
-    )).to.be.rejectedWith('BadOrigin');
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.fellowship.collective.demoteCall(memberWithRankOne.address),
-    )).to.be.rejectedWith('BadOrigin');
-    await clearFellowship(sudoer);
-  });
-
-  itSub('[Negative] Council cannot fast-track Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await expect(proposalFromAllCouncil(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0)))
-      .to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Council member cannot fast-track Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] Council cannot cancel Democracy proposals', async ({helper}) => {
-    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
-    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
-
-    await expect(proposalFromAllCouncil(helper.democracy.cancelProposalCall(proposalIndex)))
-      .to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Council member cannot cancel Democracy proposals', async ({helper}) => {
-
-    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
-    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
-
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.democracy.cancelProposalCall(proposalIndex),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] Council cannot cancel ongoing Democracy referendums', async ({helper}) => {
-    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
-    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-    const referendumIndex = startedEvent.referendumIndex;
-
-    await expect(proposalFromAllCouncil(helper.democracy.emergencyCancelCall(referendumIndex)))
-      .to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Council member cannot cancel ongoing Democracy referendums', async ({helper}) => {
-    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
-    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-    const referendumIndex = startedEvent.referendumIndex;
-
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.democracy.emergencyCancelCall(referendumIndex),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] Council cannot cancel Fellowship referendums', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-    const fellowshipProposer = fellowship[5][0];
-    const proposal = dummyProposal(helper);
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      fellowshipProposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-
-    await expect(proposalFromAllCouncil(helper.fellowship.referenda.cancelCall(referendumIndex)))
-      .to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Council member cannot cancel Fellowship referendums', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-    const fellowshipProposer = fellowship[5][0];
-    const proposal = dummyProposal(helper);
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      fellowshipProposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-    await expect(helper.council.collective.execute(
-      counselors.alex,
-      helper.fellowship.referenda.cancelCall(referendumIndex),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] Council referendum cannot be closed until the voting threshold is met', async ({helper}) => {
-    const councilSize = (await helper.callRpc('api.query.councilMembership.members')).toJSON().length as any as number;
-    expect(councilSize).is.greaterThan(1);
-    const proposeResult = await helper.council.collective.propose(
-      counselors.filip,
-      dummyProposalCall(helper),
-      councilSize,
-    );
-
-    const councilProposedEvent = Event.Council.Proposed.expect(proposeResult);
-    const proposalIndex = councilProposedEvent.proposalIndex;
-    const proposalHash = councilProposedEvent.proposalHash;
-
-
-    await helper.council.collective.vote(counselors.alex, proposalHash, proposalIndex, true);
-    await expect(helper.council.collective.close(counselors.filip, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');
-  });
-
-});
deletedjs-packages/tests/src/governance/democracy.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/governance/democracy.test.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js';
-import {clearFellowship, democracyLaunchPeriod, democracyTrackMinRank, dummyProposalCall, fellowshipConfirmPeriod, fellowshipMinEnactPeriod, fellowshipPreparePeriod, fellowshipPropositionOrigin, initFellowship, voteUnanimouslyInFellowship} from './util.js';
-import {Event} from '@unique/playgrounds/src/unique.dev.js';
-
-describeGov('Governance: Democracy tests', () => {
-  let regularUser: IKeyringPair;
-  let donor: IKeyringPair;
-  let sudoer: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Democracy]);
-
-      donor = await privateKey({url: import.meta.url});
-      sudoer = await privateKey('//Alice');
-
-      [regularUser] = await helper.arrange.createAccounts([1000n], donor);
-    });
-  });
-
-  itSub('Regular user can vote', async ({helper}) => {
-    const fellows = await initFellowship(donor, sudoer);
-    const rank1Proposer = fellows[1][0];
-
-    const democracyProposalCall = dummyProposalCall(helper);
-    const fellowshipProposal = {
-      Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(),
-    };
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      rank1Proposer,
-      fellowshipPropositionOrigin,
-      fellowshipProposal,
-      {After: 0},
-    );
-
-    const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-    await voteUnanimouslyInFellowship(helper, fellows, democracyTrackMinRank, fellowshipReferendumIndex);
-    await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex);
-
-    await helper.wait.expectEvent(
-      fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod,
-      Event.Democracy.Proposed,
-    );
-
-    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-    const referendumIndex = startedEvent.referendumIndex;
-
-    const ayeBalance = 10_000n;
-
-    await helper.democracy.vote(regularUser, referendumIndex, {
-      Standard: {
-        vote: {
-          aye: true,
-          conviction: 1,
-        },
-        balance: ayeBalance,
-      },
-    });
-
-    const referendumInfo = await helper.democracy.referendumInfo(referendumIndex);
-    const tally = referendumInfo.ongoing.tally;
-
-    expect(BigInt(tally.ayes)).to.be.equal(ayeBalance);
-
-    await clearFellowship(sudoer);
-  });
-
-  itSub('[Negative] Regular user cannot submit a regular proposal', async ({helper}) => {
-    const submitResult = helper.democracy.propose(regularUser, dummyProposalCall(helper), 0n);
-    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Regular user cannot externally propose SuperMajorityAgainst', async ({helper}) => {
-    const submitResult = helper.democracy.externalProposeDefault(regularUser, dummyProposalCall(helper));
-    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Regular user cannot externally propose SimpleMajority', async ({helper}) => {
-    const submitResult = helper.democracy.externalProposeMajority(regularUser, dummyProposalCall(helper));
-    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Regular user cannot externally propose SuperMajorityApprove', async ({helper}) => {
-    const submitResult = helper.democracy.externalPropose(regularUser, dummyProposalCall(helper));
-    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
-  });
-});
deletedjs-packages/tests/src/governance/fellowship.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/governance/fellowship.test.ts
+++ /dev/null
@@ -1,335 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/src/unique.dev.js';
-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';
-
-describeGov('Governance: Fellowship tests', () => {
-  let members: IKeyringPair[][];
-
-  let rank1Proposer: IKeyringPair;
-
-  let sudoer: any;
-  let donor: any;
-  let counselors: ICounselors;
-  let techcomms: ITechComms;
-
-  const submissionDeposit = 1000n;
-
-  async function testBadFellowshipProposal(
-    helper: DevUniqueHelper,
-    proposalCall: any,
-  ) {
-    const badProposal = {
-      Inline: proposalCall.method.toHex(),
-    };
-    const submitResult = await helper.fellowship.referenda.submit(
-      rank1Proposer,
-      fellowshipPropositionOrigin,
-      badProposal,
-      defaultEnactmentMoment,
-    );
-
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-    await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, referendumIndex);
-    await helper.fellowship.referenda.placeDecisionDeposit(donor, referendumIndex);
-
-    const enactmentId = await helper.fellowship.referenda.enactmentEventId(referendumIndex);
-    const dispatchedEvent = await helper.wait.expectEvent(
-      fellowshipPreparePeriod + fellowshipConfirmPeriod + defaultEnactmentMoment.After,
-      Event.Scheduler.Dispatched,
-      (event: any) => event.id == enactmentId,
-    );
-
-    expect(dispatchedEvent.result.isErr, 'Bad Fellowship must fail')
-      .to.be.true;
-
-    expect(dispatchedEvent.result.asErr.isBadOrigin, 'Bad Fellowship must fail with BadOrigin')
-      .to.be.true;
-  }
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Fellowship, Pallets.TechnicalCommittee, Pallets.Council]);
-
-      sudoer = await privateKey('//Alice');
-      donor = await privateKey({url: import.meta.url});
-    });
-
-    counselors = await initCouncil(donor, sudoer);
-    techcomms = await initTechComm(donor, sudoer);
-    members = await initFellowship(donor, sudoer);
-
-    rank1Proposer = members[1][0];
-  });
-
-  after(async () => {
-    await clearFellowship(sudoer);
-    await clearTechComm(sudoer);
-    await clearCouncil(sudoer);
-    await hardResetFellowshipReferenda(sudoer);
-    await hardResetDemocracy(sudoer);
-    await hardResetGovScheduler(sudoer);
-  });
-
-  itSub('FellowshipProposition can submit regular Democracy proposals', async ({helper}) => {
-    const democracyProposalCall = dummyProposalCall(helper);
-    const fellowshipProposal = {
-      Inline: helper.democracy.proposeCall(democracyProposalCall, 0n).method.toHex(),
-    };
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      rank1Proposer,
-      fellowshipPropositionOrigin,
-      fellowshipProposal,
-      defaultEnactmentMoment,
-    );
-
-    const fellowshipReferendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-    await voteUnanimouslyInFellowship(helper, members, democracyTrackMinRank, fellowshipReferendumIndex);
-    await helper.fellowship.referenda.placeDecisionDeposit(donor, fellowshipReferendumIndex);
-
-    const democracyProposed = await helper.wait.expectEvent(
-      fellowshipPreparePeriod + fellowshipConfirmPeriod + fellowshipMinEnactPeriod,
-      Event.Democracy.Proposed,
-    );
-
-    const democracyEnqueuedProposal = await helper.democracy.expectPublicProposal(democracyProposed.proposalIndex);
-    expect(democracyEnqueuedProposal.inline, 'Fellowship proposal expected to be in the Democracy')
-      .to.be.equal(democracyProposalCall.method.toHex());
-
-    await helper.wait.newBlocks(democracyVotingPeriod);
-  });
-
-  itSub('Fellowship (rank-1 or greater) member can submit Fellowship proposals on the Democracy track', async ({helper}) => {
-    for(let rank = 1; rank < fellowshipRankLimit; rank++) {
-      const rankMembers = members[rank];
-
-      for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) {
-        const member = rankMembers[memberIdx];
-        const newDummyProposal = dummyProposal(helper);
-
-        const submitResult = await helper.fellowship.referenda.submit(
-          member,
-          fellowshipPropositionOrigin,
-          newDummyProposal,
-          defaultEnactmentMoment,
-        );
-
-        const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-        const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex);
-        expect(referendumInfo.ongoing.track, `${memberIdx}-th member of rank #${rank}: proposal #${referendumIndex} is on invalid track`)
-          .to.be.equal(democracyTrackId);
-      }
-    }
-  });
-
-  itSub(`Fellowship (rank-${democracyTrackMinRank} or greater) members can vote on the Democracy track`, async ({helper}) => {
-    const proposal = dummyProposal(helper);
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      rank1Proposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-
-    let expectedAyes = 0;
-    for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) {
-      const rankMembers = members[rank];
-
-      for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) {
-        const member = rankMembers[memberIdx];
-        await helper.fellowship.collective.vote(member, referendumIndex, true);
-        expectedAyes += 1;
-
-        const referendumInfo = await helper.fellowship.referenda.referendumInfo(referendumIndex);
-        expect(referendumInfo.ongoing.tally.bareAyes, `Vote from ${memberIdx}-th member of rank #${rank} isn't accounted`)
-          .to.be.equal(expectedAyes);
-      }
-    }
-  });
-
-  itSub('Fellowship rank vote strength is correct', async ({helper}) => {
-    const excessRankWeightTable = [
-      1,
-      3,
-      6,
-      10,
-      15,
-      21,
-    ];
-
-    const proposal = dummyProposal(helper);
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      rank1Proposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-
-    for(let rank = democracyTrackMinRank; rank < fellowshipRankLimit; rank++) {
-      const rankMembers = members[rank];
-
-      for(let memberIdx = 0; memberIdx < rankMembers.length; memberIdx++) {
-        const member = rankMembers[memberIdx];
-
-        const referendumInfoBefore = await helper.fellowship.referenda.referendumInfo(referendumIndex);
-        const ayesBefore = referendumInfoBefore.ongoing.tally.ayes;
-
-        await helper.fellowship.collective.vote(member, referendumIndex, true);
-
-        const referendumInfoAfter = await helper.fellowship.referenda.referendumInfo(referendumIndex);
-        const ayesAfter = referendumInfoAfter.ongoing.tally.ayes;
-
-        const expectedVoteWeight = excessRankWeightTable[rank - democracyTrackMinRank];
-        const voteWeight = ayesAfter - ayesBefore;
-
-        expect(voteWeight, `Vote weight of ${memberIdx}-th member of rank #${rank} is invalid`)
-          .to.be.equal(expectedVoteWeight);
-      }
-    }
-  });
-
-  itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityAgainst', async ({helper}) => {
-    await testBadFellowshipProposal(helper, helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper)));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot externally propose SimpleMajority', async ({helper}) => {
-    await testBadFellowshipProposal(helper, helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper)));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot externally propose SuperMajorityApprove', async ({helper}) => {
-    await testBadFellowshipProposal(helper, helper.democracy.externalProposeCall(dummyProposalCall(helper)));
-  });
-
-  itSub('[Negative] Fellowship (rank-0) member cannot submit Fellowship proposals on the Democracy track', async ({helper}) => {
-    const rank0Proposer = members[0][0];
-
-    const proposal = dummyProposal(helper);
-
-    const submitResult = helper.fellowship.referenda.submit(
-      rank0Proposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-
-    await expect(submitResult).to.be.rejectedWith(/BadOrigin/);
-  });
-
-  itSub('[Negative] Fellowship (rank-1 or greater) member cannot submit if no deposit can be provided', async ({helper}) => {
-    const poorMember = rank1Proposer;
-
-    const balanceBefore = await helper.balance.getSubstrate(poorMember.address);
-    await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, submissionDeposit - 1n);
-
-    const proposal = dummyProposal(helper);
-
-    const submitResult = helper.fellowship.referenda.submit(
-      poorMember,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-
-    await expect(submitResult).to.be.rejectedWith(/account balance too low/);
-
-    await helper.getSudo().balance.setBalanceSubstrate(sudoer, poorMember.address, balanceBefore);
-  });
-
-  itSub(`[Negative] Fellowship (rank-${democracyTrackMinRank - 1} or less) members cannot vote on the Democracy track`, async ({helper}) => {
-    const proposal = dummyProposal(helper);
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      rank1Proposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-
-    for(let rank = 0; rank < democracyTrackMinRank; rank++) {
-      for(const member of members[rank]) {
-        const voteResult = helper.fellowship.collective.vote(member, referendumIndex, true);
-        await expect(voteResult).to.be.rejectedWith(/RankTooLow/);
-      }
-    }
-  });
-
-  itSub('[Negative] FellowshipProposition cannot add/remove a Council member', async ({helper}) => {
-    const [councilNonMember] = await helper.arrange.createAccounts([10n], donor);
-
-    await testBadFellowshipProposal(helper, helper.council.membership.addMemberCall(councilNonMember.address));
-    await testBadFellowshipProposal(helper, helper.council.membership.removeMemberCall(counselors.ildar.address));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot set/clear Council prime member', async ({helper}) => {
-    await testBadFellowshipProposal(helper, helper.council.membership.setPrimeCall(counselors.ildar.address));
-    await testBadFellowshipProposal(helper, helper.council.membership.clearPrimeCall());
-  });
-
-  itSub('[Negative] FellowshipProposition cannot add/remove a TechComm member', async ({helper}) => {
-    const [techCommNonMember] = await helper.arrange.createAccounts([10n], donor);
-
-    await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.addMemberCall(techCommNonMember.address));
-    await testBadFellowshipProposal(helper, helper.technicalCommittee.membership.removeMemberCall(techcomms.constantine.address));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot add/remove a Fellowship member', async ({helper}) => {
-    const [fellowshipNonMember] = await helper.arrange.createAccounts([10n], donor);
-
-    await testBadFellowshipProposal(helper, helper.fellowship.collective.addMemberCall(fellowshipNonMember.address));
-    await testBadFellowshipProposal(helper, helper.fellowship.collective.removeMemberCall(rank1Proposer.address, 1));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot promote/demote a Fellowship member', async ({helper}) => {
-    await testBadFellowshipProposal(helper, helper.fellowship.collective.promoteCall(rank1Proposer.address));
-    await testBadFellowshipProposal(helper, helper.fellowship.collective.demoteCall(rank1Proposer.address));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot fast-track Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await testBadFellowshipProposal(helper, helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot cancel Democracy proposals', async ({helper}) => {
-    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
-    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
-
-    await testBadFellowshipProposal(helper, helper.democracy.cancelProposalCall(proposalIndex));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot cancel ongoing Democracy referendums', async ({helper}) => {
-    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
-    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-    const referendumIndex = startedEvent.referendumIndex;
-
-    await testBadFellowshipProposal(helper, helper.democracy.emergencyCancelCall(referendumIndex));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot blacklist Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await testBadFellowshipProposal(helper, helper.democracy.blacklistCall(preimageHash, null));
-  });
-
-  itSub('[Negative] FellowshipProposition cannot veto external proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await testBadFellowshipProposal(helper, helper.democracy.vetoExternalCall(preimageHash));
-  });
-});
deletedjs-packages/tests/src/governance/init.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/governance/init.test.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js';
-import {Event} from '@unique/playgrounds/src/unique.dev.js';
-import {democracyLaunchPeriod, democracyVotingPeriod, democracyEnactmentPeriod, clearCouncil, clearTechComm, clearFellowship} from './util.js';
-import type {ICounselors, ITechComms} from './util.js';
-
-describeGov('Governance: Initialization', () => {
-  let donor: IKeyringPair;
-  let sudoer: IKeyringPair;
-  let counselors: ICounselors;
-  let techcomms: ITechComms;
-  let coreDevs: any;
-
-  const expectedAlexFellowRank = 7;
-  const expectedFellowRank = 6;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Democracy, Pallets.Council, Pallets.TechnicalCommittee]);
-
-      const councilMembers = await helper.council.membership.getMembers();
-      const techcommMembers = await helper.technicalCommittee.membership.getMembers();
-      expect(councilMembers.length == 0, 'The Council must be empty before the Gov Init');
-      expect(techcommMembers.length == 0, 'The Technical Commettee must be empty before the Gov Init');
-
-      donor = await privateKey({url: import.meta.url});
-      sudoer = await privateKey('//Alice');
-
-      const counselorsNum = 5;
-      const techCommsNum = 3;
-      const coreDevsNum = 2;
-      const [
-        alex,
-        ildar,
-        charu,
-        filip,
-        irina,
-
-        greg,
-        andy,
-        constantine,
-
-        yaroslav,
-        daniel,
-      ] = await helper.arrange.createAccounts(new Array(counselorsNum + techCommsNum + coreDevsNum).fill(10_000n), donor);
-
-      counselors = {
-        alex,
-        ildar,
-        charu,
-        filip,
-        irina,
-      };
-
-      techcomms = {
-        greg,
-        andy,
-        constantine,
-      };
-
-      coreDevs = {
-        yaroslav: yaroslav,
-        daniel: daniel,
-      };
-    });
-  });
-
-  itSub('Initialize Governance', async ({helper}) => {
-    const promoteFellow = (fellow: string, promotionsNum: number) => new Array(promotionsNum).fill(helper.fellowship.collective.promoteCall(fellow));
-
-    const expectFellowRank = async (fellow: string, expectedRank: number) => {
-      expect(await helper.fellowship.collective.getMemberRank(fellow)).to.be.equal(expectedRank);
-    };
-
-    console.log('\t- Setup the Prime of the Council via sudo');
-    await helper.getSudo().utility.batchAll(sudoer, [
-      helper.council.membership.addMemberCall(counselors.alex.address),
-      helper.council.membership.setPrimeCall(counselors.alex.address),
-
-      helper.fellowship.collective.addMemberCall(counselors.alex.address),
-      ...promoteFellow(counselors.alex.address, expectedAlexFellowRank),
-    ]);
-
-    let councilMembers = await helper.council.membership.getMembers();
-    const councilPrime = await helper.council.collective.getPrimeMember();
-    const alexFellowRank = await helper.fellowship.collective.getMemberRank(counselors.alex.address);
-    expect(councilMembers).to.be.deep.equal([counselors.alex.address]);
-    expect(councilPrime).to.be.equal(counselors.alex.address);
-    expect(alexFellowRank).to.be.equal(expectedAlexFellowRank);
-
-    console.log('\t- The Council Prime initializes the Technical Commettee');
-    const councilProposalThreshold = 1;
-
-    await helper.council.collective.propose(
-      counselors.alex,
-      helper.utility.batchAllCall([
-        helper.technicalCommittee.membership.addMemberCall(techcomms.greg.address),
-        helper.technicalCommittee.membership.addMemberCall(techcomms.andy.address),
-        helper.technicalCommittee.membership.addMemberCall(techcomms.constantine.address),
-
-        helper.technicalCommittee.membership.setPrimeCall(techcomms.greg.address),
-      ]),
-      councilProposalThreshold,
-    );
-
-    const techCommMembers = await helper.technicalCommittee.membership.getMembers();
-    const techCommPrime = await helper.technicalCommittee.membership.getPrimeMember();
-    const expectedTechComms = [techcomms.greg.address, techcomms.andy.address, techcomms.constantine.address];
-    expect(techCommMembers.length).to.be.equal(expectedTechComms.length);
-    expect(techCommMembers).to.containSubset(expectedTechComms);
-    expect(techCommPrime).to.be.equal(techcomms.greg.address);
-
-    console.log('\t- The Council Prime initiates a referendum to add counselors');
-    const returnPreimageHash = true;
-    const preimageHash = await helper.preimage.notePreimageFromCall(counselors.alex, helper.utility.batchAllCall([
-      helper.council.membership.addMemberCall(counselors.ildar.address),
-      helper.council.membership.addMemberCall(counselors.charu.address),
-      helper.council.membership.addMemberCall(counselors.filip.address),
-      helper.council.membership.addMemberCall(counselors.irina.address),
-
-      helper.fellowship.collective.addMemberCall(counselors.charu.address),
-      helper.fellowship.collective.addMemberCall(counselors.ildar.address),
-      helper.fellowship.collective.addMemberCall(counselors.irina.address),
-      helper.fellowship.collective.addMemberCall(counselors.filip.address),
-      helper.fellowship.collective.addMemberCall(techcomms.greg.address),
-      helper.fellowship.collective.addMemberCall(techcomms.andy.address),
-      helper.fellowship.collective.addMemberCall(techcomms.constantine.address),
-      helper.fellowship.collective.addMemberCall(coreDevs.yaroslav.address),
-      helper.fellowship.collective.addMemberCall(coreDevs.daniel.address),
-
-      ...promoteFellow(counselors.charu.address, expectedFellowRank),
-      ...promoteFellow(counselors.ildar.address, expectedFellowRank),
-      ...promoteFellow(counselors.irina.address, expectedFellowRank),
-      ...promoteFellow(counselors.filip.address, expectedFellowRank),
-      ...promoteFellow(techcomms.greg.address, expectedFellowRank),
-      ...promoteFellow(techcomms.andy.address, expectedFellowRank),
-      ...promoteFellow(techcomms.constantine.address, expectedFellowRank),
-      ...promoteFellow(coreDevs.yaroslav.address, expectedFellowRank),
-      ...promoteFellow(coreDevs.daniel.address, expectedFellowRank),
-    ]), returnPreimageHash);
-
-    await helper.council.collective.propose(
-      counselors.alex,
-      helper.democracy.externalProposeDefaultWithPreimageCall(preimageHash),
-      councilProposalThreshold,
-    );
-
-    console.log('\t- The referendum is being decided');
-    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-
-    await helper.democracy.vote(counselors.filip, startedEvent.referendumIndex, {
-      Standard: {
-        vote: {
-          aye: true,
-          conviction: 1,
-        },
-        balance: 10_000n,
-      },
-    });
-
-    const passedReferendumEvent = await helper.wait.expectEvent(democracyVotingPeriod, Event.Democracy.Passed);
-    expect(passedReferendumEvent.referendumIndex).to.be.equal(startedEvent.referendumIndex);
-
-    await helper.wait.expectEvent(democracyEnactmentPeriod, Event.Scheduler.Dispatched);
-
-    councilMembers = await helper.council.membership.getMembers();
-    const expectedCounselors = [
-      counselors.alex.address,
-      counselors.ildar.address,
-      counselors.charu.address,
-      counselors.filip.address,
-      counselors.irina.address,
-    ];
-    expect(councilMembers.length).to.be.equal(expectedCounselors.length);
-    expect(councilMembers).to.containSubset(expectedCounselors);
-
-    await expectFellowRank(counselors.ildar.address, expectedFellowRank);
-    await expectFellowRank(counselors.charu.address, expectedFellowRank);
-    await expectFellowRank(counselors.filip.address, expectedFellowRank);
-    await expectFellowRank(counselors.irina.address, expectedFellowRank);
-    await expectFellowRank(techcomms.greg.address, expectedFellowRank);
-    await expectFellowRank(techcomms.andy.address, expectedFellowRank);
-    await expectFellowRank(techcomms.constantine.address, expectedFellowRank);
-    await expectFellowRank(coreDevs.yaroslav.address, expectedFellowRank);
-    await expectFellowRank(coreDevs.daniel.address, expectedFellowRank);
-  });
-
-  after(async function() {
-    await clearFellowship(sudoer);
-    await clearTechComm(sudoer);
-    await clearCouncil(sudoer);
-  });
-});
deletedjs-packages/tests/src/governance/technicalCommittee.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/governance/technicalCommittee.test.ts
+++ /dev/null
@@ -1,383 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect, Pallets, requirePalletsOrSkip, describeGov} from '../util/index.js';
-import {Event} from '@unique/playgrounds/src/unique.dev.js';
-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';
-
-describeGov('Governance: Technical Committee tests', () => {
-  let sudoer: IKeyringPair;
-  let techcomms: ITechComms;
-  let donor: IKeyringPair;
-  let preImageHash: string;
-
-
-  const allTechCommitteeThreshold = 3;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.TechnicalCommittee]);
-      sudoer = await privateKey('//Alice');
-      donor = await privateKey({url: import.meta.url});
-
-      techcomms = await initTechComm(donor, sudoer);
-
-      const proposalCall = await helper.constructApiCall('api.tx.balances.forceSetBalance', [donor.address, 20n * 10n ** 25n]);
-      preImageHash = await helper.preimage.notePreimageFromCall(sudoer, proposalCall, true);
-    });
-  });
-
-  after(async () => {
-    await usingPlaygrounds(async (helper) => {
-      await clearTechComm(sudoer);
-
-      await helper.preimage.unnotePreimage(sudoer, preImageHash);
-      await hardResetFellowshipReferenda(sudoer);
-      await hardResetDemocracy(sudoer);
-      await hardResetGovScheduler(sudoer);
-    });
-  });
-
-  function proposalFromAllCommittee(proposal: any) {
-    return usingPlaygrounds(async (helper) => {
-      expect((await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length).to.be.equal(allTechCommitteeThreshold);
-      const proposeResult = await helper.technicalCommittee.collective.propose(
-        techcomms.andy,
-        proposal,
-        allTechCommitteeThreshold,
-      );
-
-      const commiteeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);
-      const proposalIndex = commiteeProposedEvent.proposalIndex;
-      const proposalHash = commiteeProposedEvent.proposalHash;
-
-
-      await helper.technicalCommittee.collective.vote(techcomms.andy, proposalHash, proposalIndex, true);
-      await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);
-      await helper.technicalCommittee.collective.vote(techcomms.greg, proposalHash, proposalIndex, true);
-
-      const closeResult = await helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex);
-      Event.TechnicalCommittee.Closed.expect(closeResult);
-      Event.TechnicalCommittee.Approved.expect(closeResult);
-      const {result} = Event.TechnicalCommittee.Executed.expect(closeResult);
-      expect(result).to.eq('Ok');
-
-      return closeResult;
-    });
-  }
-
-  itSub('TechComm can fast-track Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await helper.wait.parachainBlockMultiplesOf(35n);
-
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    const fastTrackProposal = await proposalFromAllCommittee(helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0));
-    Event.Democracy.Started.expect(fastTrackProposal);
-  });
-
-  itSub('TechComm can cancel Democracy proposals', async ({helper}) => {
-    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
-    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
-
-    const cancelProposal = await proposalFromAllCommittee(helper.democracy.cancelProposalCall(proposalIndex));
-    Event.Democracy.ProposalCanceled.expect(cancelProposal);
-  });
-
-  itSub('TechComm can cancel ongoing Democracy referendums', async ({helper}) => {
-    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
-    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-    const referendumIndex = startedEvent.referendumIndex;
-
-    const emergencyCancelProposal = await proposalFromAllCommittee(helper.democracy.emergencyCancelCall(referendumIndex));
-    Event.Democracy.Cancelled.expect(emergencyCancelProposal);
-  });
-
-  itSub('TechComm member can veto Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    const vetoExternalCall = await helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.democracy.vetoExternalCall(preimageHash),
-    );
-    Event.Democracy.Vetoed.expect(vetoExternalCall);
-  });
-
-  itSub('TechComm can cancel Fellowship referendums', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-    const fellowshipProposer = fellowship[5][0];
-    const proposal = dummyProposal(helper);
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      fellowshipProposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-    const cancelProposal = await proposalFromAllCommittee(helper.fellowship.referenda.cancelCall(referendumIndex));
-    Event.FellowshipReferenda.Cancelled.expect(cancelProposal);
-  });
-
-  itSub.skip('TechComm member can add a Fellowship member', async ({helper}) => {
-    const newFellowshipMember = helper.arrange.createEmptyAccount();
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.fellowship.collective.addMemberCall(newFellowshipMember.address),
-    )).to.be.fulfilled;
-    const fellowshipMembers = (await helper.callRpc('api.query.fellowshipCollective.members')).toJSON();
-    expect(fellowshipMembers).to.contains(newFellowshipMember.address);
-    await clearFellowship(sudoer);
-  });
-
-  itSub('[Negative] TechComm cannot submit regular democracy proposal', async ({helper}) => {
-    const councilProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
-
-    await expect(proposalFromAllCommittee(councilProposal)).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm cannot externally propose SuperMajorityAgainst', async ({helper}) => {
-    const commiteeProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));
-
-    await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm cannot externally propose SimpleMajority', async ({helper}) => {
-    const commiteeProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
-
-    await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm cannot externally propose SuperMajorityApprove', async ({helper}) => {
-    const commiteeProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
-
-    await expect(proposalFromAllCommittee(commiteeProposal)).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot submit regular democracy proposal', async ({helper}) => {
-    const memberProposal = await helper.democracy.proposeCall(dummyProposalCall(helper), 0n);
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      memberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot externally propose SuperMajorityAgainst', async ({helper}) => {
-    const memberProposal = await helper.democracy.externalProposeDefaultCall(dummyProposalCall(helper));
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      memberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot externally propose SimpleMajority', async ({helper}) => {
-    const memberProposal = await helper.democracy.externalProposeMajorityCall(dummyProposalCall(helper));
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      memberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot externally propose SuperMajorityApprove', async ({helper}) => {
-    const memberProposal = await helper.democracy.externalProposeCall(dummyProposalCall(helper));
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      memberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-
-  itSub.skip('[Negative] TechComm cannot promote/demote Fellowship member', async () => {
-
-  });
-
-  itSub.skip('[Negative] TechComm member cannot promote/demote Fellowship member', async () => {
-
-  });
-
-  itSub('[Negative] TechComm cannot add/remove a Council member', async ({helper}) => {
-    const newCouncilMember = helper.arrange.createEmptyAccount();
-    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
-    const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);
-
-    await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');
-    await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot add/remove a Council member', async ({helper}) => {
-    const newCouncilMember = helper.arrange.createEmptyAccount();
-    const addMemberProposal = helper.council.membership.addMemberCall(newCouncilMember.address);
-    const removeMemberProposal = helper.council.membership.removeMemberCall(newCouncilMember.address);
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      addMemberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      removeMemberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm cannot set/clear Council prime member', async ({helper}) => {
-    const counselors = await initCouncil(donor, sudoer);
-    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
-    const proposalForClear = await helper.council.membership.clearPrimeCall();
-
-    await expect(proposalFromAllCommittee(proposalForSet)).to.be.rejectedWith('BadOrigin');
-    await expect(proposalFromAllCommittee(proposalForClear)).to.be.rejectedWith('BadOrigin');
-    await clearCouncil(sudoer);
-  });
-
-  itSub('[Negative] TechComm member cannot set/clear Council prime member', async ({helper}) => {
-    const counselors = await initCouncil(donor, sudoer);
-    const proposalForSet = await helper.council.membership.setPrimeCall(counselors.charu.address);
-    const proposalForClear = await helper.council.membership.clearPrimeCall();
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      proposalForSet,
-    )).to.be.rejectedWith('BadOrigin');
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      proposalForClear,
-    )).to.be.rejectedWith('BadOrigin');
-    await clearCouncil(sudoer);
-  });
-
-  itSub('[Negative] TechComm cannot add/remove a TechComm member', async ({helper}) => {
-    const newCommMember = helper.arrange.createEmptyAccount();
-    const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);
-    const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);
-
-    await expect(proposalFromAllCommittee(addMemberProposal)).to.be.rejectedWith('BadOrigin');
-    await expect(proposalFromAllCommittee(removeMemberProposal)).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot add/remove a TechComm member', async ({helper}) => {
-    const newCommMember = helper.arrange.createEmptyAccount();
-    const addMemberProposal = helper.council.membership.addMemberCall(newCommMember.address);
-    const removeMemberProposal = helper.council.membership.removeMemberCall(newCommMember.address);
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      addMemberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      removeMemberProposal,
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm cannot remove a Fellowship member', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-
-    await expect(proposalFromAllCommittee(helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5))).to.be.rejectedWith('BadOrigin');
-    await clearFellowship(sudoer);
-  });
-
-  itSub('[Negative] TechComm member cannot remove a Fellowship member', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.fellowship.collective.removeMemberCall(fellowship[5][0].address, 5),
-    )).to.be.rejectedWith('BadOrigin');
-    await clearFellowship(sudoer);
-  });
-
-  itSub('[Negative] TechComm member cannot fast-track Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.democracy.fastTrackCall(preimageHash, democracyFastTrackVotingPeriod, 0),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot cancel Democracy proposals', async ({helper}) => {
-    const proposeResult = await helper.getSudo().democracy.propose(sudoer, dummyProposalCall(helper), 0n);
-    const proposalIndex = Event.Democracy.Proposed.expect(proposeResult).proposalIndex;
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.democracy.cancelProposalCall(proposalIndex),
-    ))
-      .to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot cancel ongoing Democracy referendums', async ({helper}) => {
-    await helper.getSudo().democracy.externalProposeDefault(sudoer, dummyProposalCall(helper));
-    const startedEvent = await helper.wait.expectEvent(democracyLaunchPeriod, Event.Democracy.Started);
-    const referendumIndex = startedEvent.referendumIndex;
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.democracy.emergencyCancelCall(referendumIndex),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm cannot blacklist Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await expect(proposalFromAllCommittee(helper.democracy.blacklistCall(preimageHash))).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm member cannot blacklist Democracy proposals', async ({helper}) => {
-    const preimageHash = await helper.preimage.notePreimageFromCall(sudoer, dummyProposalCall(helper), true);
-    await helper.getSudo().democracy.externalProposeDefaultWithPreimage(sudoer, preimageHash);
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.democracy.blacklistCall(preimageHash),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub.skip('[Negative] TechComm member cannot veto external Democracy proposals until the cool-off period pass', async () => {
-
-  });
-
-  itSub('[Negative] TechComm member cannot cancel Fellowship referendums', async ({helper}) => {
-    const fellowship = await initFellowship(donor, sudoer);
-    const fellowshipProposer = fellowship[5][0];
-    const proposal = dummyProposal(helper);
-
-    const submitResult = await helper.fellowship.referenda.submit(
-      fellowshipProposer,
-      fellowshipPropositionOrigin,
-      proposal,
-      defaultEnactmentMoment,
-    );
-
-    const referendumIndex = Event.FellowshipReferenda.Submitted.expect(submitResult).referendumIndex;
-
-    await expect(helper.technicalCommittee.collective.execute(
-      techcomms.andy,
-      helper.fellowship.referenda.cancelCall(referendumIndex),
-    )).to.be.rejectedWith('BadOrigin');
-  });
-
-  itSub('[Negative] TechComm referendum cannot be closed until the voting threshold is met', async ({helper}) => {
-    const committeeSize = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON().length as any as number;
-    expect(committeeSize).is.greaterThan(1);
-    const proposeResult = await helper.technicalCommittee.collective.propose(
-      techcomms.andy,
-      dummyProposalCall(helper),
-      committeeSize,
-    );
-
-    const committeeProposedEvent = Event.TechnicalCommittee.Proposed.expect(proposeResult);
-    const proposalIndex = committeeProposedEvent.proposalIndex;
-    const proposalHash = committeeProposedEvent.proposalHash;
-
-    await helper.technicalCommittee.collective.vote(techcomms.constantine, proposalHash, proposalIndex, true);
-
-    await expect(helper.technicalCommittee.collective.close(techcomms.andy, proposalHash, proposalIndex)).to.be.rejectedWith('TooEarly');
-  });
-});
deletedjs-packages/tests/src/governance/util.tsdiffbeforeafterboth
--- a/js-packages/tests/src/governance/util.ts
+++ /dev/null
@@ -1,224 +0,0 @@
-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/src/unique.js';
-import {DevUniqueHelper} from '@unique/playgrounds/src/unique.dev.js';
-
-export const democracyLaunchPeriod = 35;
-export const democracyVotingPeriod = 35;
-export const councilMotionDuration = 35;
-export const democracyEnactmentPeriod = 40;
-export const democracyFastTrackVotingPeriod = 5;
-
-export const fellowshipRankLimit = 7;
-export const fellowshipPropositionOrigin = 'FellowshipProposition';
-export const fellowshipPreparePeriod = 3;
-export const fellowshipConfirmPeriod = 3;
-export const fellowshipMinEnactPeriod = 1;
-
-export const defaultEnactmentMoment = {After: 0};
-
-export const democracyTrackId = 10;
-export const democracyTrackMinRank = 3;
-const twox128 = (data: any) => xxhashAsHex(data, 128);
-export interface ICounselors {
-  alex: IKeyringPair;
-  ildar: IKeyringPair;
-  charu: IKeyringPair;
-  filip: IKeyringPair;
-  irina: IKeyringPair;
-}
-export interface ITechComms {
-    greg: IKeyringPair;
-    andy: IKeyringPair;
-    constantine: IKeyringPair;
-}
-
-export async function initCouncil(donor: IKeyringPair, superuser: IKeyringPair) {
-  let counselors: IKeyringPair[] = [];
-
-  await usingPlaygrounds(async (helper) => {
-    const [alex, ildar, charu, filip, irina] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n, 10_000n, 10_000n], donor);
-    const sudo = helper.getSudo();
-    {
-      const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON() as [];
-      if(members.length != 0) {
-        await clearCouncil(superuser);
-      }
-    }
-    const expectedMembers = [alex, ildar, charu, filip, irina];
-    for(const member of expectedMembers) {
-      await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.addMember', [member.address]);
-    }
-    await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.setPrime', [alex.address]);
-    {
-      const members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
-      expect(members).to.containSubset(expectedMembers.map((x: IKeyringPair) => x.address));
-      expect(members.length).to.be.equal(expectedMembers.length);
-    }
-
-    counselors = [alex, ildar, charu, filip, irina];
-  });
-  return {
-    alex: counselors[0],
-    ildar: counselors[1],
-    charu: counselors[2],
-    filip: counselors[3],
-    irina: counselors[4],
-  };
-}
-
-export async function clearCouncil(superuser: IKeyringPair) {
-  await usingPlaygrounds(async (helper) => {
-    let members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
-    if(members.length) {
-      const sudo = helper.getSudo();
-      for(const address of members) {
-        await sudo.executeExtrinsic(superuser, 'api.tx.councilMembership.removeMember', [address]);
-      }
-      members = (await helper.callRpc('api.query.councilMembership.members')).toJSON();
-    }
-    expect(members).to.be.deep.equal([]);
-  });
-}
-
-
-export async function initTechComm(donor: IKeyringPair, superuser: IKeyringPair) {
-  let techcomms: IKeyringPair[] = [];
-
-  await usingPlaygrounds(async (helper) => {
-    const [greg, andy, constantine] = await helper.arrange.createAccounts([10_000n, 10_000n, 10_000n], donor);
-    const sudo = helper.getSudo();
-    {
-      const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON() as [];
-      if(members.length != 0) {
-        await clearTechComm(superuser);
-      }
-    }
-    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [greg.address]);
-    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [andy.address]);
-    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.addMember', [constantine.address]);
-    await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.setPrime', [greg.address]);
-    {
-      const members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
-      expect(members).to.containSubset([greg.address, andy.address, constantine.address]);
-      expect(members.length).to.be.equal(3);
-    }
-
-    techcomms = [greg, andy, constantine];
-  });
-
-  return {
-    greg: techcomms[0],
-    andy: techcomms[1],
-    constantine: techcomms[2],
-  };
-}
-
-export async function clearTechComm(superuser: IKeyringPair) {
-  await usingPlaygrounds(async (helper) => {
-    let members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
-    if(members.length) {
-      const sudo = helper.getSudo();
-      for(const address of members) {
-        await sudo.executeExtrinsic(superuser, 'api.tx.technicalCommitteeMembership.removeMember', [address]);
-      }
-      members = (await helper.callRpc('api.query.technicalCommitteeMembership.members')).toJSON();
-    }
-    expect(members).to.be.deep.equal([]);
-  });
-}
-
-export async function initFellowship(donor: IKeyringPair, sudoer: IKeyringPair) {
-  const numMembersInRank = 3;
-  const memberBalance = 5000n;
-  const members: IKeyringPair[][] = [];
-
-  await usingPlaygrounds(async (helper) => {
-    const currentFellows = await helper.getApi().query.fellowshipCollective.members.keys();
-
-    if(currentFellows.length != 0) {
-      await clearFellowship(sudoer);
-    }
-    for(let i = 0; i < fellowshipRankLimit; i++) {
-      const rankMembers = await helper.arrange.createAccounts(
-        Array(numMembersInRank).fill(memberBalance),
-        donor,
-      );
-
-      for(const member of rankMembers) {
-        await helper.getSudo().fellowship.collective.addMember(sudoer, member.address);
-
-        for(let rank = 0; rank < i; rank++) {
-          await helper.getSudo().fellowship.collective.promote(sudoer, member.address);
-        }
-      }
-
-      members.push(rankMembers);
-    }
-  });
-
-  return members;
-}
-
-export async function clearFellowship(sudoer: IKeyringPair) {
-  await usingPlaygrounds(async (helper) => {
-    const fellowship = (await helper.getApi().query.fellowshipCollective.members.keys())
-      .map((key) => key.args[0].toString());
-    for(const  member of fellowship) {
-      await helper.getSudo().fellowship.collective.removeMember(sudoer, member, fellowshipRankLimit);
-    }
-  });
-}
-
-export async function clearFellowshipReferenda(sudoer: IKeyringPair) {
-  await usingPlaygrounds(async (helper) => {
-    const proposalsCount = (await helper.getApi().query.fellowshipReferenda.referendumCount()) as u32;
-    for(let i = 0; i < proposalsCount.toNumber(); i++) {
-      await helper.getSudo().fellowship.referenda.cancel(sudoer, i);
-    }
-  });
-}
-
-export async function hardResetFellowshipReferenda(sudoer: IKeyringPair) {
-  await usingPlaygrounds(async (helper) => {
-    const api = helper.getApi();
-    const prefix = twox128('FellowshipReferenda');
-    await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));
-  });
-}
-
-export async function hardResetDemocracy(sudoer: IKeyringPair) {
-  await usingPlaygrounds(async (helper) => {
-    const api = helper.getApi();
-    const prefix = twox128('Democracy');
-    await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 100)));
-  });
-}
-
-export async function hardResetGovScheduler(sudoer: IKeyringPair) {
-  await usingPlaygrounds(async (helper) => {
-    const api = helper.getApi();
-    const prefix = twox128('GovScheduler');
-    await helper.signTransaction(sudoer, api.tx.sudo.sudo(api.tx.system.killPrefix(prefix, 500)));
-  });
-}
-
-export async function voteUnanimouslyInFellowship(helper: DevUniqueHelper, fellows: IKeyringPair[][], minRank: number, referendumIndex: number) {
-  for(let rank = minRank; rank < fellowshipRankLimit; rank++) {
-    for(const member of fellows[rank]) {
-      await helper.fellowship.collective.vote(member, referendumIndex, true);
-    }
-  }
-}
-
-export function dummyProposalCall(helper: UniqueHelper) {
-  return helper.constructApiCall('api.tx.system.remark', ['dummy proposal' + (new Date()).getTime()]);
-}
-
-export function dummyProposal(helper: UniqueHelper) {
-  return {
-    Inline: dummyProposalCall(helper).method.toHex(),
-  };
-}
deletedjs-packages/tests/src/inflation.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/inflation.seqtest.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util/index.js';
-
-// todo:playgrounds requires sudo, look into on the later stage
-describe('integration test: Inflation', () => {
-  let superuser: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (_, privateKey) => {
-      superuser = await privateKey('//Alice');
-    });
-  });
-
-  itSub('First year inflation is 10%', async ({helper}) => {
-    // Make sure non-sudo can't start inflation
-    const [bob] = await helper.arrange.createAccounts([10n], superuser);
-
-    await expect(helper.executeExtrinsic(bob, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
-
-    // Make sure superuser can't start inflation without explicit sudo
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.inflation.startInflation', [1])).to.be.rejectedWith(/BadOrigin/);
-
-    // Start inflation on relay block 1 (Alice is sudo)
-    const tx = helper.constructApiCall('api.tx.inflation.startInflation', [1]);
-    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [tx])).to.not.be.rejected;
-
-    const blockInterval = (helper.getApi().consts.inflation.inflationBlockInterval as any).toBigInt();
-    const totalIssuanceStart = ((await helper.callRpc('api.query.inflation.startingYearTotalIssuance', [])) as any).toBigInt();
-    const blockInflation = (await helper.callRpc('api.query.inflation.blockInflation', []) as any).toBigInt();
-
-    const YEAR = 5259600n;  // 6-second block. Blocks in one year
-    // const YEAR = 2629800n; // 12-second block. Blocks in one year
-
-    const totalExpectedInflation = totalIssuanceStart / 10n;
-    const totalActualInflation = blockInflation * YEAR / blockInterval;
-
-    const tolerance = 0.00001; // Relative difference per year between theoretical and actual inflation
-    const expectedInflation = totalExpectedInflation / totalActualInflation - 1n;
-
-    expect(Math.abs(Number(expectedInflation))).to.be.lessThanOrEqual(tolerance);
-  });
-});
deletedjs-packages/tests/src/limits.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/limits.test.ts
+++ /dev/null
@@ -1,474 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js';
-
-describe('Number of tokens per address (NFT)', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
-    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});
-
-    for(let i = 0; i < 10; i++){
-      await expect(collection.mintToken(alice)).to.be.not.rejected;
-    }
-    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
-    for(let i = 1; i < 11; i++) {
-      await expect(collection.burnToken(alice, i)).to.be.not.rejected;
-    }
-    await collection.burn(alice);
-  });
-
-  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
-    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});
-
-    await collection.mintToken(alice);
-    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
-
-    await collection.burnToken(alice, 1);
-    await expect(collection.burn(alice)).to.be.not.rejected;
-  });
-});
-
-describe('Number of tokens per address (ReFungible)', () => {
-  let alice: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itSub.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    await collection.setLimits(alice, {accountTokenOwnershipLimit: 20});
-
-    for(let i = 0; i < 10; i++){
-      await expect(collection.mintToken(alice, 10n)).to.be.not.rejected;
-    }
-    await expect(collection.mintToken(alice, 10n)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
-    for(let i = 1; i < 11; i++) {
-      await expect(collection.burnToken(alice, i, 10n)).to.be.not.rejected;
-    }
-    await collection.burn(alice);
-  });
-
-  itSub('Collection limits allow lower number than chain limits, collection limits are enforced', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    await collection.setLimits(alice, {accountTokenOwnershipLimit: 1});
-
-    await collection.mintToken(alice);
-    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
-
-    await collection.burnToken(alice, 1);
-    await expect(collection.burn(alice)).to.be.not.rejected;
-  });
-});
-
-// todo:playgrounds skipped ~ postponed
-describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => {
-  /*let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingApi(async (api, privateKeyWrapper) => {
-      alice = privateKeyWrapper('//Alice');
-      bob = privateKeyWrapper('//Bob');
-      charlie = privateKeyWrapper('//Charlie');
-    });
-  });
-
-  itSub.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
-    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});
-    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
-    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
-    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
-    await transferExpectSuccess(collectionId, tokenId, alice, bob);
-    const aliceBalanceBefore = await getFreeBalance(alice);
-
-    // check setting SponsorTimeout = 5, fail
-    await waitNewBlocks(5);
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie);
-    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
-
-    // check setting SponsorTimeout = 7, success
-    await waitNewBlocks(2); // 5 + 2
-    await transferExpectSuccess(collectionId, tokenId, charlie, bob);
-    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
-    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
-    await destroyCollectionExpectSuccess(collectionId);
-  });
-
-  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {
-
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
-    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});
-    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
-    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
-    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
-    await transferExpectSuccess(collectionId, tokenId, alice, bob);
-    const aliceBalanceBefore = await getFreeBalance(alice);
-
-    // check setting SponsorTimeout = 1, fail
-    await waitNewBlocks(1);
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie);
-    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
-
-    // check setting SponsorTimeout = 5, success
-    await waitNewBlocks(4);
-    await transferExpectSuccess(collectionId, tokenId, charlie, bob);
-    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
-    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
-    await destroyCollectionExpectSuccess(collectionId);
-  });
-});
-
-describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingApi(async (api, privateKeyWrapper) => {
-      alice = privateKeyWrapper('//Alice');
-      bob = privateKeyWrapper('//Bob');
-      charlie = privateKeyWrapper('//Charlie');
-    });
-  });
-
-  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});
-    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');
-    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
-    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
-    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
-    const aliceBalanceBefore = await getFreeBalance(alice);
-
-    // check setting SponsorTimeout = 5, fail
-    await waitNewBlocks(5);
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
-    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
-
-    // check setting SponsorTimeout = 7, success
-    await waitNewBlocks(2); // 5 + 2
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
-    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
-    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
-
-    await destroyCollectionExpectSuccess(collectionId);
-  });
-
-  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {
-
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
-    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});
-    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');
-    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
-    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
-    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
-    const aliceBalanceBefore = await getFreeBalance(alice);
-
-    // check setting SponsorTimeout = 1, fail
-    await waitNewBlocks(1);
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
-    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
-
-    // check setting SponsorTimeout = 5, success
-    await waitNewBlocks(4);
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');
-    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
-    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
-
-    await destroyCollectionExpectSuccess(collectionId);
-  });
-});
-
-describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingApi(async (api, privateKeyWrapper) => {
-      alice = privateKeyWrapper('//Alice');
-      bob = privateKeyWrapper('//Bob');
-      charlie = privateKeyWrapper('//Charlie');
-    });
-  });
-
-  itSub('Collection limits have greater timeout value than chain limits, collection limits are enforced', async ({helper}) => {
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
-    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});
-    const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible');
-    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
-    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
-    await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible');
-    const aliceBalanceBefore = await getFreeBalance(alice);
-
-    // check setting SponsorTimeout = 5, fail
-    await waitNewBlocks(5);
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');
-    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
-
-    // check setting SponsorTimeout = 7, success
-    await waitNewBlocks(2); // 5 + 2
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');
-    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
-    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
-    await destroyCollectionExpectSuccess(collectionId);
-  });
-
-  itSub('Collection limits have lower timeout value than chain limits, chain limits are enforced', async ({helper}) => {
-
-    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
-    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});
-    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
-    await setCollectionSponsorExpectSuccess(collectionId, alice.address);
-    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');
-    await transferExpectSuccess(collectionId, tokenId, alice, bob);
-    const aliceBalanceBefore = await getFreeBalance(alice);
-
-    // check setting SponsorTimeout = 1, fail
-    await waitNewBlocks(1);
-    await transferExpectSuccess(collectionId, tokenId, bob, charlie);
-    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);
-
-    // check setting SponsorTimeout = 5, success
-    await waitNewBlocks(4);
-    await transferExpectSuccess(collectionId, tokenId, charlie, bob);
-    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);
-    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;
-    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);
-    await destroyCollectionExpectSuccess(collectionId);
-  });*/
-});
-
-describe('Collection zero limits (NFT)', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    });
-  });
-
-  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
-    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});
-
-    for(let i = 0; i < 10; i++){
-      await collection.mintToken(alice);
-    }
-    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
-  });
-
-  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
-    await collection.setLimits(alice, {sponsorTransferTimeout: 0});
-    const token = await collection.mintToken(alice);
-
-    await collection.setSponsor(alice, alice.address);
-    await collection.confirmSponsorship(alice);
-
-    await token.transfer(alice, {Substrate: bob.address});
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-
-    // check setting SponsorTimeout = 0, success with next block
-    await helper.wait.newBlocks(1);
-    await token.transfer(bob, {Substrate: charlie.address});
-    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);
-    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;
-  });
-});
-
-describe('Collection zero limits (Fungible)', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {});
-    await collection.setLimits(alice, {sponsorTransferTimeout: 0});
-    await collection.mint(alice, 3n);
-
-    await collection.setSponsor(alice, alice.address);
-    await collection.confirmSponsorship(alice);
-
-    await collection.transfer(alice, {Substrate: bob.address}, 2n);
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-
-    // check setting SponsorTimeout = 0, success with next block
-    await helper.wait.newBlocks(1);
-    await collection.transfer(bob, {Substrate: charlie.address});
-    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);
-    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;
-  });
-});
-
-describe('Collection zero limits (ReFungible)', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-    });
-  });
-
-  itSub.skip('Limits have 0 in tokens per address field, the chain limits are applied', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    await collection.setLimits(alice, {accountTokenOwnershipLimit: 0});
-    for(let i = 0; i < 10; i++){
-      await collection.mintToken(alice);
-    }
-    await expect(collection.mintToken(alice)).to.be.rejectedWith(/common\.AccountTokenLimitExceeded/);
-  });
-
-  itSub('Limits have 0 in sponsor timeout, no limits are applied', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    await collection.setLimits(alice, {sponsorTransferTimeout: 0});
-    const token = await collection.mintToken(alice, 3n);
-
-    await collection.setSponsor(alice, alice.address);
-    await collection.confirmSponsorship(alice);
-
-    await token.transfer(alice, {Substrate: bob.address}, 2n);
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-
-    // check setting SponsorTimeout = 0, success with next block
-    await helper.wait.newBlocks(1);
-    await token.transfer(bob, {Substrate: charlie.address});
-    const aliceBalanceAfterSponsoredTransaction1 = await helper.balance.getSubstrate(alice.address);
-    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;
-  });
-});
-
-describe('Effective collection limits (NFT)', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itSub('Effective collection limits', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {});
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-
-    {
-      // Check that limits are undefined
-      const collectionInfo = await collection.getData();
-      const limits = collectionInfo?.raw.limits;
-      expect(limits).to.be.any;
-
-      expect(limits.accountTokenOwnershipLimit).to.be.null;
-      expect(limits.sponsoredDataSize).to.be.null;
-      expect(limits.sponsoredDataRateLimit).to.be.null;
-      expect(limits.tokenLimit).to.be.null;
-      expect(limits.sponsorTransferTimeout).to.be.null;
-      expect(limits.sponsorApproveTimeout).to.be.null;
-      expect(limits.ownerCanTransfer).to.be.true;
-      expect(limits.ownerCanDestroy).to.be.null;
-      expect(limits.transfersEnabled).to.be.null;
-    }
-
-    { // Check that limits is undefined for non-existent collection
-      const limits = await helper.collection.getEffectiveLimits(999999);
-      expect(limits).to.be.null;
-    }
-
-    { // Check that default values defined for collection limits
-      const limits = await collection.getEffectiveLimits();
-
-      expect(limits.accountTokenOwnershipLimit).to.be.eq(100000);
-      expect(limits.sponsoredDataSize).to.be.eq(2048);
-      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});
-      expect(limits.tokenLimit).to.be.eq(4294967295);
-      expect(limits.sponsorTransferTimeout).to.be.eq(5);
-      expect(limits.sponsorApproveTimeout).to.be.eq(5);
-      expect(limits.ownerCanTransfer).to.be.true;
-      expect(limits.ownerCanDestroy).to.be.true;
-      expect(limits.transfersEnabled).to.be.true;
-    }
-
-    {
-      // Check the values for collection limits
-      await collection.setLimits(alice, {
-        accountTokenOwnershipLimit: 99_999,
-        sponsoredDataSize: 1024,
-        tokenLimit: 123,
-        transfersEnabled: false,
-      });
-
-      const limits = await collection.getEffectiveLimits();
-
-      expect(limits.accountTokenOwnershipLimit).to.be.eq(99999);
-      expect(limits.sponsoredDataSize).to.be.eq(1024);
-      expect(limits.sponsoredDataRateLimit).to.be.deep.eq({sponsoringDisabled: null});
-      expect(limits.tokenLimit).to.be.eq(123);
-      expect(limits.sponsorTransferTimeout).to.be.eq(5);
-      expect(limits.sponsorApproveTimeout).to.be.eq(5);
-      expect(limits.ownerCanTransfer).to.be.true;
-      expect(limits.ownerCanDestroy).to.be.true;
-      expect(limits.transfersEnabled).to.be.false;
-    }
-  });
-});
deletedjs-packages/tests/src/maintenance.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/maintenance.seqtest.ts
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {ApiPromise} from '@polkadot/api';
-import {expect, itSched, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from './util/index.js';
-import {itEth} from './eth/util/index.js';
-import {main as correctState} from './migrations/correctStateAfterMaintenance.js';
-import type {PalletBalancesIdAmount} from '@polkadot/types/lookup';
-import type {Vec} from '@polkadot/types-codec';
-
-async function maintenanceEnabled(api: ApiPromise): Promise<boolean> {
-  return (await api.query.maintenance.enabled()).toJSON() as boolean;
-}
-
-describe('Integration Test: Maintenance Functionality', () => {
-  let superuser: IKeyringPair;
-  let donor: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.Maintenance]);
-      superuser = await privateKey('//Alice');
-      donor = await privateKey({url: import.meta.url});
-      [bob] = await helper.arrange.createAccounts([10000n], donor);
-
-    });
-  });
-
-  describe('Maintenance Mode', () => {
-    before(async function() {
-      await usingPlaygrounds(async (helper) => {
-        if(await maintenanceEnabled(helper.getApi())) {
-          console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.');
-          await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled;
-        }
-      });
-    });
-
-    itSub('Allows superuser to enable and disable maintenance mode - and disallows anyone else', async ({helper}) => {
-      // Make sure non-sudo can't enable maintenance mode
-      await expect(helper.executeExtrinsic(superuser, 'api.tx.maintenance.enable', []), 'on commoner enabling MM')
-        .to.be.rejectedWith(/BadOrigin/);
-
-      // Set maintenance mode
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
-
-      // Make sure non-sudo can't disable maintenance mode
-      await expect(helper.executeExtrinsic(bob, 'api.tx.maintenance.disable', []), 'on commoner disabling MM')
-        .to.be.rejectedWith(/BadOrigin/);
-
-      // Disable maintenance mode
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
-    });
-
-    itSub('MM blocks unique pallet calls', async ({helper}) => {
-      // Can create an NFT collection before enabling the MM
-      const nftCollection = await helper.nft.mintCollection(bob, {
-        tokenPropertyPermissions: [{
-          key: 'test', permission: {
-            collectionAdmin: true,
-            tokenOwner: true,
-            mutable: true,
-          },
-        }],
-      });
-
-      // Can mint an NFT before enabling the MM
-      const nft = await nftCollection.mintToken(bob);
-
-      // Can create an FT collection before enabling the MM
-      const ftCollection = await helper.ft.mintCollection(superuser);
-
-      // Can mint an FT before enabling the MM
-      await expect(ftCollection.mint(superuser)).to.be.fulfilled;
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
-
-      // Unable to create a collection when the MM is enabled
-      await expect(helper.nft.mintCollection(superuser), 'cudo forbidden stuff')
-        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
-
-      // Unable to set token properties when the MM is enabled
-      await expect(nft.setProperties(
-        bob,
-        [{key: 'test', value: 'test-val'}],
-      )).to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
-
-      // Unable to mint an NFT when the MM is enabled
-      await expect(nftCollection.mintToken(superuser))
-        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
-
-      // Unable to mint an FT when the MM is enabled
-      await expect(ftCollection.mint(superuser))
-        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
-
-      // Can create a collection after disabling the MM
-      await expect(helper.nft.mintCollection(bob), 'MM is disabled, the collection should be created').to.be.fulfilled;
-
-      // Can set token properties after disabling the MM
-      await nft.setProperties(bob, [{key: 'test', value: 'test-val'}]);
-
-      // Can mint an NFT after disabling the MM
-      await nftCollection.mintToken(bob);
-
-      // Can mint an FT after disabling the MM
-      await ftCollection.mint(superuser);
-    });
-
-    itSub.ifWithPallets('MM blocks unique pallet calls (Re-Fungible)', [Pallets.ReFungible], async ({helper}) => {
-      // Can create an RFT collection before enabling the MM
-      const rftCollection = await helper.rft.mintCollection(superuser);
-
-      // Can mint an RFT before enabling the MM
-      await expect(rftCollection.mintToken(superuser)).to.be.fulfilled;
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
-
-      // Unable to mint an RFT when the MM is enabled
-      await expect(rftCollection.mintToken(superuser))
-        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
-
-      // Can mint an RFT after disabling the MM
-      await rftCollection.mintToken(superuser);
-    });
-
-    itSub('MM allows native token transfers and RPC calls', async ({helper}) => {
-      // We can use RPC before the MM is enabled
-      const totalCount = await helper.collection.getTotalCount();
-
-      // We can transfer funds before the MM is enabled
-      await expect(helper.balance.transferToSubstrate(superuser, bob.address, 2n)).to.be.fulfilled;
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
-
-      // RPCs work while in maintenance
-      expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount);
-
-      // We still able to transfer funds
-      await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled;
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
-
-      // RPCs work after maintenance
-      expect(await helper.collection.getTotalCount()).to.be.deep.equal(totalCount);
-
-      // Transfers work after maintenance
-      await expect(helper.balance.transferToSubstrate(bob, superuser.address, 1n)).to.be.fulfilled;
-    });
-
-    itSched.ifWithPallets('MM blocks scheduled calls and the scheduler itself', [Pallets.UniqueScheduler], async (scheduleKind, {helper}) => {
-      const collection = await helper.nft.mintCollection(bob);
-
-      const nftBeforeMM = await collection.mintToken(bob);
-      const nftDuringMM = await collection.mintToken(bob);
-      const nftAfterMM = await collection.mintToken(bob);
-
-      const [
-        scheduledIdBeforeMM,
-        scheduledIdDuringMM,
-        scheduledIdBunkerThroughMM,
-        scheduledIdAttemptDuringMM,
-        scheduledIdAfterMM,
-      ] = scheduleKind == 'named'
-        ? helper.arrange.makeScheduledIds(5)
-        : new Array(5);
-
-      const blocksToWait = 6;
-
-      // Scheduling works before the maintenance
-      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdBeforeMM})
-        .nft.transferToken(bob, collection.collectionId, nftBeforeMM.tokenId, {Substrate: superuser.address});
-
-
-      await helper.wait.newBlocks(blocksToWait + 1);
-      expect(await nftBeforeMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
-
-      // Schedule a transaction that should occur *during* the maintenance
-      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdDuringMM})
-        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
-
-
-      // Schedule a transaction that should occur *after* the maintenance
-      await helper.scheduler.scheduleAfter(blocksToWait * 2, {scheduledId: scheduledIdBunkerThroughMM})
-        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address});
-
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
-
-      await helper.wait.newBlocks(blocksToWait + 1);
-      // The owner should NOT change since the scheduled transaction should be rejected
-      expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: bob.address});
-
-      // Any attempts to schedule a tx during the MM should be rejected
-      await expect(helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAttemptDuringMM})
-        .nft.transferToken(bob, collection.collectionId, nftDuringMM.tokenId, {Substrate: superuser.address}))
-        .to.be.rejectedWith(/Invalid Transaction: Transaction call is not expected/);
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
-
-      // Scheduling works after the maintenance
-      await helper.scheduler.scheduleAfter(blocksToWait, {scheduledId: scheduledIdAfterMM})
-        .nft.transferToken(bob, collection.collectionId, nftAfterMM.tokenId, {Substrate: superuser.address});
-
-      await helper.wait.newBlocks(blocksToWait + 1);
-
-      expect(await nftAfterMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
-      // The owner of the token scheduled for transaction *before* maintenance should now change *after* maintenance
-      expect(await nftDuringMM.getOwner()).to.be.deep.equal({Substrate: superuser.address});
-    });
-
-    itEth('Disallows Ethereum transactions to execute while in maintenance', async ({helper}) => {
-      const owner = await helper.eth.createAccountWithBalance(donor);
-      const receiver = helper.eth.createAccount();
-
-      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'A', 'B', 'C', '');
-
-      // Set maintenance mode
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
-
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
-      const tokenId = await contract.methods.nextTokenId().call();
-      expect(tokenId).to.be.equal('1');
-
-      await expect(contract.methods.mintWithTokenURI(receiver, 'Test URI').send())
-        .to.be.rejectedWith(/Returned error: unknown error/);
-
-      await expect(contract.methods.ownerOf(tokenId).call()).rejectedWith(/token not found/);
-
-      // Disable maintenance mode
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
-    });
-
-    itSub('Allows to enable and disable MM repeatedly', async ({helper}) => {
-      // Set maintenance mode
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is OFF when it should be ON').to.be.true;
-
-      // Disable maintenance mode
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-      expect(await maintenanceEnabled(helper.getApi()), 'MM is ON when it should be OFF').to.be.false;
-    });
-
-    afterEach(async () => {
-      await usingPlaygrounds(async helper => {
-        if(helper.fetchMissingPalletNames([Pallets.Maintenance]).length != 0) return;
-        if(await maintenanceEnabled(helper.getApi())) {
-          console.warn('\tMaintenance mode was left enabled AFTER a test has finished! Be careful. Disabling it now.');
-          await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-        }
-        expect(await maintenanceEnabled(helper.getApi()), 'Disastrous! Exited the test suite with maintenance mode on.').to.be.false;
-      });
-    });
-  });
-
-  describe('Integration Test: Maintenance mode & App Promo', () => {
-    let superuser: IKeyringPair;
-
-    before(async function() {
-      await usingPlaygrounds(async (helper, privateKey) => {
-        requirePalletsOrSkip(this, helper, [Pallets.Maintenance]);
-        superuser = await privateKey('//Alice');
-      });
-    });
-
-    describe('Test AppPromo script for check state after Maintenance mode', () => {
-      before(async function () {
-        await usingPlaygrounds(async (helper) => {
-          if(await maintenanceEnabled(helper.getApi())) {
-            console.warn('\tMaintenance mode was left enabled BEFORE the test suite! Disabling it now.');
-            await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled;
-          }
-        });
-      });
-      itSub('Can find and fix inconsistent state', async ({helper}) => {
-        const api = helper.getApi();
-
-        await helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [api.tx.system.setStorage([
-          // pendingUnstake(1 -> [superuser.address, 100UNQ])
-          ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f5153cb1f00942ff401000000',
-            '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'],
-          // pendingUnstake(2 -> [superuser.address, 100UNQ])
-          ['0x42b67acb8bd223c60d0c8f621ffefc0ae280fa2db99bd3827aac976de75af95f9eb2dcce60f37a2702000000',
-            '0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d000010632d5ec76b0500000000000000'],
-          // Balances.freezes(superuser.address -> freeze with app promo id and 200 UNQ )
-          ['0xc2261276cc9d1f8598ea4b6a74b15c2fb1c0eb12e038e5c7f91e120ed4b7ebf1de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d',
-            '0x046170707374616b656170707374616b65000020c65abc8ed70a00000000000000'],
-        ])]);
-
-        expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]);
-        expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([[superuser.address, '0x00000000000000056bc75e2d63100000']]);
-        expect((await api.query.balances.freezes(superuser.address) as Vec<PalletBalancesIdAmount>)
-          .map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()})))
-          .to.be.deep.equal([{id: 'appstakeappstake', amount: 200000000000000000000n}]);
-        await correctState();
-
-        expect((await api.query.appPromotion.pendingUnstake(1)).toJSON()).to.be.deep.equal([]);
-        expect((await api.query.appPromotion.pendingUnstake(2)).toJSON()).to.be.deep.equal([]);
-        expect((await api.query.balances.freezes(superuser.address)).toJSON()).to.be.deep.equal([]);
-
-      });
-
-      itSub('(!negative test!) Only works when Maintenance mode is disabled', async({helper}) => {
-        await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.enable', [])).to.be.fulfilled;
-        await expect(correctState()).to.be.rejectedWith('The network is still in maintenance mode');
-        await expect(helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', [])).to.be.fulfilled;
-      });
-    });
-  });
-
-  after(async () => {
-    await usingPlaygrounds(async(helper) => {
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.maintenance.disable', []);
-    });
-  });
-});
deletedjs-packages/tests/src/migrations/942057-appPromotion/README.mddiffbeforeafterboth
--- a/js-packages/tests/src/migrations/942057-appPromotion/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Update Procedure
-
-- Enable maintenance mode
-- [Collect migration data using ChainQL](#stakers-data-loading)
-- ❗️❗️❗️ Initiate the runtime upgrade only at this point ❗️❗️❗️
-- Wait for the upgrade to complete
-- [Execute offchain migration](#execute-offchain-migration)
-- Disable maintenance mode
-
-## Stakers Data Loading
-
-Set the environment variable (WS_RPC). For example, ws://localhost:9944. Execute the following command:
-
-```sh
-chainql --tla-str=chainUrl=<WS_RPC> stakersParser.jsonnet > output.json
-```
-
-where `<WS_RPC>` - is the network address.
-
-Example for Opal:
-
-```sh
-chainql --tla-str=chainUrl=wss://eu-ws-opal.unique.network:443 stakersParser.jsonnet > output.json
-```
-
-To install chainql, execute the following command:
-
-```sh
-cargo install chainql
-```
-
-## Execute offchain migration
-
-To run, you need to set an environment variables:
-- `SUPERUSER_SEED` – the sudo key seed.
-- `WS_RPC` – the network address
-
-Run the migration by executing the following command:
-
-```sh
-npx ts-node --esm executeMigration.ts
-```
deletedjs-packages/tests/src/migrations/942057-appPromotion/collectData.tsdiffbeforeafterboth
--- a/js-packages/tests/src/migrations/942057-appPromotion/collectData.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import {exec} from 'child_process';
-import path from 'path';
-import {dirname} from 'path';
-import {fileURLToPath} from 'url';
-
-export const collectData = () => {
-  const dirName = dirname(fileURLToPath(import.meta.url));
-
-  const pathToScript = path.resolve(dirName, './stakersParser.jsonnet');
-  const outputPath = path.resolve(dirName, './output.json');
-  exec(`chainql --tla-str=chainUrl=ws://127.0.0.1:9944 ${pathToScript} > ${outputPath}`);
-};
deletedjs-packages/tests/src/migrations/942057-appPromotion/executeMigration.tsdiffbeforeafterboth
--- a/js-packages/tests/src/migrations/942057-appPromotion/executeMigration.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import {migrateLockedToFreeze} from './lockedToFreeze.js';
-
-
-const WS_RPC = process.env.WS_RPC || 'wss://ws-opal.unique.network:443';
-const SUPERUSER_SEED = process.env.SUPERUSER_SEED || '';
-
-migrateLockedToFreeze({
-  wsEndpoint: WS_RPC,
-  donorSeed: SUPERUSER_SEED,
-})
-  .catch(console.error);
\ No newline at end of file
deletedjs-packages/tests/src/migrations/942057-appPromotion/index.tsdiffbeforeafterboth
--- a/js-packages/tests/src/migrations/942057-appPromotion/index.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import type {Migration} from '../../util/frankensteinMigrate.js';
-import {collectData} from './collectData.js';
-import {migrateLockedToFreeze} from './lockedToFreeze.js';
-
-export const migration: Migration = {
-  async before() {
-    await collectData();
-  },
-  async after() {
-    await migrateLockedToFreeze();
-  },
-};
\ No newline at end of file
deletedjs-packages/tests/src/migrations/942057-appPromotion/lockedToFreeze.tsdiffbeforeafterboth
--- a/js-packages/tests/src/migrations/942057-appPromotion/lockedToFreeze.ts
+++ /dev/null
@@ -1,259 +0,0 @@
-// import { usingApi, privateKey, onlySign } from "./../../load/lib";
-import * as fs from 'fs';
-import {usingPlaygrounds} from '../../util/index.js';
-import path, {dirname} from 'path';
-import {isInteger, parse} from 'lossless-json';
-import {fileURLToPath} from 'url';
-import config from '../../config.js';
-
-
-const WS_ENDPOINT = config.substrateUrl;
-const DONOR_SEED = '//Alice';
-const UPDATE_IF_VERSION = 942057;
-
-export function customNumberParser(value: any) {
-  return isInteger(value) ? BigInt(value) : parseFloat(value);
-}
-
-export const migrateLockedToFreeze = async(options: { wsEndpoint: string; donorSeed: string } = {
-  wsEndpoint: WS_ENDPOINT,
-  donorSeed: DONOR_SEED,
-}) => {
-  await usingPlaygrounds(async (helper, privateKey) => {
-    const api = helper.getApi();
-    // 1. Check version equal 942057 or skip
-    console.log((api.consts.system.version as any).specVersion.toNumber());
-    if((api.consts.system.version as any).specVersion.toNumber() != UPDATE_IF_VERSION) {
-      console.log("Version isn't 942057.");
-      return;
-    }
-
-    // 2. Get sudo signer
-    const signer = await privateKey(options.donorSeed);
-    console.log('2. Getting sudo:', signer.address);
-
-    // 3. Parse data to migrate
-    console.log('3. Parsing chainql results...');
-    const dirName = dirname(fileURLToPath(import.meta.url));
-    const parsingResult = parse(fs.readFileSync(path.resolve(dirName, 'output.json'), 'utf-8'), undefined, customNumberParser);
-
-    const chainqlImportData = parsingResult as {
-      address: string;
-      balance: string;
-      account: {
-        fee_frozen: string,
-        free: string,
-        misc_frozen: string,
-        reserved: string,
-      },
-      locks: {
-        amount: string,
-        id: string,
-      }[],
-      stakes: object,
-      unstakes: object,
-    }[];
-    testChainqlData(chainqlImportData);
-
-    const stakers = chainqlImportData.map((i) => i.address);
-
-    // 3.1 Split into chunks by 100
-    console.log('3.1 Splitting into chunks...');
-    const stakersChunks = chunk(stakers, 100);
-    console.log('3.1 Done, total chunks:', stakersChunks.length);
-
-    // 4. Get signer/sudo nonce
-    console.log('4. Getting sudo nonce...');
-    const signerAccount = (
-        await api.query.system.account(signer.address)
-      ).toJSON() as any;
-
-    let nonce: number = signerAccount.nonce;
-    console.log('4. Sudo nonce is:', nonce);
-
-    // 5. Only sign upgradeAccounts-transactions for each chunk
-    console.log('5. Signing transactions...');
-    const signedTxs = [];
-    for(const chunk of stakersChunks) {
-      const tx = api.tx.sudo.sudo(api.tx.appPromotion.upgradeAccounts(chunk));
-      const signed = tx.sign(signer, {
-        blockHash: api.genesisHash,
-        genesisHash: api.genesisHash,
-        runtimeVersion: api.runtimeVersion,
-        nonce: nonce++,
-      });
-      signedTxs.push(signed);
-    }
-
-    // 6. Send all signed transactions
-    console.log('6. Sending transactions...');
-    const promises = signedTxs.map((tx) => api.rpc.author.submitAndWatchExtrinsic(tx));
-    // 6.1 Wait all transactions settled
-    console.log('6.1 Waiting all transactions settled...');
-    const res = await Promise.allSettled(promises);
-
-    console.log('Wait 5 blocks for transactions to be included in a block...');
-    await helper.wait.newBlocks(5);
-    // 6.2 Filter failed transactions
-    console.log('6.2 Getting failed transactions...');
-    const failedTx = res.filter((r) => r.status == 'rejected') as PromiseRejectedResult[];
-    console.log('6.2. total failedTxs:', failedTx.length);
-
-    // 6.3 Log the reasons of failed tx
-    for(const tx of failedTx) {
-      console.log(tx.reason);
-    }
-
-    // 7. Check balances for 10 blocks:
-    console.log('7. Check balances...');
-    let blocksLeft = 10;
-    let notMigrated = stakers;
-    const suspiciousAccounts = [];
-    do {
-      console.log('blocks left:', blocksLeft);
-      const _notMigrated: string[] = [];
-      console.log('accounts to migrate...', notMigrated.length);
-      for(const accountToMigrate of notMigrated) {
-        let accountMigrated = true;
-        // 7.0 get data from chainql:
-        const oldAccount = chainqlImportData.find(acc => acc.address === accountToMigrate);
-        if(!oldAccount) {
-          console.log('Cannot find old account data for', accountMigrated);
-          accountMigrated = false;
-          _notMigrated.push(accountToMigrate);
-          continue;
-        }
-
-        // 7.1 system.account
-        const balance = await api.query.system.account(accountToMigrate) as any;
-        // new balances
-        const free = balance.data.free;
-        const reserved = balance.data.reserved;
-        const frozen = balance.data.frozen;
-        // old balances
-        const oldFree = oldAccount.account.free;
-        const oldReserved = oldAccount.account.reserved;
-        const oldFrozen = oldAccount.account.fee_frozen;
-        // asserts new = old
-        if(oldFree.toString() !== free.toString()) {
-          console.log('Old free !== New free, which is probably not a problem', oldFree.toString(), free.toString());
-          suspiciousAccounts.push(accountToMigrate);
-        }
-        if(oldFrozen.toString() !== frozen.toString()) {
-          console.log('Old frozen !== New frozen, which is probably not a problem', oldFrozen.toString(), frozen.toString());
-          suspiciousAccounts.push(accountToMigrate);
-        }
-        if(oldReserved.toString() !== reserved.toString()) {
-          console.log('Old reserved !== New reserved, which is probably not a problem', oldReserved.toString(), reserved.toString());
-          suspiciousAccounts.push(accountToMigrate);
-        }
-
-        // 7.2 balances.locks: no id appstake
-        const locks = await helper.balance.getLocked(accountToMigrate);
-        const appPromoLocks = locks.filter(lock => lock.id === 'appstake');
-        if(appPromoLocks.length !== 0) {
-          console.log('Account still has app-promo lock');
-          accountMigrated = false;
-        }
-
-        // 7.3 balances.freezes set...
-        let freezes = await api.query.balances.freezes(accountToMigrate) as any;
-        freezes = freezes.map((freez: any) => ({id: freez.id.toString(), amount: freez.amount.toString()}));
-        if(!freezes) {
-          console.log('Account does not have freezes');
-          accountMigrated = false;
-        } else {
-          const oldAppPromoLocks = oldAccount.locks.filter(l => l.id === '0x6170707374616b65'); // get app promo locks
-          // should be only one freez for each account
-          if(freezes.length !== 1) {
-            console.log('freezes.length !== 1 and old appPromoLocks.length', freezes.length, oldAppPromoLocks.length);
-            accountMigrated = false;
-          } else {
-            const appPromoFreez = freezes[0];
-            // freez-amount should be equal to migrated lock amount
-            if(appPromoFreez.amount.toString() !== oldAppPromoLocks[0].amount.toString()) {
-              console.log('freezes amount !== old appPromoLocks amount', appPromoFreez.amount.toString(), oldAppPromoLocks[0].amount.toString());
-              accountMigrated = false;
-            }
-            // freez id should be correct
-            if(appPromoFreez.id !== '0x6170707374616b656170707374616b65') {
-              console.log('Got freez with incorrect id:', appPromoFreez.id);
-              accountMigrated = false;
-            }
-          }
-        }
-
-        // 7.4 Stakes number the same
-        const stakesNumber = await helper.staking.getStakesNumber({Substrate: accountToMigrate});
-        const oldStakesNumber = oldAccount.stakes ? Object.keys(oldAccount.stakes).length : 0;
-        if(stakesNumber.toString() !== oldStakesNumber.toString()) {
-          console.log('Old stakes number !== New stakes number', oldStakesNumber, stakesNumber);
-          accountMigrated = false;
-        }
-
-        // 7.5 Total pendingUnstake + total staked = old locked
-        const pendingUnstakes = await helper.staking.getPendingUnstake({Substrate: accountToMigrate});
-        const totalStaked = await helper.staking.getTotalStaked({Substrate: accountToMigrate});
-        const totalBalanceInAppPromo = pendingUnstakes + totalStaked;
-        if(totalBalanceInAppPromo.toString() !== oldAccount.balance.toString()) {
-          console.log('totalBalanceInAppPromo !== old locked in app promo', totalBalanceInAppPromo.toString(), oldAccount.balance.toString());
-          accountMigrated = false;
-        }
-
-        // 8 Add to not-migrated
-        if(!accountMigrated) {
-          console.log('Add to not migrated:', accountToMigrate);
-          _notMigrated.push(accountToMigrate);
-        }
-      }
-
-      blocksLeft--;
-      notMigrated = _notMigrated;
-      await helper.wait.newBlocks(1);
-    } while(blocksLeft > 0 && notMigrated.length !== 0);
-
-    console.log('Not migrated accounts...', notMigrated.length);
-    if(suspiciousAccounts.length > 0) {
-      console.log('Saving suspicious accounts to suspicious.json:');
-      fs.writeFileSync('./suspicious.json', JSON.stringify(suspiciousAccounts));
-    }
-    if(notMigrated.length > 0) {
-      console.log('Saving not migrated list to failed.json:');
-      notMigrated.forEach(console.log);
-      fs.writeFileSync('./failed.json', JSON.stringify(notMigrated));
-      process.exit(1);
-    } else {
-      console.log('Migration success');
-    }
-  }, options.wsEndpoint);
-};
-
-const chunk = <T>(arr: T[], size: number) =>
-  Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) =>
-    arr.slice(i * size, i * size + size));
-
-const testChainqlData = (data: any) => {
-  const wrongData = [];
-  for(const account of data) {
-    try {
-      if(account.address == null) throw Error('no address in data');
-      if(account.balance == null) throw Error('no balance in data');
-      if(account.account == null) throw Error('no account in data');
-      if(account.account.fee_frozen == null) throw Error('no account.fee_frozen in data');
-      if(account.account.misc_frozen == null) throw Error('no account.misc_frozen in data');
-      if(account.account.free == null) throw Error('no account.free in data');
-      if(account.account.reserved == null) throw Error('no account.reserved in data');
-      if(account.locks == null) throw Error('no locks in data');
-      if(account.locks[0].amount == null) throw Error('no locks.amount in data');
-      if(account.locks[0].id == null) throw Error('no locks.id in data');
-    } catch (error) {
-      wrongData.push(account.address);
-      console.log((error as Error).message, account.address);
-    }
-    if(wrongData.length > 0) {
-      console.log(data);
-      throw Error('Chainql data not correct');
-    }
-  }
-  console.log('Chainql data correct');
-};
deletedjs-packages/tests/src/migrations/942057-appPromotion/stakersParser.jsonnetdiffbeforeafterboth
--- a/js-packages/tests/src/migrations/942057-appPromotion/stakersParser.jsonnet
+++ /dev/null
@@ -1,29 +0,0 @@
-function(chainUrl)
-
-  local
-    state = cql.chain(chainUrl).latest,
-    locked_balances = state.Balances.Locks._preloadKeys,
-    accountsRaw = state.System.Account._preloadKeys,
-    stakers = state.AppPromotion.Staked._preloadKeys,
-    unstakes = state.AppPromotion.PendingUnstake._preloadKeys,
-    locks = [
-      { [k]: std.filter(function(l) l.id == '0x6170707374616b65', locked_balances[k]) }
-      for k in std.objectFields(locked_balances)
-    ],
-    unstakersData = [
-      { [pair[0]]: { block: block, value: pair[1] } }
-
-      for block in std.objectFields(unstakes)
-      for pair in unstakes[block]
-    ],
-    non_empty_locks = std.prune(locks)
-  ;
-
-  std.map(function(a) {
-    address: std.objectFields(a)[0],
-    balance: a[std.objectFields(a)[0]][0].amount,
-    account: accountsRaw[std.objectFields(a)[0]].data,
-    locks: locked_balances[std.objectFields(a)[0]],
-    stakes: if std.objectHas(stakers, std.objectFields(a)[0]) then stakers[std.objectFields(a)[0]] else null,
-    unstakes: std.filterMap(function(b) std.objectHas(b, std.objectFields(a)[0]), function(c) std.objectValues(c)[0], unstakersData),
-  }, non_empty_locks)
deletedjs-packages/tests/src/migrations/correctStateAfterMaintenance.tsdiffbeforeafterboth
--- a/js-packages/tests/src/migrations/correctStateAfterMaintenance.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import config from '../config.js';
-import {usingPlaygrounds} from '../util/index.js';
-import type {u32} from '@polkadot/types-codec';
-
-const WS_ENDPOINT = config.substrateUrl;
-const DONOR_SEED = '//Alice';
-
-export const main = async(options: { wsEndpoint: string; donorSeed: string } = {
-  wsEndpoint: WS_ENDPOINT,
-  donorSeed: DONOR_SEED,
-}) => {
-  await usingPlaygrounds(async (helper, privateKey) => {
-    const api = helper.getApi();
-
-    if((await api.query.maintenance.enabled()).valueOf()) {
-      throw Error('The network is still in maintenance mode');
-    }
-
-    const pendingBlocks = (
-      await api.query.appPromotion.pendingUnstake.entries()
-    ).map(([k, _v]) =>
-      (k.args[0] as u32).toNumber());
-
-    const currentBlock = (await api.query.system.number() as u32).toNumber();
-
-    const filteredBlocks = pendingBlocks.filter(b => currentBlock > b);
-
-    if(filteredBlocks.length != 0) {
-      console.log(`During maintenance mode, ${filteredBlocks.length} block(s) were not processed. Number(s): ${filteredBlocks}`);
-    } else {
-      console.log('Nothing to change');
-      return;
-    }
-
-    const skippedBlocks = chunk(filteredBlocks, 10);
-
-    const signer = await privateKey(options.donorSeed);
-
-    const txs = skippedBlocks.map((b) =>
-      api.tx.sudo.sudo(api.tx.appPromotion.forceUnstake(b)));
-
-
-    const promises = txs.map((tx) => () => helper.signTransaction(signer, tx));
-
-    await Promise.allSettled(promises.map((p) => p()));
-
-    const failedBlocks: number[] = [];
-    let isSuccess = true;
-
-    for(const b of filteredBlocks) {
-      if(((await api.query.appPromotion.pendingUnstake(b)).toJSON() as any[]).length != 0) {
-        failedBlocks.push(b);
-        isSuccess = false;
-      }
-    }
-
-    if(isSuccess) {
-      console.log('Done. %d block(s) were processed.', filteredBlocks.length);
-    } else {
-      throw new Error(`Something went wrong. Block(s) have not been processed: ${failedBlocks}`);
-    }
-
-
-  }, options.wsEndpoint);
-};
-
-const chunk = <T>(arr: T[], size: number) =>
-  Array.from({length: Math.ceil(arr.length / size)}, (_: any, i: number) =>
-    arr.slice(i * size, i * size + size));
deletedjs-packages/tests/src/migrations/runCheckState.tsdiffbeforeafterboth
--- a/js-packages/tests/src/migrations/runCheckState.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import {main} from './correctStateAfterMaintenance.js';
-
-main({
-  wsEndpoint: process.env.WS_RPC!,
-  donorSeed: process.env.SUPERUSER_SEED!,
-}).then(() => process.exit(0))
-  .catch((e) => {
-    console.error(e);
-    process.exit(1);
-  });
deletedjs-packages/tests/src/nativeFungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nativeFungible.test.ts
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2019-2023 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from './util/index.js';
-
-describe('Native fungible', () => {
-  let root: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      root = await privateKey('//Alice');
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
-    });
-  });
-
-  itSub('destroy_collection()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.burn(alice)).to.be.rejectedWith('common.UnsupportedOperation');
-    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.destroyCollection', [0])).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('add_to_allow_list()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.addToAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('remove_from_allow_list()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.removeFromAllowList(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('change_collection_owner()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.changeOwner(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('add_collection_admin()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('remove_collection_admin()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.removeAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('set_collection_sponsor()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.setSponsor(alice, bob.address)).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('confirm_sponsorship()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.confirmSponsorship(alice)).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('remove_collection_sponsor()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.removeSponsor(alice)).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('create_item()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.mint(alice, 100n, {Substrate: bob.address})).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('set_collection_properties()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.setProperties(alice, [{key: 'value'}])).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('delete_collection_properties()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.deleteProperties(alice, ['key'])).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('set_token_properties()', async ({helper}) => {
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.setTokenProperties',
-      [0, 0, [{key: 'value'}]],
-      true,
-    )).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('delete_token_properties()', async ({helper}) => {
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.deleteTokenProperties',
-      [0, 0, ['key']],
-      true,
-    )).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('set_transfers_enabled_flag()', async ({helper}) => {
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.setTransfersEnabledFlag',
-      [0, true],
-      true,
-    )).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('burn_item()', async ({helper}) => {
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.burnItem',
-      [0, 0, 100n],
-      true,
-    )).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('burn_from()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.burnTokens(alice, 100n)).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('transfer()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    const balanceAliceBefore = await helper.balance.getSubstrate(alice.address);
-    const balanceBobBefore = await helper.balance.getSubstrate(bob.address);
-    await collection.transfer(alice, {Substrate: bob.address}, 100n);
-    const balanceAliceAfter = await helper.balance.getSubstrate(alice.address);
-    const balanceBobAfter = await helper.balance.getSubstrate(bob.address);
-    expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true;
-    expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true;
-  });
-
-  itSub('transfer_from()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    const balanceAliceBefore = await helper.balance.getSubstrate(alice.address);
-    const balanceBobBefore = await helper.balance.getSubstrate(bob.address);
-
-    await collection.transferFrom(alice, {Substrate: alice.address}, {Substrate: bob.address}, 100n);
-
-    const balanceAliceAfter = await helper.balance.getSubstrate(alice.address);
-    const balanceBobAfter = await helper.balance.getSubstrate(bob.address);
-    expect(balanceAliceBefore - balanceAliceAfter > 100n).to.be.true;
-    expect(balanceBobAfter - balanceBobBefore === 100n).to.be.true;
-
-    await expect(collection.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address}, 100n)).to.be.rejectedWith('common.ApprovedValueTooLow');
-  });
-
-  itSub('approve()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.approveTokens(alice, {Substrate: bob.address}, 100n)).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('approve_from()', async ({helper}) => {
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.approveFrom',
-      [{Substrate: alice.address}, {Substrate: bob.address}, 0, 0, 100n],
-      true,
-    )).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('set_collection_limits()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.setLimits(alice, {accountTokenOwnershipLimit: 1})).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('set_collection_permissions()', async ({helper}) => {
-    const collection = helper.ft.getCollectionObject(0);
-    await expect(collection.setPermissions(alice, {access: 'AllowList'})).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('repartition()', async ({helper}) => {
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.repartition',
-      [0, 0, 100n],
-      true,
-    )).to.be.rejectedWith('unique.RepartitionCalledOnNonRefungibleCollection');
-  });
-
-  itSub('force_repair_collection()', async ({helper}) => {
-    await expect(helper.executeExtrinsic(
-      alice,
-      'api.tx.unique.forceRepairCollection',
-      [0],
-      true,
-    )).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('force_repair_item()', async ({helper}) => {
-    await expect(helper.getSudo().executeExtrinsic(
-      root,
-      'api.tx.unique.forceRepairItem',
-      [0, 0],
-      true,
-    )).to.be.rejectedWith('common.UnsupportedOperation');
-  });
-
-  itSub('Nest into NFT token()', async ({helper}) => {
-    const nftCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await nftCollection.mintToken(alice);
-
-    const collection = helper.ft.getCollectionObject(0);
-    await collection.transfer(alice, targetToken.nestingAccount(), 100n);
-
-    await collection.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 100n);
-  });
-});
\ No newline at end of file
deletedjs-packages/tests/src/nesting/collectionProperties.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nesting/collectionProperties.seqtest.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js';
-
-describe('Integration Test: Collection Properties with sudo', () => {
-  let superuser: IKeyringPair;
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      superuser = await privateKey('//Alice');
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'ft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
-    before(async function() {
-      // eslint-disable-next-line require-await
-      await usingPlaygrounds(async helper => {
-        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
-      });
-    });
-
-    itSub('Repairing an unbroken collection\'s properties preserves the consumed space', async({helper}) => {
-      const properties = [
-        {key: 'sea-creatures', value: 'mermaids'},
-        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
-      ];
-      const collection = await helper[testSuite.mode].mintCollection(alice, {properties});
-
-      const newProperty = {key: 'space', value: ' '.repeat(4096)};
-      await collection.setProperties(alice, [newProperty]);
-      const originalSpace = await collection.getPropertiesConsumedSpace();
-      expect(originalSpace).to.be.equal(sizeOfProperty(properties[0]) + sizeOfProperty(properties[1]) + sizeOfProperty(newProperty));
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true);
-      const recomputedSpace = await collection.getPropertiesConsumedSpace();
-      expect(recomputedSpace).to.be.equal(originalSpace);
-    });
-  }));
-});
deletedjs-packages/tests/src/nesting/collectionProperties.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nesting/collectionProperties.test.ts
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js';
-
-describe('Integration Test: Collection Properties', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([200n, 10n], donor);
-    });
-  });
-
-  itSub('Properties are initially empty', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    expect(await collection.getProperties()).to.be.empty;
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'ft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
-    before(async function() {
-      // eslint-disable-next-line require-await
-      await usingPlaygrounds(async helper => {
-        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
-      });
-    });
-
-    itSub('Sets properties for a collection', async ({helper}) =>  {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      // As owner
-      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}])).to.be.fulfilled;
-
-      await collection.addAdmin(alice, {Substrate: bob.address});
-
-      // As administrator
-      await expect(collection.setProperties(bob, [{key: 'black_hole'}])).to.be.fulfilled;
-
-      const properties = await collection.getProperties();
-      expect(properties).to.include.deep.members([
-        {key: 'electron', value: 'come bond'},
-        {key: 'black_hole', value: ''},
-      ]);
-    });
-
-    itSub('Check valid names for collection properties keys', async ({helper}) =>  {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      // alpha symbols
-      await expect(collection.setProperties(alice, [{key: 'answer'}])).to.be.fulfilled;
-
-      // numeric symbols
-      await expect(collection.setProperties(alice, [{key: '451'}])).to.be.fulfilled;
-
-      // underscore symbol
-      await expect(collection.setProperties(alice, [{key: 'black_hole'}])).to.be.fulfilled;
-
-      // dash symbol
-      await expect(collection.setProperties(alice, [{key: '-'}])).to.be.fulfilled;
-
-      // dot symbol
-      await expect(collection.setProperties(alice, [{key: 'once.in.a.long.long.while...', value: 'you get a little lost'}])).to.be.fulfilled;
-
-      const properties = await collection.getProperties();
-      expect(properties).to.include.deep.members([
-        {key: 'answer', value: ''},
-        {key: '451', value: ''},
-        {key: 'black_hole', value: ''},
-        {key: '-', value: ''},
-        {key: 'once.in.a.long.long.while...', value: 'you get a little lost'},
-      ]);
-    });
-
-    itSub('Changes properties of a collection', async ({helper}) =>  {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: ''}])).to.be.fulfilled;
-
-      // Mutate the properties
-      await expect(collection.setProperties(alice, [{key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;
-
-      const properties = await collection.getProperties();
-      expect(properties).to.include.deep.members([
-        {key: 'electron', value: 'come bond'},
-        {key: 'black_hole', value: 'LIGO'},
-      ]);
-    });
-
-    itSub('Deletes properties of a collection', async ({helper}) =>  {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      await expect(collection.setProperties(alice, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}])).to.be.fulfilled;
-
-      await expect(collection.deleteProperties(alice, ['electron'])).to.be.fulfilled;
-
-      const properties = await collection.getProperties(['black_hole', 'electron']);
-      expect(properties).to.be.deep.equal([
-        {key: 'black_hole', value: 'LIGO'},
-      ]);
-    });
-
-    itSub('Allows modifying a collection property multiple times with the same size', async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      const maxCollectionPropertiesSize = 40960;
-
-      const propDataSize = 4096;
-
-      let propDataChar = 'a';
-      const makeNewPropData = () => {
-        propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1);
-        return `${propDataChar}`.repeat(propDataSize);
-      };
-
-      const property = {key: propKey, value: makeNewPropData()};
-      await collection.setProperties(alice, [property]);
-      const originalSpace = await collection.getPropertiesConsumedSpace();
-      expect(originalSpace).to.be.equal(sizeOfProperty(property));
-
-      const sameSizePropertiesPossibleNum = maxCollectionPropertiesSize / propDataSize;
-
-      // It is possible to modify a property as many times as needed.
-      // It will not consume any additional space.
-      for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) {
-        await collection.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);
-        const consumedSpace = await collection.getPropertiesConsumedSpace();
-        expect(consumedSpace).to.be.equal(originalSpace);
-      }
-    });
-
-    itSub('Adding then removing a collection property doesn\'t change the consumed space', async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-      const originalSpace = await collection.getPropertiesConsumedSpace();
-
-      const propDataSize = 4096;
-      const propData = 'a'.repeat(propDataSize);
-
-      const property = {key: propKey, value: propData};
-      await collection.setProperties(alice, [property]);
-      let consumedSpace = await collection.getPropertiesConsumedSpace();
-      expect(consumedSpace).to.be.equal(sizeOfProperty(property));
-
-      await collection.deleteProperties(alice, [propKey]);
-      consumedSpace = await collection.getPropertiesConsumedSpace();
-      expect(consumedSpace).to.be.equal(originalSpace);
-    });
-
-    itSub('Modifying a collection property with different sizes correctly changes the consumed space', async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-      const originalSpace = await collection.getPropertiesConsumedSpace();
-
-      const initProp = {key: propKey, value: 'a'.repeat(4096)};
-      const biggerProp = {key: propKey, value: 'b'.repeat(5000)};
-      const smallerProp = {key: propKey, value: 'c'.repeat(4000)};
-
-      let consumedSpace;
-      let expectedConsumedSpaceDiff;
-
-      await collection.setProperties(alice, [initProp]);
-      consumedSpace = await collection.getPropertiesConsumedSpace();
-      expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace;
-      expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff);
-
-      await collection.setProperties(alice, [biggerProp]);
-      consumedSpace = await collection.getPropertiesConsumedSpace();
-      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp);
-      expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff);
-
-      await collection.setProperties(alice, [smallerProp]);
-      consumedSpace = await collection.getPropertiesConsumedSpace();
-      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp);
-      expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff);
-    });
-  }));
-});
-
-describe('Negative Integration Test: Collection Properties', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([1000n, 100n], donor);
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'ft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
-    before(async function() {
-      // eslint-disable-next-line require-await
-      await usingPlaygrounds(async helper => {
-        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
-      });
-    });
-
-    itSub('Fails to set properties in a collection if not its onwer/administrator', async ({helper}) =>  {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      await expect(collection.setProperties(bob, [{key: 'electron', value: 'come bond'}, {key: 'black_hole', value: 'LIGO'}]))
-        .to.be.rejectedWith(/common\.NoPermission/);
-
-      expect(await collection.getProperties()).to.be.empty;
-    });
-
-    itSub('Fails to set properties that exceed the limits', async ({helper}) =>  {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      const spaceLimit = (helper.getApi().consts.unique.maxCollectionPropertiesSize as any).toNumber();
-
-      // Mute the general tx parsing error, too many bytes to process
-      {
-        console.error = () => {};
-        await expect(collection.setProperties(alice, [
-          {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 9))},
-        ])).to.be.rejected;
-      }
-
-      expect(await collection.getProperties(['electron'])).to.be.empty;
-
-      await expect(collection.setProperties(alice, [
-        {key: 'electron', value: 'low high '.repeat(Math.ceil(spaceLimit! / 18))},
-        {key: 'black_hole', value: '0'.repeat(Math.ceil(spaceLimit! / 2))},
-      ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);
-
-      expect(await collection.getProperties(['electron', 'black_hole'])).to.be.empty;
-    });
-
-    itSub('Fails to set more properties than it is allowed', async ({helper}) =>  {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      const propertiesToBeSet = [];
-      for(let i = 0; i < 65; i++) {
-        propertiesToBeSet.push({
-          key: 'electron_' + i,
-          value: Math.random() > 0.5 ? 'high' : 'low',
-        });
-      }
-
-      await expect(collection.setProperties(alice, propertiesToBeSet)).
-        to.be.rejectedWith(/common\.PropertyLimitReached/);
-
-      expect(await collection.getProperties()).to.be.empty;
-    });
-
-    itSub('Fails to set properties with invalid names', async ({helper}) => {
-      const collection = await helper[testSuite.mode].mintCollection(alice);
-
-      const invalidProperties = [
-        [{key: 'electron', value: 'negative'}, {key: 'string theory', value: 'understandable'}],
-        [{key: 'Mr/Sandman', value: 'Bring me a gene'}],
-        [{key: 'déjà vu', value: 'hmm...'}],
-      ];
-
-      for(let i = 0; i < invalidProperties.length; i++) {
-        await expect(
-          collection.setProperties(alice, invalidProperties[i]),
-          `on rejecting the new badly-named property #${i}`,
-        ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
-      }
-
-      await expect(
-        collection.setProperties(alice, [{key: '', value: 'nothing must not exist'}]),
-        'on rejecting an unnamed property',
-      ).to.be.rejectedWith(/common\.EmptyPropertyKey/);
-
-      await expect(
-        collection.setProperties(alice, [{key: 'CRISPR-Cas9', value: 'rewriting nature!'}]),
-        'on setting the correctly-but-still-badly-named property',
-      ).to.be.fulfilled;
-
-      const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat('CRISPR-Cas9').concat('');
-
-      const properties = await collection.getProperties(keys);
-      expect(properties).to.be.deep.equal([
-        {key: 'CRISPR-Cas9', value: 'rewriting nature!'},
-      ]);
-
-      for(let i = 0; i < invalidProperties.length; i++) {
-        await expect(
-          collection.deleteProperties(alice, invalidProperties[i].map(propertySet => propertySet.key)),
-          `on trying to delete the non-existent badly-named property #${i}`,
-        ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
-      }
-    });
-
-    itSub('Forbids to repair a collection if called with non-sudo', async({helper}) => {
-      const collection = await helper[testSuite.mode].mintCollection(alice, {properties: [
-        {key: 'sea-creatures', value: 'mermaids'},
-        {key: 'goldenratio', value: '1.6180339887498948482045868343656381177203091798057628621354486227052604628189'},
-      ]});
-
-      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairCollection', [collection.collectionId], true))
-        .to.be.rejectedWith(/BadOrigin/);
-    });
-  }));
-});
deletedjs-packages/tests/src/nesting/graphs.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nesting/graphs.test.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../util/index.js';
-import {UniqueHelper, UniqueNFTCollection, UniqueNFToken} from '@unique/playgrounds/src/unique.js';
-
-/**
- * ```dot
- * 4 -> 3 -> 2 -> 1
- * 7 -> 6 -> 5 -> 2
- * 8 -> 5
- * ```
- */
-async function buildComplexObjectGraph(helper: UniqueHelper, sender: IKeyringPair): Promise<[UniqueNFTCollection,UniqueNFToken[]]> {
-  const collection = await helper.nft.mintCollection(sender, {permissions: {nesting: {tokenOwner: true}}});
-  const tokens = await collection.mintMultipleTokens(sender, Array(8).fill({owner: {Substrate: sender.address}}));
-
-  await tokens[7].nest(sender, tokens[4]);
-  await tokens[6].nest(sender, tokens[5]);
-  await tokens[5].nest(sender, tokens[4]);
-  await tokens[4].nest(sender, tokens[1]);
-  await tokens[3].nest(sender, tokens[2]);
-  await tokens[2].nest(sender, tokens[1]);
-  await tokens[1].nest(sender, tokens[0]);
-
-  return [collection, tokens];
-}
-
-describe('Graphs', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([10n], donor);
-    });
-  });
-
-  itSub('Ouroboros can\'t be created in a complex graph', async ({helper}) => {
-    const [collection, tokens] = await buildComplexObjectGraph(helper, alice);
-
-    await collection.setPermissions(alice, {nesting: {collectionAdmin: false, tokenOwner: true}});
-
-    // [token owner] to self
-    await expect(
-      tokens[0].nest(alice, tokens[0]),
-      '[token owner] self-nesting is forbidden',
-    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
-    // [token owner] to nested part of graph
-    await expect(
-      tokens[0].nest(alice, tokens[4]),
-      '[token owner] cannot nest the root node into an internal node',
-    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
-    await expect(
-      tokens[1].transferFrom(alice, tokens[0].nestingAccount(), tokens[7].nestingAccount()),
-      '[token owner] cannot nest higher internal node into lower internal node',
-    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
-
-    await collection.setPermissions(alice, {nesting: {collectionAdmin: true, tokenOwner: false}});
-
-    // [collection owner] to self
-    await expect(
-      tokens[0].nest(alice, tokens[0]),
-      '[collection owner] self-nesting is forbidden',
-    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
-    // [collection owner] to nested part of graph
-    await expect(
-      tokens[0].nest(alice, tokens[4]),
-      '[collection owner] cannot nest the root node into an internal node',
-    ).to.be.rejectedWith(/structure\.OuroborosDetected/);
-  });
-});
deletedjs-packages/tests/src/nesting/propertyPermissions.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nesting/propertyPermissions.test.ts
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect} from '../util/index.js';
-import {UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js';
-
-describe('Integration Test: Access Rights to Token Properties', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
-    });
-  });
-
-  itSub('Reads access rights to properties of a collection', async ({helper}) =>  {
-    const collection = await helper.nft.mintCollection(alice);
-    const propertyRights = (await helper.callRpc('api.query.common.collectionPropertyPermissions', [collection.collectionId])).toJSON();
-    expect(propertyRights).to.be.empty;
-  });
-
-  async function testSetsAccessRightsToProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {
-    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true}}]))
-      .to.be.fulfilled;
-
-    await collection.addAdmin(alice, {Substrate: bob.address});
-
-    await expect(collection.setTokenPropertyPermissions(bob, [{key: 'mindgame', permission: {collectionAdmin: true, tokenOwner: false}}]))
-      .to.be.fulfilled;
-
-    const propertyRights = await collection.getPropertyPermissions(['skullduggery', 'mindgame']);
-    expect(propertyRights).to.include.deep.members([
-      {key: 'skullduggery', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}},
-      {key: 'mindgame', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},
-    ]);
-  }
-
-  itSub('Sets access rights to properties of a collection (NFT)', async ({helper}) =>  {
-    await testSetsAccessRightsToProperties(await helper.nft.mintCollection(alice));
-  });
-
-  itSub.ifWithPallets('Sets access rights to properties of a collection (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    await testSetsAccessRightsToProperties(await helper.rft.mintCollection(alice));
-  });
-
-  async function testChangesAccessRightsToProperty(collection: UniqueNFTCollection | UniqueRFTCollection) {
-    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: true, collectionAdmin: true}}]))
-      .to.be.fulfilled;
-
-    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))
-      .to.be.fulfilled;
-
-    const propertyRights = await collection.getPropertyPermissions();
-    expect(propertyRights).to.be.deep.equal([
-      {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
-    ]);
-  }
-
-  itSub('Changes access rights to properties of a NFT collection', async ({helper}) =>  {
-    await testChangesAccessRightsToProperty(await helper.nft.mintCollection(alice));
-  });
-
-  itSub.ifWithPallets('Changes access rights to properties of a ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
-    await testChangesAccessRightsToProperty(await helper.rft.mintCollection(alice));
-  });
-});
-
-describe('Negative Integration Test: Access Rights to Token Properties', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
-    });
-  });
-
-  async function testPreventsFromSettingAccessRightsNotAdminOrOwner(collection: UniqueNFTCollection | UniqueRFTCollection) {
-    await expect(collection.setTokenPropertyPermissions(bob, [{key: 'skullduggery', permission: {mutable: true, tokenOwner: true}}]))
-      .to.be.rejectedWith(/common\.NoPermission/);
-
-    const propertyRights = await collection.getPropertyPermissions(['skullduggery']);
-    expect(propertyRights).to.be.empty;
-  }
-
-  itSub('Prevents from setting access rights to properties of a NFT collection if not an onwer/admin', async ({helper}) =>  {
-    await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.nft.mintCollection(alice));
-  });
-
-  itSub.ifWithPallets('Prevents from setting access rights to properties of a ReFungible collection if not an onwer/admin', [Pallets.ReFungible], async ({helper}) => {
-    await testPreventsFromSettingAccessRightsNotAdminOrOwner(await helper.rft.mintCollection(alice));
-  });
-
-  async function testPreventFromAddingTooManyPossibleProperties(collection: UniqueNFTCollection | UniqueRFTCollection) {
-    const constitution = [];
-    for(let i = 0; i < 65; i++) {
-      constitution.push({
-        key: 'property_' + i,
-        permission: Math.random() > 0.5 ? {mutable: true, collectionAdmin: true, tokenOwner: true} : {},
-      });
-    }
-
-    await expect(collection.setTokenPropertyPermissions(alice, constitution))
-      .to.be.rejectedWith(/common\.PropertyLimitReached/);
-
-    const propertyRights = await collection.getPropertyPermissions();
-    expect(propertyRights).to.be.empty;
-  }
-
-  itSub('Prevents from adding too many possible properties (NFT)', async ({helper}) =>  {
-    await testPreventFromAddingTooManyPossibleProperties(await helper.nft.mintCollection(alice));
-  });
-
-  itSub.ifWithPallets('Prevents from adding too many possible properties (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    await testPreventFromAddingTooManyPossibleProperties(await helper.rft.mintCollection(alice));
-  });
-
-  async function testPreventAccessRightsModifiedIfConstant(collection: UniqueNFTCollection | UniqueRFTCollection) {
-    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {mutable: false, tokenOwner: true}}]))
-      .to.be.fulfilled;
-
-    await expect(collection.setTokenPropertyPermissions(alice, [{key: 'skullduggery', permission: {collectionAdmin: true}}]))
-      .to.be.rejectedWith(/common\.NoPermission/);
-
-    const propertyRights = await collection.getPropertyPermissions(['skullduggery']);
-    expect(propertyRights).to.deep.equal([
-      {key: 'skullduggery', permission: {'mutable': false, 'collectionAdmin': false, 'tokenOwner': true}},
-    ]);
-  }
-
-  itSub('Prevents access rights to be modified if constant (NFT)', async ({helper}) =>  {
-    await testPreventAccessRightsModifiedIfConstant(await helper.nft.mintCollection(alice));
-  });
-
-  itSub.ifWithPallets('Prevents access rights to be modified if constant (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    await testPreventAccessRightsModifiedIfConstant(await helper.rft.mintCollection(alice));
-  });
-
-  async function testPreventsAddingPropertiesWithInvalidNames(collection: UniqueNFTCollection | UniqueRFTCollection) {
-    const invalidProperties = [
-      [{key: 'skullduggery', permission: {tokenOwner: true}}, {key: 'im possible', permission: {collectionAdmin: true}}],
-      [{key: 'G#4', permission: {tokenOwner: true}}],
-      [{key: 'HÆMILTON', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}],
-    ];
-
-    for(let i = 0; i < invalidProperties.length; i++) {
-      await expect(
-        collection.setTokenPropertyPermissions(alice, invalidProperties[i]),
-        `on setting the new badly-named property #${i}`,
-      ).to.be.rejectedWith(/common\.InvalidCharacterInPropertyKey/);
-    }
-
-    await expect(
-      collection.setTokenPropertyPermissions(alice, [{key: '', permission: {}}]),
-      'on rejecting an unnamed property',
-    ).to.be.rejectedWith(/common\.EmptyPropertyKey/);
-
-    const correctKey = '--0x03116e387820CA05'; // PolkadotJS would parse this as an already encoded hex-string
-    await expect(
-      collection.setTokenPropertyPermissions(alice, [
-        {key: correctKey, permission: {collectionAdmin: true}},
-      ]),
-      'on setting the correctly-but-still-badly-named property',
-    ).to.be.fulfilled;
-
-    const keys = invalidProperties.flatMap(propertySet => propertySet.map(property => property.key)).concat(correctKey).concat('');
-
-    const propertyRights = await collection.getPropertyPermissions(keys);
-    expect(propertyRights).to.be.deep.equal([
-      {key: correctKey, permission: {mutable: false, collectionAdmin: true, tokenOwner: false}},
-    ]);
-  }
-
-  itSub('Prevents adding properties with invalid names (NFT)', async ({helper}) =>  {
-    await testPreventsAddingPropertiesWithInvalidNames(await helper.nft.mintCollection(alice));
-  });
-
-  itSub.ifWithPallets('Prevents adding properties with invalid names (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    await testPreventsAddingPropertiesWithInvalidNames(await helper.rft.mintCollection(alice));
-  });
-});
deletedjs-packages/tests/src/nesting/tokenProperties.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nesting/tokenProperties.seqtest.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect, requirePalletsOrSkip, sizeOfProperty} from '../util/index.js';
-
-describe('Integration Test: Token Properties with sudo', () => {
-  let superuser: IKeyringPair;
-  let alice: IKeyringPair; // collection owner
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      superuser = await privateKey('//Alice');
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100n], donor);
-    });
-  });
-
-  [
-    {mode: 'nft' as const, pieces: undefined, requiredPallets: []} as const,
-    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const,
-  ].map(testSuite => describe(`${testSuite.mode.toUpperCase()}`, () => {
-    before(async function() {
-      // eslint-disable-next-line require-await
-      await usingPlaygrounds(async helper => {
-        requirePalletsOrSkip(this, helper, testSuite.requiredPallets);
-      });
-    });
-
-    itSub('force_repair_item preserves valid consumed space', async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testSuite.mode].mintCollection(alice, {
-        tokenPropertyPermissions: [
-          {
-            key: propKey,
-            permission: {mutable: true, tokenOwner: true},
-          },
-        ],
-      });
-      const token = await (
-        testSuite.pieces
-          ? collection.mintToken(alice, testSuite.pieces as any)
-          : collection.mintToken(alice)
-      );
-
-      const prop = {key: propKey, value: 'a'.repeat(4096)};
-
-      await token.setProperties(alice, [prop]);
-      const originalSpace = await token.getTokenPropertiesConsumedSpace();
-      expect(originalSpace).to.be.equal(sizeOfProperty(prop));
-
-      await helper.getSudo().executeExtrinsic(superuser, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true);
-      const recomputedSpace = await token.getTokenPropertiesConsumedSpace();
-      expect(recomputedSpace).to.be.equal(originalSpace);
-    });
-  }));
-});
deletedjs-packages/tests/src/nesting/tokenProperties.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nesting/tokenProperties.test.ts
+++ /dev/null
@@ -1,816 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect, sizeOfProperty} from '../util/index.js';
-import {UniqueHelper, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/src/unique.js';
-
-describe('Integration Test: Token Properties', () => {
-  let alice: IKeyringPair; // collection owner
-  let bob: IKeyringPair; // collection admin
-  let charlie: IKeyringPair; // token owner
-
-  let permissions: {permission: any, signers: IKeyringPair[]}[];
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 100n, 100n], donor);
-    });
-
-    permissions = [
-      {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob]},
-      {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob]},
-      {permission: {mutable: true, tokenOwner: true}, signers: [charlie]},
-      {permission: {mutable: false, tokenOwner: true}, signers: [charlie]},
-      {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
-      {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie]},
-    ];
-  });
-
-  async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> {
-    const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, {
-      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
-    });
-    return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n];
-  }
-
-  async function testReadsYetEmptyProperties(token: UniqueNFToken | UniqueRFToken) {
-    const properties = await token.getProperties();
-    expect(properties).to.be.empty;
-
-    const tokenData = await token.getData();
-    expect(tokenData!.properties).to.be.empty;
-  }
-
-  itSub('Reads yet empty properties of a token (NFT)', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    const token = await collection.mintToken(alice);
-    await testReadsYetEmptyProperties(token);
-  });
-
-  itSub.ifWithPallets('Reads yet empty properties of a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice);
-    const token = await collection.mintToken(alice);
-    await testReadsYetEmptyProperties(token);
-  });
-
-  async function testAssignPropertiesAccordingToPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
-    await token.collection.addAdmin(alice, {Substrate: bob.address});
-    await token.transfer(alice, {Substrate: charlie.address}, pieces);
-
-    const propertyKeys: string[] = [];
-    let i = 0;
-    for(const permission of permissions) {
-      i++;
-      let j = 0;
-      for(const signer of permission.signers) {
-        j++;
-        const key = i + '_' + signer.address;
-        propertyKeys.push(key);
-
-        await expect(
-          token.setProperties(signer, [{key: key, value: 'Serotonin increase'}]),
-          `on adding property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-      }
-    }
-
-    const properties = await token.getProperties(propertyKeys);
-    const tokenData = await token.getData();
-    for(let i = 0; i < properties.length; i++) {
-      expect(properties[i].value).to.be.equal('Serotonin increase');
-      expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');
-    }
-  }
-
-  itSub('Assigns properties to a token according to permissions (NFT)', async ({helper}) =>  {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
-    await testAssignPropertiesAccordingToPermissions(token, amount);
-  });
-
-  itSub.ifWithPallets('Assigns properties to a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
-    await testAssignPropertiesAccordingToPermissions(token, amount);
-  });
-
-  async function testChangesPropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
-    await token.collection.addAdmin(alice, {Substrate: bob.address});
-    await token.transfer(alice, {Substrate: charlie.address}, pieces);
-
-    const propertyKeys: string[] = [];
-    let i = 0;
-    for(const permission of permissions) {
-      i++;
-      if(!permission.permission.mutable) continue;
-
-      let j = 0;
-      for(const signer of permission.signers) {
-        j++;
-        const key = i + '_' + signer.address;
-        propertyKeys.push(key);
-
-        await expect(
-          token.setProperties(signer, [{key, value: 'Serotonin increase'}]),
-          `on adding property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-
-        await expect(
-          token.setProperties(signer, [{key, value: 'Serotonin stable'}]),
-          `on changing property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-      }
-    }
-
-    const properties = await token.getProperties(propertyKeys);
-    const tokenData = await token.getData();
-    for(let i = 0; i < properties.length; i++) {
-      expect(properties[i].value).to.be.equal('Serotonin stable');
-      expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');
-    }
-  }
-
-  itSub('Changes properties of a token according to permissions (NFT)', async ({helper}) =>  {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
-    await testChangesPropertiesAccordingPermission(token, amount);
-  });
-
-  itSub.ifWithPallets('Changes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
-    await testChangesPropertiesAccordingPermission(token, amount);
-  });
-
-  async function testDeletePropertiesAccordingPermission(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
-    await token.collection.addAdmin(alice, {Substrate: bob.address});
-    await token.transfer(alice, {Substrate: charlie.address}, pieces);
-
-    const propertyKeys: string[] = [];
-    let i = 0;
-
-    for(const permission of permissions) {
-      i++;
-      if(!permission.permission.mutable) continue;
-
-      let j = 0;
-      for(const signer of permission.signers) {
-        j++;
-        const key = i + '_' + signer.address;
-        propertyKeys.push(key);
-
-        await expect(
-          token.setProperties(signer, [{key, value: 'Serotonin increase'}]),
-          `on adding property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-
-        await expect(
-          token.deleteProperties(signer, [key]),
-          `on deleting property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-      }
-    }
-
-    expect(await token.getProperties(propertyKeys)).to.be.empty;
-    expect((await token.getData())!.properties).to.be.empty;
-  }
-
-  itSub('Deletes properties of a token according to permissions (NFT)', async ({helper}) =>  {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
-    await testDeletePropertiesAccordingPermission(token, amount);
-  });
-
-  itSub.ifWithPallets('Deletes properties of a token according to permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
-    await testDeletePropertiesAccordingPermission(token, amount);
-  });
-
-  itSub('Assigns properties to a nested token according to permissions', async ({helper}) =>  {
-    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const collectionB = await helper.nft.mintCollection(alice, {
-      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
-    });
-    const targetToken = await collectionA.mintToken(alice);
-    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
-
-    await collectionB.addAdmin(alice, {Substrate: bob.address});
-    await targetToken.transfer(alice, {Substrate: charlie.address});
-
-    const propertyKeys: string[] = [];
-    let i = 0;
-    for(const permission of permissions) {
-      i++;
-      let j = 0;
-      for(const signer of permission.signers) {
-        j++;
-        const key = i + '_' + signer.address;
-        propertyKeys.push(key);
-
-        await expect(
-          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
-          `on adding property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-      }
-    }
-
-    const properties = await nestedToken.getProperties(propertyKeys);
-    const tokenData = await nestedToken.getData();
-    for(let i = 0; i < properties.length; i++) {
-      expect(properties[i].value).to.be.equal('Serotonin increase');
-      expect(tokenData!.properties[i].value).to.be.equal('Serotonin increase');
-    }
-    expect(await targetToken.getProperties()).to.be.empty;
-  });
-
-  itSub('Changes properties of a nested token according to permissions', async ({helper}) =>  {
-    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const collectionB = await helper.nft.mintCollection(alice, {
-      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
-    });
-    const targetToken = await collectionA.mintToken(alice);
-    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
-
-    await collectionB.addAdmin(alice, {Substrate: bob.address});
-    await targetToken.transfer(alice, {Substrate: charlie.address});
-
-    const propertyKeys: string[] = [];
-    let i = 0;
-    for(const permission of permissions) {
-      i++;
-      if(!permission.permission.mutable) continue;
-
-      let j = 0;
-      for(const signer of permission.signers) {
-        j++;
-        const key = i + '_' + signer.address;
-        propertyKeys.push(key);
-
-        await expect(
-          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
-          `on adding property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-
-        await expect(
-          nestedToken.setProperties(signer, [{key, value: 'Serotonin stable'}]),
-          `on changing property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-      }
-    }
-
-    const properties = await nestedToken.getProperties(propertyKeys);
-    const tokenData = await nestedToken.getData();
-    for(let i = 0; i < properties.length; i++) {
-      expect(properties[i].value).to.be.equal('Serotonin stable');
-      expect(tokenData!.properties[i].value).to.be.equal('Serotonin stable');
-    }
-    expect(await targetToken.getProperties()).to.be.empty;
-  });
-
-  itSub('Deletes properties of a nested token according to permissions', async ({helper}) =>  {
-    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const collectionB = await helper.nft.mintCollection(alice, {
-      tokenPropertyPermissions: permissions.flatMap(({permission, signers}, i) =>
-        signers.map(signer => ({key: `${i+1}_${signer.address}`, permission}))),
-    });
-    const targetToken = await collectionA.mintToken(alice);
-    const nestedToken = await collectionB.mintToken(alice, targetToken.nestingAccount());
-
-    await collectionB.addAdmin(alice, {Substrate: bob.address});
-    await targetToken.transfer(alice, {Substrate: charlie.address});
-
-    const propertyKeys: string[] = [];
-    let i = 0;
-    for(const permission of permissions) {
-      i++;
-      if(!permission.permission.mutable) continue;
-
-      let j = 0;
-      for(const signer of permission.signers) {
-        j++;
-        const key = i + '_' + signer.address;
-        propertyKeys.push(key);
-
-        await expect(
-          nestedToken.setProperties(signer, [{key, value: 'Serotonin increase'}]),
-          `on adding property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-
-        await expect(
-          nestedToken.deleteProperties(signer, [key]),
-          `on deleting property #${i} by signer #${j}`,
-        ).to.be.fulfilled;
-      }
-    }
-
-    expect(await nestedToken.getProperties(propertyKeys)).to.be.empty;
-    expect((await nestedToken.getData())!.properties).to.be.empty;
-    expect(await targetToken.getProperties()).to.be.empty;
-  });
-
-  [
-    {mode: 'nft' as const, storage: 'nonfungible' as const, pieces: undefined, requiredPallets: []} as const,
-    {mode: 'rft' as const, storage: 'refungible' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]} as const,
-  ].map(testCase =>
-    itSub.ifWithPallets(`Allows modifying a token property multiple times with the same size (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPropertyPermissions: [
-          {
-            key: propKey,
-            permission: {mutable: true, tokenOwner: true},
-          },
-        ],
-      });
-
-      const maxTokenPropertiesSize = 32768;
-
-      const propDataSize = 4096;
-
-      let propDataChar = 'a';
-      const makeNewPropData = () => {
-        propDataChar = String.fromCharCode(propDataChar.charCodeAt(0) + 1);
-        return `${propDataChar}`.repeat(propDataSize);
-      };
-
-      const token = await (
-        testCase.pieces
-          ? collection.mintToken(alice, testCase.pieces as any)
-          : collection.mintToken(alice)
-      );
-
-      const property = {key: propKey, value: makeNewPropData()};
-      await token.setProperties(alice, [property]);
-      const originalSpace = await token.getTokenPropertiesConsumedSpace();
-      expect(originalSpace).to.be.equal(sizeOfProperty(property));
-
-      const sameSizePropertiesPossibleNum = maxTokenPropertiesSize / propDataSize;
-
-      // It is possible to modify a property as many times as needed.
-      // It will not consume any additional space.
-      for(let i = 0; i < sameSizePropertiesPossibleNum + 1; i++) {
-        await token.setProperties(alice, [{key: propKey, value: makeNewPropData()}]);
-        const consumedSpace = await token.getTokenPropertiesConsumedSpace();
-        expect(consumedSpace).to.be.equal(originalSpace);
-      }
-    }));
-
-  [
-    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
-    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itSub.ifWithPallets(`Adding then removing a token property doesn't change the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPropertyPermissions: [
-          {
-            key: propKey,
-            permission: {mutable: true, tokenOwner: true},
-          },
-        ],
-      });
-      const token = await (
-        testCase.pieces
-          ? collection.mintToken(alice, testCase.pieces as any)
-          : collection.mintToken(alice)
-      );
-      const originalSpace = await token.getTokenPropertiesConsumedSpace();
-
-      const propDataSize = 4096;
-      const propData = 'a'.repeat(propDataSize);
-
-      const property = {key: propKey, value: propData};
-      await token.setProperties(alice, [property]);
-      let consumedSpace = await token.getTokenPropertiesConsumedSpace();
-      expect(consumedSpace).to.be.equal(sizeOfProperty(property));
-
-      await token.deleteProperties(alice, [propKey]);
-      consumedSpace = await token.getTokenPropertiesConsumedSpace();
-      expect(consumedSpace).to.be.equal(originalSpace);
-    }));
-
-  [
-    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
-    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itSub.ifWithPallets(`Modifying a token property with different sizes correctly changes the consumed space (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPropertyPermissions: [
-          {
-            key: propKey,
-            permission: {mutable: true, tokenOwner: true},
-          },
-        ],
-      });
-      const token = await (
-        testCase.pieces
-          ? collection.mintToken(alice, testCase.pieces as any)
-          : collection.mintToken(alice)
-      );
-      const originalSpace = await token.getTokenPropertiesConsumedSpace();
-
-      const initProp = {key: propKey, value: 'a'.repeat(4096)};
-      const biggerProp = {key: propKey, value: 'b'.repeat(5000)};
-      const smallerProp = {key: propKey, value: 'c'.repeat(4000)};
-
-      let consumedSpace;
-      let expectedConsumedSpaceDiff;
-
-      await token.setProperties(alice, [initProp]);
-      consumedSpace = await token.getTokenPropertiesConsumedSpace();
-      expectedConsumedSpaceDiff = sizeOfProperty(initProp) - originalSpace;
-      expect(consumedSpace).to.be.equal(originalSpace + expectedConsumedSpaceDiff);
-
-      await token.setProperties(alice, [biggerProp]);
-      consumedSpace = await token.getTokenPropertiesConsumedSpace();
-      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(initProp);
-      expect(consumedSpace).to.be.equal(sizeOfProperty(initProp) + expectedConsumedSpaceDiff);
-
-      await token.setProperties(alice, [smallerProp]);
-      consumedSpace = await token.getTokenPropertiesConsumedSpace();
-      expectedConsumedSpaceDiff = sizeOfProperty(biggerProp) - sizeOfProperty(smallerProp);
-      expect(consumedSpace).to.be.equal(sizeOfProperty(biggerProp) - expectedConsumedSpaceDiff);
-    }));
-
-  itSub('Set sponsored properties', async({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true}}]});
-
-    await collection.setSponsor(alice, alice.address);
-    await collection.confirmSponsorship(alice);
-    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
-    await collection.addToAllowList(alice, {Substrate: bob.address});
-    await collection.setLimits(alice, {sponsoredDataRateLimit: {blocks: 30}});
-
-    const token = await collection.mintToken(alice, {Substrate: bob.address});
-
-    const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
-    const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await token.setProperties(bob, [{key: 'k', value: 'val'}]);
-
-    const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
-    const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobBalanceAfter).to.be.equal(bobBalanceBefore);
-    expect(aliceBalanceBefore > aliceBalanceAfter).to.be.true;
-  });
-});
-
-describe('Negative Integration Test: Token Properties', () => {
-  let alice: IKeyringPair; // collection owner
-  let bob: IKeyringPair; // collection admin
-  let charlie: IKeyringPair; // token owner
-
-  let constitution: {permission: any, signers: IKeyringPair[], sinner: IKeyringPair}[];
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      let dave: IKeyringPair;
-      [alice, bob, charlie, dave] = await helper.arrange.createAccounts([100n, 100n, 100n, 100n], donor);
-
-      // todo:playgrounds probably separate these tests later
-      constitution = [
-        {permission: {mutable: true, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},
-        {permission: {mutable: false, collectionAdmin: true}, signers: [alice, bob], sinner: charlie},
-        {permission: {mutable: true, tokenOwner: true}, signers: [charlie], sinner: alice},
-        {permission: {mutable: false, tokenOwner: true}, signers: [charlie], sinner: alice},
-        {permission: {mutable: true, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},
-        {permission: {mutable: false, collectionAdmin: true, tokenOwner: true}, signers: [alice, bob, charlie], sinner: dave},
-      ];
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: [Pallets.NFT]},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itSub.ifWithPallets(`Forbids adding/deleting properties of a token if token doesn't exist (${testCase.mode.toLocaleUpperCase})`, testCase.requiredPallets, async({helper}) => {
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPropertyPermissions: constitution.slice(0, 1).map(({permission}) => ({key: '1', permission})),
-      });
-      const nonExistentToken = collection.getTokenObject(1);
-
-      await expect(
-        nonExistentToken.setProperties(alice, [{key: '1', value: 'Serotonin increase'}]),
-        'on expecting failure whilst adding a property by alice',
-      ).to.be.rejectedWith(/common\.TokenNotFound/);
-
-      await expect(
-        nonExistentToken.deleteProperties(alice, ['1']),
-        'on expecting failure whilst deleting a property by alice',
-      ).to.be.rejectedWith(/common\.TokenNotFound/);
-    }));
-
-  async function mintCollectionWithAllPermissionsAndToken(helper: UniqueHelper, mode: 'NFT' | 'RFT'): Promise<[UniqueNFToken | UniqueRFToken, bigint]> {
-    const collection = await (mode == 'NFT' ? helper.nft : helper.rft).mintCollection(alice, {
-      tokenPropertyPermissions: constitution.map(({permission}, i) => ({key: `${i+1}`, permission})),
-    });
-    return mode == 'NFT' ? [await collection.mintToken(alice), 1n] : [await collection.mintToken(alice, 100n as any), 100n];
-  }
-
-  async function getConsumedSpace(api: any, collectionId: number, tokenId: number, mode: 'NFT' | 'RFT'): Promise<number> {
-    return (await (mode == 'NFT' ? api.query.nonfungible : api.query.refungible).tokenProperties(collectionId, tokenId)).toJSON().consumedSpace;
-  }
-
-  async function prepare(token: UniqueNFToken | UniqueRFToken, pieces: bigint): Promise<number> {
-    await token.collection.addAdmin(alice, {Substrate: bob.address});
-    await token.transfer(alice, {Substrate: charlie.address}, pieces);
-
-    let i = 0;
-    for(const passage of constitution) {
-      i++;
-      const signer = passage.signers[0];
-      await expect(
-        token.setProperties(signer, [{key: `${i}`, value: 'Serotonin increase'}]),
-        `on adding property ${i} by ${signer.address}`,
-      ).to.be.fulfilled;
-    }
-
-    const originalSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
-    return originalSpace;
-  }
-
-  async function testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
-    const originalSpace = await prepare(token, pieces);
-
-    let i = 0;
-    for(const forbiddance of constitution) {
-      i++;
-      if(!forbiddance.permission.mutable) continue;
-
-      await expect(
-        token.setProperties(forbiddance.sinner, [{key: `${i}`, value: 'Serotonin down'}]),
-        `on failing to change property ${i} by the malefactor`,
-      ).to.be.rejectedWith(/common\.NoPermission/);
-
-      await expect(
-        token.deleteProperties(forbiddance.sinner, [`${i}`]),
-        `on failing to delete property ${i} by the malefactor`,
-      ).to.be.rejectedWith(/common\.NoPermission/);
-    }
-
-    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
-    expect(consumedSpace).to.be.equal(originalSpace);
-  }
-
-  itSub('Forbids changing/deleting properties of a token if the user is outside of permissions (NFT)', async ({helper}) =>  {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
-    await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount);
-  });
-
-  itSub.ifWithPallets('Forbids changing/deleting properties of a token if the user is outside of permissions (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
-    await testForbidsChangingDeletingPropertiesUserOutsideOfPermissions(token, amount);
-  });
-
-  async function testForbidsChangingDeletingPropertiesIfPropertyImmutable(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
-    const originalSpace = await prepare(token, pieces);
-
-    let i = 0;
-    for(const permission of constitution) {
-      i++;
-      if(permission.permission.mutable) continue;
-
-      await expect(
-        token.setProperties(permission.signers[0], [{key: `${i}`, value: 'Serotonin down'}]),
-        `on failing to change property ${i} by signer #0`,
-      ).to.be.rejectedWith(/common\.NoPermission/);
-
-      await expect(
-        token.deleteProperties(permission.signers[0], [i.toString()]),
-        `on failing to delete property ${i} by signer #0`,
-      ).to.be.rejectedWith(/common\.NoPermission/);
-    }
-
-    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
-    expect(consumedSpace).to.be.equal(originalSpace);
-  }
-
-  itSub('Forbids changing/deleting properties of a token if the property is permanent (immutable) (NFT)', async ({helper}) =>  {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
-    await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount);
-  });
-
-  itSub.ifWithPallets('Forbids changing/deleting properties of a token if the property is permanent (immutable) (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
-    await testForbidsChangingDeletingPropertiesIfPropertyImmutable(token, amount);
-  });
-
-  async function testForbidsAddingPropertiesIfPropertyNotDeclared(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
-    const originalSpace = await prepare(token, pieces);
-
-    await expect(
-      token.setProperties(alice, [{key: 'non-existent', value: 'I exist!'}]),
-      'on failing to add a previously non-existent property',
-    ).to.be.rejectedWith(/common\.NoPermission/);
-
-    await expect(
-      token.collection.setTokenPropertyPermissions(alice, [{key: 'now-existent', permission: {}}]),
-      'on setting a new non-permitted property',
-    ).to.be.fulfilled;
-
-    await expect(
-      token.setProperties(alice, [{key: 'now-existent', value: 'I exist!'}]),
-      'on failing to add a property forbidden by the \'None\' permission',
-    ).to.be.rejectedWith(/common\.NoPermission/);
-
-    expect(await token.getProperties(['non-existent', 'now-existent'])).to.be.empty;
-
-    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
-    expect(consumedSpace).to.be.equal(originalSpace);
-  }
-
-  itSub('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (NFT)', async ({helper}) =>  {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
-    await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount);
-  });
-
-  itSub.ifWithPallets('Forbids adding properties to a token if the property is not declared / forbidden with the \'None\' permission (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
-    await testForbidsAddingPropertiesIfPropertyNotDeclared(token, amount);
-  });
-
-  async function testForbidsAddingTooLargeProperties(token: UniqueNFToken | UniqueRFToken, pieces: bigint) {
-    const originalSpace = await prepare(token, pieces);
-
-    await expect(
-      token.collection.setTokenPropertyPermissions(alice, [
-        {key: 'a_holy_book', permission: {collectionAdmin: true, tokenOwner: true}},
-        {key: 'young_years', permission: {collectionAdmin: true, tokenOwner: true}},
-      ]),
-      'on setting new permissions for properties',
-    ).to.be.fulfilled;
-
-    // Mute the general tx parsing error
-    {
-      console.error = () => {};
-      await expect(token.setProperties(alice, [{key: 'a_holy_book', value: 'word '.repeat(6554)}]))
-        .to.be.rejected;
-    }
-
-    await expect(token.setProperties(alice, [
-      {key: 'a_holy_book', value: 'word '.repeat(3277)},
-      {key: 'young_years', value: 'neverending'.repeat(1490)},
-    ])).to.be.rejectedWith(/common\.NoSpaceForProperty/);
-
-    expect(await token.getProperties(['a_holy_book', 'young_years'])).to.be.empty;
-    const consumedSpace = await getConsumedSpace(token.collection.helper.getApi(), token.collectionId, token.tokenId, pieces == 1n ? 'NFT' : 'RFT');
-    expect(consumedSpace).to.be.equal(originalSpace);
-  }
-
-  itSub('Forbids adding too large properties to a token (NFT)', async ({helper}) =>  {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'NFT');
-    await testForbidsAddingTooLargeProperties(token, amount);
-  });
-
-  itSub.ifWithPallets('Forbids adding too large properties to a token (ReFungible)', [Pallets.ReFungible], async ({helper}) => {
-    const [token, amount] = await mintCollectionWithAllPermissionsAndToken(helper, 'RFT');
-    await testForbidsAddingTooLargeProperties(token, amount);
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itSub.ifWithPallets(`Forbids adding too many propeties to a token (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
-      const collection = await helper[testCase.mode].mintCollection(alice);
-      const maxPropertiesPerItem = 64;
-
-      for(let i = 0; i < maxPropertiesPerItem; i++) {
-        await collection.setTokenPropertyPermissions(alice, [{
-          key: `${i+1}`,
-          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
-        }]);
-      }
-
-      await expect(collection.setTokenPropertyPermissions(alice, [{
-        key: `${maxPropertiesPerItem}-th`,
-        permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
-      }])).to.be.rejectedWith(/common\.PropertyLimitReached/);
-    }));
-
-  [
-    {mode: 'nft' as const, pieces: undefined, requiredPallets: []},
-    {mode: 'rft' as const, pieces: 100n, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase =>
-    itSub.ifWithPallets(`Forbids force_repair_item from non-sudo (${testCase.mode})`, testCase.requiredPallets, async({helper}) => {
-      const propKey = 'tok-prop';
-
-      const collection = await helper[testCase.mode].mintCollection(alice, {
-        tokenPropertyPermissions: [
-          {
-            key: propKey,
-            permission: {mutable: true, tokenOwner: true},
-          },
-        ],
-      });
-      const token = await (
-        testCase.pieces
-          ? collection.mintToken(alice, testCase.pieces as any)
-          : collection.mintToken(alice)
-      );
-
-      const propDataSize = 4096;
-      const propData = 'a'.repeat(propDataSize);
-      await token.setProperties(alice, [{key: propKey, value: propData}]);
-
-      await expect(helper.executeExtrinsic(alice, 'api.tx.unique.forceRepairItem', [token.collectionId, token.tokenId], true))
-        .to.be.rejectedWith(/BadOrigin/);
-    }));
-});
-
-describe('ReFungible token properties permissions tests', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  async function prepare(helper: UniqueHelper): Promise<UniqueRFToken> {
-    const collection = await helper.rft.mintCollection(alice);
-    const token = await collection.mintToken(alice, 100n);
-
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable: true, tokenOwner: true}}]);
-
-    return token;
-  }
-
-  itSub('Forbids adding token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {
-    const token = await prepare(helper);
-
-    await token.transfer(alice, {Substrate: charlie.address}, 33n);
-
-    await expect(token.setProperties(alice, [
-      {key: 'fractals', value: 'multiverse'},
-    ])).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Forbids mutating token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {
-    const token = await prepare(helper);
-
-    await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, tokenOwner: true}}]))
-      .to.be.fulfilled;
-
-    await expect(token.setProperties(alice, [
-      {key: 'fractals', value: 'multiverse'},
-    ])).to.be.fulfilled;
-
-    await token.transfer(alice, {Substrate: charlie.address}, 33n);
-
-    await expect(token.setProperties(alice, [
-      {key: 'fractals', value: 'want to rule the world'},
-    ])).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Forbids deleting token property with tokenOwner==true when signer doesn\'t have all pieces', async ({helper}) =>  {
-    const token = await prepare(helper);
-
-    await expect(token.setProperties(alice, [
-      {key: 'fractals', value: 'one headline - why believe it'},
-    ])).to.be.fulfilled;
-
-    await token.transfer(alice, {Substrate: charlie.address}, 33n);
-
-    await expect(token.deleteProperties(alice, ['fractals'])).
-      to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Allows token property mutation with collectionOwner==true when admin doesn\'t have all pieces', async ({helper}) =>  {
-    const token = await prepare(helper);
-
-    await token.transfer(alice, {Substrate: charlie.address}, 33n);
-
-    await expect(token.collection.setTokenPropertyPermissions(alice, [{key: 'fractals', permission: {mutable:true, collectionAdmin: true}}]))
-      .to.be.fulfilled;
-
-    await expect(token.setProperties(alice, [
-      {key: 'fractals', value: 'multiverse'},
-    ])).to.be.fulfilled;
-  });
-});
deletedjs-packages/tests/src/nesting/unnest.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nesting/unnest.test.ts
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../util/index.js';
-import {CrossAccountId, UniqueFTCollection, UniqueNFToken, UniqueRFToken} from '@unique/playgrounds/src/unique.js';
-
-describe('Integration Test: Unnesting', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 50n, 50n], donor);
-    });
-  });
-
-  itSub('NFT: allows the owner to successfully unnest a token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    // Create a nested token
-    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
-
-    // Unnest
-    await expect(nestedToken.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}), 'while unnesting').to.be.fulfilled;
-    expect(await nestedToken.getOwner()).to.be.deep.equal({Substrate: alice.address});
-
-    // Nest and burn
-    await nestedToken.nest(alice, targetToken);
-    await expect(nestedToken.burnFrom(alice, targetToken.nestingAccount()), 'while burning').to.be.fulfilled;
-    await expect(nestedToken.getOwner()).to.be.rejected;
-  });
-
-  itSub('NativeFungible: allows the owner to successfully unnest a token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    const collectionFT = helper.ft.getCollectionObject(0);
-
-    // Nest
-    await collectionFT.transfer(alice, targetToken.nestingAccount(), 10n);
-    // Unnest
-    await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
-
-    expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
-  });
-
-  itSub('Fungible: allows the owner to successfully unnest a token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    const collectionFT = await helper.ft.mintCollection(alice);
-
-    // Nest and unnest
-    await collectionFT.mint(alice, 10n, targetToken.nestingAccount());
-    await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
-    expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(9n);
-    expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
-
-    // Nest and burn
-    await collectionFT.transfer(alice, targetToken.nestingAccount(), 5n);
-    await expect(collectionFT.burnTokensFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled;
-    expect(await collectionFT.getBalance({Substrate: alice.address})).to.be.equal(4n);
-    expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
-    expect(await targetToken.getChildren()).to.be.length(0);
-  });
-
-  itSub.ifWithPallets('ReFungible: allows the owner to successfully unnest a token', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    const collectionRFT = await helper.rft.mintCollection(alice);
-
-    // Nest and unnest
-    const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount());
-    await expect(token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n), 'while unnesting').to.be.fulfilled;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n);
-    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
-
-    // Nest and burn
-    await token.transfer(alice, targetToken.nestingAccount(), 5n);
-    await expect(token.burnFrom(alice, targetToken.nestingAccount(), 6n), 'while burning').to.be.fulfilled;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(4n);
-    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
-    expect(await targetToken.getChildren()).to.be.length(0);
-  });
-
-  async function checkNestedAmountState({
-    expectedBalance,
-    childrenShouldPresent,
-    nested,
-    targetNft,
-  }: {
-    expectedBalance: bigint,
-    childrenShouldPresent: boolean,
-    nested: UniqueFTCollection | UniqueRFToken,
-    targetNft: UniqueNFToken,
-  }) {
-    const balance = await nested.getBalance(targetNft.nestingAccount());
-    expect(balance).to.be.equal(expectedBalance);
-
-    const children = await targetNft.getChildren();
-
-    if(childrenShouldPresent) {
-      expect(children[0]).to.be.deep.equal({
-        collectionId: nested.collectionId,
-        tokenId: (nested instanceof UniqueFTCollection) ? 0 : nested.tokenId,
-      });
-    } else {
-      expect(children.length).to.be.equal(0);
-    }
-  }
-
-  function ownerOrAdminUnnestCases(modes: ('ft' | 'nft' | 'rft')[]): {
-    mode: 'ft' | 'nft' | 'rft',
-    sender: string,
-    op: 'transfer' | 'burn',
-    requiredPallets: Pallets[],
-  }[] {
-    const senders = ['owner', 'admin'];
-    const ops = ['transfer', 'burn'];
-
-    const cases = [];
-    for(const mode of modes) {
-      const requiredPallets = (mode === 'rft')
-        ? [Pallets.ReFungible]
-        : [];
-
-      for(const sender of senders) {
-        for(const op of ops) {
-          cases.push({
-            mode: mode as 'ft' | 'nft' | 'rft',
-            sender,
-            op: op as 'transfer' | 'burn',
-            requiredPallets,
-          });
-        }
-      }
-    }
-
-    return cases;
-  }
-
-  ownerOrAdminUnnestCases(['ft', 'rft']).map(testCase =>
-    itSub.ifWithPallets(`[${testCase.mode}]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, testCase.requiredPallets, async({helper}) => {
-      const owner = alice;
-      const admin = bob;
-
-      const unnester = (testCase.sender === 'owner')
-        ? owner
-        : admin;
-
-      const collectionNFT = await helper.nft.mintCollection(owner);
-      await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}});
-
-      const collectionNested = await helper[testCase.mode as 'ft' | 'rft'].mintCollection(owner, {
-        limits: {
-          ownerCanTransfer: true,
-        },
-      });
-      await collectionNested.addAdmin(owner, {Substrate: admin.address});
-
-      const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address});
-
-      let nested: UniqueFTCollection | UniqueRFToken;
-      const totalAmount = 5n;
-      const firstUnnestAmount = 2n;
-      const restUnnestAmount = totalAmount - firstUnnestAmount;
-
-      if(collectionNested instanceof UniqueFTCollection) {
-        await collectionNested.mint(owner, totalAmount, {Substrate: charlie.address});
-        nested = collectionNested;
-      } else {
-        nested = await collectionNested.mintToken(owner, totalAmount, {Substrate: charlie.address});
-      }
-
-      // transfer/burn `amount` of nested assets by `unnester`.
-      const doOperationAndCheck = async ({
-        amount,
-        shouldBeNestedAfterOp,
-      }: {
-        amount: bigint,
-        shouldBeNestedAfterOp: boolean,
-      }) => {
-        const nestedBalanceBeforeOp = await nested.getBalance(targetNft.nestingAccount());
-
-        if(testCase.op === 'transfer') {
-          const bobBalanceBeforeOp = await nested.getBalance({Substrate: bob.address});
-
-          await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address}, amount);
-          expect(await nested.getBalance({Substrate: bob.address})).to.be.equal(bobBalanceBeforeOp + amount);
-        } else {
-          if(nested instanceof UniqueFTCollection) {
-            await nested.burnTokensFrom(unnester, targetNft.nestingAccount(), amount);
-          } else {
-            await nested.burnFrom(unnester, targetNft.nestingAccount(), amount);
-          }
-        }
-
-        await checkNestedAmountState({
-          expectedBalance: nestedBalanceBeforeOp - amount,
-          childrenShouldPresent: shouldBeNestedAfterOp,
-          nested,
-          targetNft,
-        });
-      };
-
-      // Initial setup: nest (fungibles/rft parts).
-      // Check NFT's balance of nested assets and NFT's children.
-      await nested.transfer(charlie, targetNft.nestingAccount(), totalAmount);
-      await checkNestedAmountState({
-        expectedBalance: totalAmount,
-        childrenShouldPresent: true,
-        nested,
-        targetNft,
-      });
-
-      // Transfer/burn only a part of nested assets.
-      // Check that NFT's balance of the nested assets correctly decreased and NFT's children are not changed.
-      await doOperationAndCheck({
-        amount: firstUnnestAmount,
-        shouldBeNestedAfterOp: true,
-      });
-
-      // Transfer/burn all remaining nested assets.
-      // Check that NFT's balance of the nested assets is 0 and NFT has no more children.
-      await doOperationAndCheck({
-        amount: restUnnestAmount,
-        shouldBeNestedAfterOp: false,
-      });
-    }));
-
-  ownerOrAdminUnnestCases(['nft']).map(testCase =>
-    itSub(`[nft]: allows a collection ${testCase.sender} to ${testCase.op} nested token`, async ({helper}) => {
-      const owner = alice;
-      const admin = bob;
-
-      const unnester = (testCase.sender === 'owner')
-        ? owner
-        : admin;
-
-      const collectionNFT = await helper.nft.mintCollection(owner);
-      await collectionNFT.setPermissions(owner, {nesting: {tokenOwner: true}});
-
-      const collectionNested = await helper.nft.mintCollection(owner, {
-        limits: {
-          ownerCanTransfer: true,
-        },
-      });
-      await collectionNested.addAdmin(owner, {Substrate: admin.address});
-
-      const targetNft = await collectionNFT.mintToken(owner, {Substrate: charlie.address});
-      const nested = await collectionNested.mintToken(owner, {Substrate: charlie.address});
-
-      await nested.transfer(charlie, targetNft.nestingAccount());
-      expect(await targetNft.getChildren()).to.be.deep.equal([{
-        collectionId: nested.collectionId,
-        tokenId: nested.tokenId,
-      }]);
-
-      if(testCase.op === 'transfer') {
-        await nested.transferFrom(unnester, targetNft.nestingAccount(), {Substrate: bob.address});
-      } else {
-        await nested.burnFrom(unnester, targetNft.nestingAccount());
-      }
-
-      expect((await targetNft.getChildren()).length).to.be.equal(0);
-    }));
-});
-
-describe('Negative Test: Unnesting', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
-    });
-  });
-
-  itSub('Disallows a non-owner to unnest/burn a token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    // Create a nested token
-    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
-
-    // Try to unnest
-    await expect(nestedToken.unnest(bob, targetToken, {Substrate: alice.address})).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-
-    // Try to burn
-    await expect(nestedToken.burnFrom(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-  });
-
-  // todo another test for creating excessive depth matryoshka with Ethereum?
-
-  // Recursive nesting
-  itSub('Prevents Ouroboros creation', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    // Fail to create a nested token ouroboros
-    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
-    await expect(targetToken.nest(alice, nestedToken)).to.be.rejectedWith(/^structure\.OuroborosDetected$/);
-  });
-});
deletedjs-packages/tests/src/nextSponsoring.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/nextSponsoring.test.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from './util/index.js';
-
-const SPONSORING_TIMEOUT = 5;
-
-describe('Integration Test getNextSponsored(collection_id, owner, item_id):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
-    });
-  });
-
-  itSub('NFT', async ({helper}) => {
-    // Non-existing collection
-    expect(await helper.collection.getTokenNextSponsored(0, 0, {Substrate: alice.address})).to.be.null;
-
-    const collection = await helper.nft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice);
-
-    // Check with Disabled sponsoring state
-    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null;
-
-    // Check with Unconfirmed sponsoring state
-    await collection.setSponsor(alice, bob.address);
-    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null;
-
-    // Check with Confirmed sponsoring state
-    await collection.confirmSponsorship(bob);
-    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0);
-
-    // Check after transfer
-    await token.transfer(alice, {Substrate: bob.address});
-    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT);
-
-    // Non-existing token
-    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null;
-  });
-
-  itSub('Fungible', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {});
-    await collection.mint(alice, 10n);
-
-    // Check with Disabled sponsoring state
-    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null;
-
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-
-    // Check with Confirmed sponsoring state
-    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.equal(0);
-
-    // Check after transfer
-    await collection.transfer(alice, {Substrate: bob.address});
-    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT);
-  });
-
-  itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {});
-    const token = await collection.mintToken(alice, 10n);
-
-    // Check with Disabled sponsoring state
-    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.null;
-
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-
-    // Check with Confirmed sponsoring state
-    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.equal(0);
-
-    // Check after transfer
-    await token.transfer(alice, {Substrate: bob.address});
-    expect(await token.getNextSponsored({Substrate: alice.address})).to.be.lessThanOrEqual(SPONSORING_TIMEOUT);
-
-    // Non-existing token
-    expect(await collection.getTokenNextSponsored(0, {Substrate: alice.address})).to.be.null;
-  });
-});
deletedjs-packages/tests/src/pallet-presence.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/pallet-presence.test.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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';
-
-// Pallets that must always be present
-const requiredPallets = [
-  'balances',
-  'balancesadapter',
-  'common',
-  'timestamp',
-  'transactionpayment',
-  'treasury',
-  'statetriemigration',
-  'structure',
-  'system',
-  'utility',
-  'vesting',
-  'parachainsystem',
-  'parachaininfo',
-  'evm',
-  'evmcodersubstrate',
-  'evmcontracthelpers',
-  'evmmigration',
-  'evmtransactionpayment',
-  'ethereum',
-  'fungible',
-  'xcmpqueue',
-  'polkadotxcm',
-  'cumulusxcm',
-  'dmpqueue',
-  'inflation',
-  'unique',
-  'nonfungible',
-  'charging',
-  'configuration',
-  'tokens',
-  'xtokens',
-  'maintenance',
-];
-
-// Pallets that depend on consensus and governance configuration
-const consensusPallets = [
-  'sudo',
-  'aura',
-  'auraext',
-];
-
-describe('Pallet presence', () => {
-  before(async () => {
-    await usingPlaygrounds(async helper => {
-      const runtimeVersion = await helper.callRpc('api.rpc.state.getRuntimeVersion', []);
-      const chain = runtimeVersion.specName;
-
-      const refungible = 'refungible';
-      const foreignAssets = 'foreignassets';
-      const appPromotion = 'apppromotion';
-      const collatorSelection = ['authorship', 'session', 'collatorselection'];
-      const preimage = ['preimage'];
-      const governance = [
-        'council',
-        'councilmembership',
-        'democracy',
-        'fellowshipcollective',
-        'fellowshipreferenda',
-        'origins',
-        'scheduler',
-        'technicalcommittee',
-        'technicalcommitteemembership',
-        'identity',
-      ];
-      const testUtils = 'testutils';
-
-      if(chain.eq('opal')) {
-        requiredPallets.push(
-          refungible,
-          foreignAssets,
-          appPromotion,
-          testUtils,
-          ...collatorSelection,
-          ...preimage,
-          ...governance,
-        );
-      } else if(chain.eq('quartz') || chain.eq('sapphire')) {
-        requiredPallets.push(
-          refungible,
-          appPromotion,
-          foreignAssets,
-          ...collatorSelection,
-          ...preimage,
-          ...governance,
-        );
-      } else if(chain.eq('unique')) {
-        // Insert Unique additional pallets here
-        requiredPallets.push(
-          refungible,
-          foreignAssets,
-          appPromotion,
-          ...preimage,
-          ...governance,
-        );
-      }
-    });
-  });
-
-  itSub('Required pallets are present', ({helper}) => {
-    expect(helper.fetchAllPalletNames()).to.contain.members([...requiredPallets].sort());
-  });
-
-  itSub('Governance and consensus pallets are present', ({helper}) => {
-    expect(helper.fetchAllPalletNames()).to.contain.members([...consensusPallets].sort());
-  });
-
-  itSub('No extra pallets are included', ({helper}) => {
-    expect(helper.fetchAllPalletNames().sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort());
-  });
-});
deletedjs-packages/tests/src/performance.seq.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/performance.seq.test.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright 2019-2023 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {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/src/types.js';
-import {UniqueHelper} from '@unique/playgrounds/src/unique.js';
-
-describe('Performace tests', () => {
-  let alice: IKeyringPair;
-  const MAX_TOKENS_TO_MINT = 120;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([100_000n], donor);
-    });
-  });
-
-  itSub('NFT tokens minting', async ({helper}) => {
-    const propertyKey = 'prop-a';
-    const collection = await helper.nft.mintCollection(alice, {
-      name: 'test properties',
-      description: 'test properties collection',
-      tokenPrefix: 'TPC',
-      tokenPropertyPermissions: [
-        {key: propertyKey, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
-      ],
-    });
-
-    const step = 1_000;
-    const sizeOfKey = sizeOfEncodedStr(propertyKey);
-    let currentSize = step;
-    let startCount = 0;
-    let minterFunc = tryMintUnsafeRPC;
-    try {
-      startCount = await tryMintUnsafeRPC(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address});
-    }
-    catch (e) {
-      startCount = await tryMintExplicit(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address});
-      minterFunc = tryMintExplicit;
-    }
-
-    expect(startCount).to.be.equal(MAX_TOKENS_TO_MINT);
-
-    while(currentSize <= 32_000) {
-      const property = {key: propertyKey, value: 'A'.repeat(currentSize - sizeOfKey - sizeOfInt(currentSize))};
-      const tokens = await minterFunc(helper, alice, MAX_TOKENS_TO_MINT, collection.collectionId, {Substrate: alice.address}, property);
-      expect(tokens).to.be.equal(MAX_TOKENS_TO_MINT);
-
-      currentSize += step;
-      await helper.wait.newBlocks(2);
-    }
-  });
-});
-
-
-const dryRun = async (api: ApiPromise, signer: IKeyringPair, tx: any) => {
-  const signed = await tx.signAsync(signer);
-  const dryRun = await api.rpc.system.dryRun(signed.toHex());
-  return dryRun.isOk && dryRun.asOk.isOk;
-};
-
-const getTokens = (tokensCount: number, owner: ICrossAccountId, property?: IProperty) => (new Array(tokensCount)).fill(0).map(() => {
-  const token = {owner} as {owner: ICrossAccountId, properties?: IProperty[]};
-  if(property) token.properties = [property];
-  return token;
-});
-
-const tryMintUnsafeRPC = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise<number> => {
-  if(tokensCount < 10) console.log('try mint', tokensCount, 'tokens');
-  const tokens = getTokens(tokensCount, owner, property);
-  const tx = helper.constructApiCall('api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]);
-  if(!(await dryRun(helper.getApi(), signer, tx))) {
-    if(tokensCount < 2) return 0;
-    return await tryMintUnsafeRPC(helper, signer, tokensCount - 1, collectionId, owner, property);
-  }
-  await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]);
-  return tokensCount;
-};
-
-const tryMintExplicit = async (helper: UniqueHelper, signer: IKeyringPair, tokensCount: number, collectionId: number, owner: ICrossAccountId, property?: IProperty): Promise<number> => {
-  const tokens = getTokens(tokensCount, owner, property);
-  try {
-    await helper.executeExtrinsic(signer, 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}]);
-  }
-  catch (e) {
-    if(tokensCount < 2) return 0;
-    return await tryMintExplicit(helper, signer, tokensCount - 1, collectionId, owner, property);
-  }
-  return tokensCount;
-};
-
-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();
-function sizeOfEncodedStr(v: string) {
-  const encoded = UTF8_ENCODER.encode(v);
-  return sizeOfInt(encoded.length) + encoded.length;
-}
deletedjs-packages/tests/src/refungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/refungible.test.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from './util/index.js';
-
-const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n;
-
-describe('integration test: Refungible functionality:', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
-    });
-  });
-
-  itSub('Create refungible collection and token', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    const itemCountBefore = await collection.getLastTokenId();
-    const token = await collection.mintToken(alice, 100n);
-
-    const itemCountAfter = await collection.getLastTokenId();
-
-    // What to expect
-    expect(token?.tokenId).to.be.gte(itemCountBefore);
-    expect(itemCountAfter).to.be.equal(itemCountBefore + 1);
-    expect(itemCountAfter.toString()).to.be.equal(token?.tokenId.toString());
-  });
-
-  itSub('Checking RPC methods when interacting with maximum allowed values (MAX_REFUNGIBLE_PIECES)', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    const token = await collection.mintToken(alice, MAX_REFUNGIBLE_PIECES);
-
-    expect(await collection.getTokenBalance(token.tokenId, {Substrate: alice.address})).to.be.equal(MAX_REFUNGIBLE_PIECES);
-
-    await collection.transferToken(alice, token.tokenId, {Substrate: bob.address}, MAX_REFUNGIBLE_PIECES);
-    expect(await collection.getTokenBalance(token.tokenId, {Substrate: bob.address})).to.be.equal(MAX_REFUNGIBLE_PIECES);
-    expect(await token.getTotalPieces()).to.be.equal(MAX_REFUNGIBLE_PIECES);
-
-    await expect(collection.mintToken(alice, MAX_REFUNGIBLE_PIECES + 1n))
-      .to.eventually.be.rejectedWith(/refungible\.WrongRefungiblePieces/);
-  });
-
-  itSub('RPC method tokenOwners for refungible collection and token', async ({helper}) => {
-    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
-    const facelessCrowd = (await helper.arrange.createAccounts(Array(7).fill(0n), donor)).map(keyring => ({Substrate: keyring.address}));
-
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-
-    const token = await collection.mintToken(alice, 10_000n);
-
-    await token.transfer(alice, {Substrate: bob.address}, 1000n);
-    await token.transfer(alice, ethAcc, 900n);
-
-    for(let i = 0; i < 7; i++) {
-      await token.transfer(alice, facelessCrowd[i], 50n * BigInt(i + 1));
-    }
-
-    const owners = await token.getTop10Owners();
-
-    // What to expect
-    expect(owners).to.deep.include.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]);
-    expect(owners.length).to.be.equal(10);
-
-    const [eleven] = await helper.arrange.createAccounts([0n], donor);
-    expect(await token.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true;
-    expect((await token.getTop10Owners()).length).to.be.equal(10);
-  });
-
-  itSub('Create multiple tokens', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    // TODO: fix mintMultipleTokens
-    // await collection.mintMultipleTokens(alice, [
-    //   {owner: {Substrate: alice.address}, pieces: 1n},
-    //   {owner: {Substrate: alice.address}, pieces: 2n},
-    //   {owner: {Substrate: alice.address}, pieces: 100n},
-    // ]);
-    await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, [
-      {pieces: 1n},
-      {pieces: 2n},
-      {pieces: 100n},
-    ]);
-    const lastTokenId = await collection.getLastTokenId();
-    expect(lastTokenId).to.be.equal(3);
-    expect(await collection.getTokenBalance(lastTokenId, {Substrate: alice.address})).to.be.equal(100n);
-  });
-
-  itSub('Set allowance for token', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
-
-    expect(await token.approve(alice, {Substrate: bob.address}, 60n)).to.be.true;
-    expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(60n);
-
-    expect(await token.transferFrom(bob, {Substrate: alice.address}, {Substrate: bob.address}, 20n)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(80n);
-    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(20n);
-    expect(await token.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(40n);
-  });
-
-  itSub('Create new collection with properties', async ({helper}) => {
-    const properties = [{key: 'key1', value: 'val1'}];
-    const tokenPropertyPermissions = [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}];
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test', properties, tokenPropertyPermissions});
-    const info = await collection.getData();
-    expect(info?.raw.properties).to.be.deep.equal(properties);
-    expect(info?.raw.tokenPropertyPermissions).to.be.deep.equal(tokenPropertyPermissions);
-  });
-});
deletedjs-packages/tests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/removeCollectionAdmin.test.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
-    });
-  });
-
-  itSub('Remove collection admin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-1', tokenPrefix: 'RCA'});
-    const collectionInfo = await collection.getData();
-    expect(collectionInfo?.raw.owner.toString()).to.be.deep.eq(alice.address);
-    // first - add collection admin Bob
-    await collection.addAdmin(alice, {Substrate: bob.address});
-
-    const adminListAfterAddAdmin = await collection.getAdmins();
-    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
-
-    // then remove bob from admins of collection
-    await collection.removeAdmin(alice, {Substrate: bob.address});
-
-    const adminListAfterRemoveAdmin = await collection.getAdmins();
-    expect(adminListAfterRemoveAdmin).not.to.be.deep.contains({Substrate: bob.address});
-  });
-
-  itSub('Remove admin from collection that has no admins', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-2', tokenPrefix: 'RCA'});
-
-    const adminListBeforeAddAdmin = await collection.getAdmins();
-    expect(adminListBeforeAddAdmin).to.have.lengthOf(0);
-
-    await expect(collection.removeAdmin(alice, {Substrate: alice.address})).to.be.rejectedWith('common.UserIsNotCollectionAdmin');
-  });
-});
-
-describe('Negative Integration Test removeCollectionAdmin(collection_id, account_id):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('Can\'t remove collection admin from not existing collection', async ({helper}) => {
-    const collectionId = NON_EXISTENT_COLLECTION_ID;
-
-    await expect(helper.collection.removeAdmin(alice, collectionId, {Substrate: bob.address}))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('Can\'t remove collection admin from deleted collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-2', tokenPrefix: 'RCA'});
-
-    expect(await collection.burn(alice)).to.be.true;
-
-    await expect(helper.collection.removeAdmin(alice, collection.collectionId, {Substrate: bob.address}))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('Regular user can\'t remove collection admin', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-3', tokenPrefix: 'RCA'});
-
-    await collection.addAdmin(alice, {Substrate: bob.address});
-
-    await expect(collection.removeAdmin(charlie, {Substrate: bob.address}))
-      .to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('Admin can\'t remove collection admin.', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionAdmin-Neg-4', tokenPrefix: 'RCA'});
-
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await collection.addAdmin(alice, {Substrate: charlie.address});
-
-    const adminListAfterAddAdmin = await collection.getAdmins();
-    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
-    expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: charlie.address});
-
-    await expect(collection.removeAdmin(charlie, {Substrate: bob.address}))
-      .to.be.rejectedWith(/common\.NoPermission/);
-
-    const adminListAfterRemoveAdmin = await collection.getAdmins();
-    expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: bob.address});
-    expect(adminListAfterRemoveAdmin).to.be.deep.contains({Substrate: charlie.address});
-  });
-});
deletedjs-packages/tests/src/removeCollectionSponsor.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/removeCollectionSponsor.test.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('integration test: ext. removeCollectionSponsor():', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('Removing NFT collection sponsor stops sponsorship', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-1', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    await collection.removeSponsor(alice);
-
-    // Find unused address
-    const [zeroBalance] = await helper.arrange.createAccounts([0n], donor);
-
-    // Mint token for unused address
-    const token = await collection.mintToken(alice, {Substrate: zeroBalance.address});
-
-    // Transfer this tokens from unused address to Alice - should fail
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    await expect(token.transfer(zeroBalance, {Substrate: alice.address}))
-      .to.be.rejectedWith('Inability to pay some fees');
-    const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
-  });
-
-  itSub('Remove a sponsor after it was already removed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-2', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
-    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
-  });
-
-  itSub('Remove sponsor in a collection that never had the sponsor set', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-3', tokenPrefix: 'RCS'});
-    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
-  });
-
-  itSub('Remove sponsor for a collection that had the sponsor set, but not confirmed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-4', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await expect(collection.removeSponsor(alice)).to.not.be.rejected;
-  });
-
-  itSub('Remove a sponsor from a collection with collection admin permissions', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-1', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.addAdmin(alice, {Substrate: charlie.address});
-    await expect(collection.removeSponsor(charlie)).not.to.be.rejected;
-  });
-});
-
-describe('(!negative test!) integration test: ext. removeCollectionSponsor():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('(!negative test!) Remove sponsor for a collection that never existed', async ({helper}) => {
-    const collectionId = NON_EXISTENT_COLLECTION_ID;
-    await expect(helper.collection.removeSponsor(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('(!negative test!) Remove sponsor for a collection by regular user', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-2', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await expect(collection.removeSponsor(charlie)).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('(!negative test!) Remove sponsor in a destroyed collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-3', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.burn(alice);
-    await expect(collection.removeSponsor(alice)).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('Set - remove - confirm: fails', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-4', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.removeSponsor(alice);
-    await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
-  });
-
-  itSub('Set - confirm - remove - confirm: Sponsor cannot come back', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'RemoveCollectionSponsor-Neg-5', tokenPrefix: 'RCS'});
-    await collection.setSponsor(alice, bob.address);
-    await collection.confirmSponsorship(bob);
-    await collection.removeSponsor(alice);
-    await expect(collection.confirmSponsorship(bob)).to.be.rejectedWith(/common\.ConfirmSponsorshipFail/);
-  });
-});
deletedjs-packages/tests/src/rpc.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/rpc.test.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {usingPlaygrounds, itSub, expect} from './util/index.js';
-import {ICrossAccountId} from '@unique/playgrounds/src/types.js';
-
-describe('integration test: RPC methods', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
-    });
-  });
-
-  itSub('returns None for fungible collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'RPC-1', tokenPrefix: 'RPC'});
-    const owner = (await helper.callRpc('api.rpc.unique.tokenOwner', [collection.collectionId, 0])).toJSON() as any;
-    expect(owner).to.be.null;
-  });
-
-  itSub('RPC method tokenOwners for fungible collection and token', async ({helper}) => {
-    // Set-up a few token owners of all stripes
-    const ethAcc = {Ethereum: '0x67fb3503a61b284dc83fa96dceec4192db47dc7c'};
-    const facelessCrowd = (await helper.arrange.createAccounts([0n, 0n, 0n, 0n, 0n, 0n, 0n], donor))
-      .map(i => ({Substrate: i.address}));
-
-    const collection = await helper.ft.mintCollection(alice, {name: 'RPC-2', tokenPrefix: 'RPC'});
-    // mint some maximum (u128) amounts of tokens possible
-    await collection.mint(alice, (1n << 128n) - 1n);
-
-    await collection.transfer(alice, {Substrate: bob.address}, 1000n);
-    await collection.transfer(alice, ethAcc, 900n);
-
-    for(let i = 0; i < facelessCrowd.length; i++) {
-      await collection.transfer(alice, facelessCrowd[i], 1n);
-    }
-    // Set-up over
-
-    const owners = await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0]);
-    const ids = owners.toHuman() as ICrossAccountId[];
-
-    expect(ids).to.have.deep.members([{Substrate: alice.address}, ethAcc, {Substrate: bob.address}, ...facelessCrowd]);
-    expect(owners.length == 10).to.be.true;
-
-    // Make sure only 10 results are returned with this RPC
-    const [eleven] = await helper.arrange.createAccounts([0n], donor);
-    expect(await collection.transfer(alice, {Substrate: eleven.address}, 10n)).to.be.true;
-    expect((await helper.callRpc('api.rpc.unique.tokenOwners', [collection.collectionId, 0])).length).to.be.equal(10);
-  });
-});
deletedjs-packages/tests/src/scheduler.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/scheduler.seqtest.ts
+++ /dev/null
@@ -1,796 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/src/unique.dev.js';
-
-describe('Scheduling token and balance transfers', () => {
-  let superuser: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]);
-
-      superuser = await privateKey('//Alice');
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-
-      await helper.testUtils.enable(Pallets.TestUtils);
-    });
-  });
-
-  beforeEach(async () => {
-    await usingPlaygrounds(async (helper) => {
-      await helper.wait.noScheduledTasks();
-    });
-  });
-
-  itSched('Can delay a transfer of an owned token', async (scheduleKind, {helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-    const blocksBeforeExecution = 4;
-    await helper.scheduler.scheduleAfter(blocksBeforeExecution, {scheduledId})
-      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
-    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-
-  itSched('Can transfer funds periodically', async (scheduleKind, {helper}) => {
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-    const waitForBlocks = 1;
-
-    const amount = 1n * helper.balance.getOneTokenNominal();
-    const periodic = {
-      period: 2,
-      repetitions: 2,
-    };
-
-    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic})
-      .balance.transferToSubstrate(alice, bob.address, amount);
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
-    expect(bobsBalanceAfterFirst)
-      .to.be.equal(
-        bobsBalanceBefore + 1n * amount,
-        '#1 Balance of the recipient should be increased by 1 * amount',
-      );
-
-    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
-
-    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
-    expect(bobsBalanceAfterSecond)
-      .to.be.equal(
-        bobsBalanceBefore + 2n * amount,
-        '#2 Balance of the recipient should be increased by 2 * amount',
-      );
-  });
-
-  itSub('Can cancel a scheduled operation which has not yet taken effect', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-
-    const scheduledId = helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
-      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.scheduler.cancelScheduled(alice, scheduledId);
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itSub('Can cancel a periodic operation (transfer of funds)', async ({helper}) => {
-    const waitForBlocks = 1;
-    const periodic = {
-      period: 3,
-      repetitions: 2,
-    };
-
-    const scheduledId = helper.arrange.makeScheduledId();
-
-    const amount = 1n * helper.balance.getOneTokenNominal();
-
-    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, periodic})
-      .balance.transferToSubstrate(alice, bob.address, amount);
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const bobsBalanceAfterFirst = await helper.balance.getSubstrate(bob.address);
-
-    expect(bobsBalanceAfterFirst)
-      .to.be.equal(
-        bobsBalanceBefore + 1n * amount,
-        '#1 Balance of the recipient should be increased by 1 * amount',
-      );
-
-    await helper.scheduler.cancelScheduled(alice, scheduledId);
-    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
-
-    const bobsBalanceAfterSecond = await helper.balance.getSubstrate(bob.address);
-    expect(bobsBalanceAfterSecond)
-      .to.be.equal(
-        bobsBalanceAfterFirst,
-        '#2 Balance of the recipient should not be changed',
-      );
-  });
-
-  itSched('scheduler will not insert more tasks than allowed', async (scheduleKind, {helper}) => {
-    const maxScheduledPerBlock = 50;
-    let fillScheduledIds = new Array(maxScheduledPerBlock);
-    let extraScheduledId = undefined;
-
-    if(scheduleKind == 'named') {
-      const scheduledIds = helper.arrange.makeScheduledIds(maxScheduledPerBlock + 1);
-      fillScheduledIds = scheduledIds.slice(0, maxScheduledPerBlock);
-      extraScheduledId = scheduledIds[maxScheduledPerBlock];
-    }
-
-    // Since the dev node has Instant Seal,
-    // we need a larger gap between the current block and the target one.
-    //
-    // We will schedule `maxScheduledPerBlock` transaction into the target block,
-    // so we need at least `maxScheduledPerBlock`-wide gap.
-    // We add some additional blocks to this gap to mitigate possible PolkadotJS delays.
-    const waitForBlocks = await helper.arrange.isDevNode() ? maxScheduledPerBlock + 5 : 5;
-
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks;
-
-    const amount = 1n * helper.balance.getOneTokenNominal();
-
-    const balanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    // Fill the target block
-    for(let i = 0; i < maxScheduledPerBlock; i++) {
-      await helper.scheduler.scheduleAt(executionBlock, {scheduledId: fillScheduledIds[i]})
-        .balance.transferToSubstrate(superuser, bob.address, amount);
-    }
-
-    // Try to schedule a task into a full block
-    await expect(helper.scheduler.scheduleAt(executionBlock, {scheduledId: extraScheduledId})
-      .balance.transferToSubstrate(superuser, bob.address, amount))
-      .to.be.rejectedWith(/scheduler\.AgendaIsExhausted/);
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const balanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(balanceAfter > balanceBefore).to.be.true;
-
-    const diff = balanceAfter - balanceBefore;
-    expect(diff).to.be.equal(amount * BigInt(maxScheduledPerBlock));
-  });
-
-  itSched.ifWithPallets('Scheduled tasks are transactional', [Pallets.TestUtils], async (scheduleKind, {helper}) => {
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-    const waitForBlocks = 4;
-
-    const initTestVal = 42;
-    const changedTestVal = 111;
-
-    await helper.testUtils.setTestValue(alice, initTestVal);
-
-    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId})
-      .testUtils.setTestValueAndRollback(alice, changedTestVal);
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const testVal = await helper.testUtils.testValue();
-    expect(testVal, 'The test value should NOT be commited')
-      .to.be.equal(initTestVal);
-  });
-
-  itSched.ifWithPallets('Scheduled tasks should take correct fees', [Pallets.TestUtils], async function(scheduleKind, {helper}) {
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-    const waitForBlocks = 4;
-    const periodic = {
-      period: 2,
-      repetitions: 2,
-    };
-
-    const dummyTx = helper.constructApiCall('api.tx.testUtils.justTakeFee', []);
-    const scheduledLen = dummyTx.callIndex.length;
-
-    const expectedScheduledFee = (await helper.getPaymentInfo(alice, dummyTx, scheduledLen))
-      .partialFee.toBigInt();
-
-    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId, periodic})
-      .testUtils.justTakeFee(alice);
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    const aliceInitBalance = await helper.balance.getSubstrate(alice.address);
-    let diff;
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const aliceBalanceAfterFirst = await helper.balance.getSubstrate(alice.address);
-    expect(
-      aliceBalanceAfterFirst < aliceInitBalance,
-      '[after execution #1] Scheduled task should take a fee',
-    ).to.be.true;
-
-    diff = aliceInitBalance - aliceBalanceAfterFirst;
-    expect(diff).to.be.equal(
-      expectedScheduledFee,
-      'Scheduled task should take the right amount of fees',
-    );
-
-    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
-
-    const aliceBalanceAfterSecond = await helper.balance.getSubstrate(alice.address);
-    expect(
-      aliceBalanceAfterSecond < aliceBalanceAfterFirst,
-      '[after execution #2] Scheduled task should take a fee',
-    ).to.be.true;
-
-    diff = aliceBalanceAfterFirst - aliceBalanceAfterSecond;
-    expect(diff).to.be.equal(
-      expectedScheduledFee,
-      'Scheduled task should take the right amount of fees',
-    );
-  });
-
-  // Check if we can cancel a scheduled periodic operation
-  // in the same block in which it is running
-  itSub.ifWithPallets('Can cancel the periodic sheduled tx when the tx is running', [Pallets.TestUtils], async ({helper}) => {
-    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
-    const blocksBeforeExecution = 10;
-    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
-
-    const [
-      scheduledId,
-      scheduledCancelId,
-    ] = helper.arrange.makeScheduledIds(2);
-
-    const periodic = {
-      period: 5,
-      repetitions: 5,
-    };
-
-    const initTestVal = 0;
-    const incTestVal = initTestVal + 1;
-    const finalTestVal = initTestVal + 2;
-
-    await helper.testUtils.setTestValue(alice, initTestVal);
-
-    await helper.scheduler.scheduleAt<DevUniqueHelper>(firstExecutionBlockNumber, {scheduledId, periodic})
-      .testUtils.incTestValue(alice);
-
-    // Cancel the inc tx after 2 executions
-    // *in the same block* in which the second execution is scheduled
-    await helper.scheduler.scheduleAt(
-      firstExecutionBlockNumber + periodic.period,
-      {scheduledId: scheduledCancelId},
-    ).scheduler.cancelScheduled(alice, scheduledId);
-
-    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
-
-    // execution #0
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(incTestVal);
-
-    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);
-
-    // execution #1
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(finalTestVal);
-
-    for(let i = 1; i < periodic.repetitions; i++) {
-      await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period * (i + 1));
-      expect(await helper.testUtils.testValue())
-        .to.be.equal(finalTestVal);
-    }
-  });
-
-  itSub.ifWithPallets('A scheduled operation can cancel itself', [Pallets.TestUtils], async ({helper}) => {
-    const scheduledId = helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-    const periodic = {
-      period: 2,
-      repetitions: 5,
-    };
-
-    const initTestVal = 0;
-    const maxTestVal = 2;
-
-    await helper.testUtils.setTestValue(alice, initTestVal);
-
-    await helper.scheduler.scheduleAfter<DevUniqueHelper>(waitForBlocks, {scheduledId, periodic})
-      .testUtils.selfCancelingInc(alice, scheduledId, maxTestVal);
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    // execution #0
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(initTestVal + 1);
-
-    await helper.wait.forParachainBlockNumber(executionBlock + periodic.period);
-
-    // execution #1
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(initTestVal + 2);
-
-    await helper.wait.forParachainBlockNumber(executionBlock + 2 * periodic.period);
-
-    // <canceled>
-    expect(await helper.testUtils.testValue())
-      .to.be.equal(initTestVal + 2);
-  });
-
-  itSub('Root can cancel any scheduled operation', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(bob);
-
-    const scheduledId = helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
-      .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.getSudo().scheduler.cancelScheduled(superuser, scheduledId);
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-
-  itSched('Root can set prioritized scheduled operation', async (scheduleKind, {helper}) => {
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-    const waitForBlocks = 4;
-
-    const amount = 42n * helper.balance.getOneTokenNominal();
-
-    const balanceBefore = await helper.balance.getSubstrate(charlie.address);
-
-    await helper.getSudo()
-      .scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42})
-      .balance.forceTransferToSubstrate(superuser, bob.address, charlie.address, amount);
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const balanceAfter = await helper.balance.getSubstrate(charlie.address);
-
-    expect(balanceAfter > balanceBefore).to.be.true;
-
-    const diff = balanceAfter - balanceBefore;
-    expect(diff).to.be.equal(amount);
-  });
-
-  itSub("Root can change scheduled operation's priority", async ({helper}) => {
-    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(bob);
-
-    const scheduledId = helper.arrange.makeScheduledId();
-    const waitForBlocks = 6;
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
-      .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    const priority = 112;
-    await helper.getSudo().scheduler.changePriority(superuser, scheduledId, priority);
-
-    const priorityChanged = await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged);
-
-    const [blockNumber, index] = priorityChanged.task();
-    expect(blockNumber).to.be.equal(executionBlock);
-    expect(index).to.be.equal(0);
-
-    expect(priorityChanged.priority().toString()).to.be.equal(priority.toString());
-  });
-
-  itSub('Prioritized operations execute in valid order', async ({helper}) => {
-    const [
-      scheduledFirstId,
-      scheduledSecondId,
-    ] = helper.arrange.makeScheduledIds(2);
-
-    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
-    const blocksBeforeExecution = 6;
-    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
-
-    const prioHigh = 0;
-    const prioLow = 255;
-
-    const periodic = {
-      period: 6,
-      repetitions: 2,
-    };
-
-    const amount = 1n * helper.balance.getOneTokenNominal();
-
-    // Scheduler a task with a lower priority first, then with a higher priority
-    await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, {
-      scheduledId: scheduledFirstId,
-      priority: prioLow,
-      periodic,
-    }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);
-
-    await helper.getSudo().scheduler.scheduleAt(firstExecutionBlockNumber, {
-      scheduledId: scheduledSecondId,
-      priority: prioHigh,
-      periodic,
-    }).balance.forceTransferToSubstrate(superuser, alice.address, bob.address, amount);
-
-    const capture = await helper.arrange.captureEvents('scheduler', 'Dispatched');
-
-    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
-
-    // Flip priorities
-    await helper.getSudo().scheduler.changePriority(superuser, scheduledFirstId, prioHigh);
-    await helper.getSudo().scheduler.changePriority(superuser, scheduledSecondId, prioLow);
-
-    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + periodic.period);
-
-    const dispatchEvents = capture.extractCapturedEvents();
-    expect(dispatchEvents.length).to.be.equal(4);
-
-    const dispatchedIds = dispatchEvents.map(r => r.event.data[1].toString());
-
-    const firstExecuctionIds = [dispatchedIds[0], dispatchedIds[1]];
-    const secondExecuctionIds = [dispatchedIds[2], dispatchedIds[3]];
-
-    expect(firstExecuctionIds[0]).to.be.equal(scheduledSecondId);
-    expect(firstExecuctionIds[1]).to.be.equal(scheduledFirstId);
-
-    expect(secondExecuctionIds[0]).to.be.equal(scheduledFirstId);
-    expect(secondExecuctionIds[1]).to.be.equal(scheduledSecondId);
-  });
-
-  itSched('Periodic operations always can be rescheduled', async (scheduleKind, {helper}) => {
-    const maxScheduledPerBlock = 50;
-    const numFilledBlocks = 3;
-    const ids = helper.arrange.makeScheduledIds(numFilledBlocks * maxScheduledPerBlock + 1);
-    const periodicId = scheduleKind == 'named' ? ids[0] : undefined;
-    const fillIds = ids.slice(1);
-
-    const fillScheduleFn = scheduleKind == 'named' ? 'scheduleNamed' : 'schedule';
-
-    const initTestVal = 0;
-    const firstExecTestVal = 1;
-    const secondExecTestVal = 2;
-    await helper.testUtils.setTestValue(alice, initTestVal);
-
-    const currentBlockNumber = await helper.chain.getLatestBlockNumber();
-    const blocksBeforeExecution = 8;
-    const firstExecutionBlockNumber = currentBlockNumber + blocksBeforeExecution;
-
-    const period = 5;
-
-    const periodic = {
-      period,
-      repetitions: 2,
-    };
-
-    // Fill `numFilledBlocks` blocks beginning from the block in which the second execution should occur
-    const txs = [];
-    for(let offset = 0; offset < numFilledBlocks; offset ++) {
-      for(let i = 0; i < maxScheduledPerBlock; i++) {
-
-        const scheduledTx = helper.constructApiCall('api.tx.balances.transfer', [bob.address, 1n]);
-
-        const when = firstExecutionBlockNumber + period + offset;
-        const mandatoryArgs = [when, null, null, scheduledTx];
-        const scheduleArgs = scheduleKind == 'named'
-          ? [fillIds[i + offset * maxScheduledPerBlock], ...mandatoryArgs]
-          : mandatoryArgs;
-
-        const tx = helper.constructApiCall(`api.tx.scheduler.${fillScheduleFn}`, scheduleArgs);
-
-        txs.push(tx);
-      }
-    }
-    await helper.executeExtrinsic(alice, 'api.tx.testUtils.batchAll', [txs], true);
-
-    await helper.scheduler.scheduleAt<DevUniqueHelper>(firstExecutionBlockNumber, {
-      scheduledId: periodicId,
-      periodic,
-    }).testUtils.incTestValue(alice);
-
-    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber);
-    expect(await helper.testUtils.testValue()).to.be.equal(firstExecTestVal);
-
-    await helper.wait.forParachainBlockNumber(firstExecutionBlockNumber + period + numFilledBlocks);
-
-    // The periodic operation should be postponed by `numFilledBlocks`
-    for(let i = 0; i < numFilledBlocks; i++) {
-      expect(await helper.testUtils.testValue(firstExecutionBlockNumber + period + i)).to.be.equal(firstExecTestVal);
-    }
-
-    // After the `numFilledBlocks` the periodic operation will eventually be executed
-    expect(await helper.testUtils.testValue()).to.be.equal(secondExecTestVal);
-  });
-
-  itSched('scheduled operations does not change nonce', async (scheduleKind, {helper}) => {
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-    const blocksBeforeExecution = 4;
-
-    await helper.scheduler
-      .scheduleAfter<DevUniqueHelper>(blocksBeforeExecution, {scheduledId})
-      .balance.transferToSubstrate(alice, bob.address, 1n);
-    const executionBlock = await helper.chain.getLatestBlockNumber() + blocksBeforeExecution + 1;
-
-    const initNonce = await helper.chain.getNonce(alice.address);
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const finalNonce = await helper.chain.getNonce(alice.address);
-
-    expect(initNonce).to.be.equal(finalNonce);
-  });
-});
-
-describe('Negative Test: Scheduling', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.UniqueScheduler]);
-
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-
-      await helper.testUtils.enable(Pallets.TestUtils);
-    });
-  });
-
-  itSub("Can't overwrite a scheduled ID", async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-
-    const scheduledId = helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
-      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId});
-    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, 1n * helper.balance.getOneTokenNominal()))
-      .to.be.rejectedWith(/scheduler\.FailedToSchedule/);
-
-    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-    expect(bobsBalanceBefore).to.be.equal(bobsBalanceAfter);
-  });
-
-  itSub("Can't cancel an operation which is not scheduled", async ({helper}) => {
-    const scheduledId = helper.arrange.makeScheduledId();
-    await expect(helper.scheduler.cancelScheduled(alice, scheduledId))
-      .to.be.rejectedWith(/scheduler\.NotFound/);
-  });
-
-  itSub("Can't cancel a non-owned scheduled operation", async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(alice);
-
-    const scheduledId = helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
-      .nft.transferToken(alice, collection.collectionId, token.tokenId, {Substrate: bob.address});
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await expect(helper.scheduler.cancelScheduled(bob, scheduledId))
-      .to.be.rejectedWith(/BadOrigin/);
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-
-  itSched("Regular user can't set prioritized scheduled operation", async (scheduleKind, {helper}) => {
-    const scheduledId = scheduleKind == 'named' ? helper.arrange.makeScheduledId() : undefined;
-    const waitForBlocks = 4;
-
-    const amount = 42n * helper.balance.getOneTokenNominal();
-
-    const balanceBefore = await helper.balance.getSubstrate(bob.address);
-
-    const scheduled = helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId, priority: 42});
-
-    await expect(scheduled.balance.transferToSubstrate(alice, bob.address, amount))
-      .to.be.rejectedWith(/BadOrigin/);
-
-    const executionBlock = await helper.chain.getLatestBlockNumber() + waitForBlocks + 1;
-
-    await helper.wait.forParachainBlockNumber(executionBlock);
-
-    const balanceAfter = await helper.balance.getSubstrate(bob.address);
-
-    expect(balanceAfter).to.be.equal(balanceBefore);
-  });
-
-  itSub("Regular user can't change scheduled operation's priority", async ({helper}) => {
-    const collection = await helper.nft.mintCollection(bob, {tokenPrefix: 'schd'});
-    const token = await collection.mintToken(bob);
-
-    const scheduledId = helper.arrange.makeScheduledId();
-    const waitForBlocks = 4;
-
-    await helper.scheduler.scheduleAfter(waitForBlocks, {scheduledId})
-      .nft.transferToken(bob, collection.collectionId, token.tokenId, {Substrate: alice.address});
-
-    const priority = 112;
-    await expect(helper.scheduler.changePriority(alice, scheduledId, priority))
-      .to.be.rejectedWith(/BadOrigin/);
-
-    await helper.wait.expectEvent(waitForBlocks, Event.UniqueScheduler.PriorityChanged);
-  });
-});
-
-// Implementation of the functionality tested here was postponed/shelved
-describe.skip('Sponsoring scheduling', () => {
-  // let alice: IKeyringPair;
-  // let bob: IKeyringPair;
-
-  // before(async() => {
-  //   await usingApi(async (_, privateKey) => {
-  //     alice = privateKey('//Alice');
-  //     bob = privateKey('//Bob');
-  //   });
-  // });
-
-  it('Can sponsor scheduling a transaction', async () => {
-    // const collectionId = await createCollectionExpectSuccess();
-    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
-    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
-
-    // await usingApi(async api => {
-    //   const scheduledId = await makeScheduledId();
-    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
-
-    //   const bobBalanceBefore = await getFreeBalance(bob);
-    //   const waitForBlocks = 4;
-    //   // no need to wait to check, fees must be deducted on scheduling, immediately
-    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, bob, 0, waitForBlocks, scheduledId);
-    //   const bobBalanceAfter = await getFreeBalance(bob);
-    //   // expect(aliceBalanceAfter == aliceBalanceBefore).to.be.true;
-    //   expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
-    //   // wait for sequentiality matters
-    //   await waitNewBlocks(waitForBlocks - 1);
-    // });
-  });
-
-  it('Schedules and dispatches a transaction even if the caller has no funds at the time of the dispatch', async () => {
-    // await usingApi(async (api, privateKey) => {
-    //   // Find an empty, unused account
-    //   const zeroBalance = await findUnusedAddress(api, privateKey);
-
-    //   const collectionId = await createCollectionExpectSuccess();
-
-    //   // Add zeroBalance address to allow list
-    //   await enablePublicMintingExpectSuccess(alice, collectionId);
-    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
-
-    //   // Grace zeroBalance with money, enough to cover future transactions
-    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
-    //   await submitTransactionAsync(alice, balanceTx);
-
-    //   // Mint a fresh NFT
-    //   const tokenId = await createItemExpectSuccess(zeroBalance, collectionId, 'NFT');
-    //   const scheduledId = await makeScheduledId();
-
-    //   // Schedule transfer of the NFT a few blocks ahead
-    //   const waitForBlocks = 5;
-    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, zeroBalance, alice, 1, waitForBlocks, scheduledId);
-
-    //   // Get rid of the account's funds before the scheduled transaction takes place
-    //   const balanceTx2 = api.tx.balances.transfer(alice.address, UNIQUE * 68n / 100n);
-    //   const events = await submitTransactionAsync(zeroBalance, balanceTx2);
-    //   expect(getGenericResult(events).success).to.be.true;
-    //   /*const emptyBalanceTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0); // do not null reserved?
-    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceTx as any);
-    //   const events = await submitTransactionAsync(alice, sudoTx);
-    //   expect(getGenericResult(events).success).to.be.true;*/
-
-    //   // Wait for a certain number of blocks, discarding the ones that already happened while accepting the late transactions
-    //   await waitNewBlocks(waitForBlocks - 3);
-
-    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(alice.address));
-    // });
-  });
-
-  it('Sponsor going bankrupt does not impact a scheduled transaction', async () => {
-    // const collectionId = await createCollectionExpectSuccess();
-
-    // await usingApi(async (api, privateKey) => {
-    //   const zeroBalance = await findUnusedAddress(api, privateKey);
-    //   const balanceTx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
-    //   await submitTransactionAsync(alice, balanceTx);
-
-    //   await setCollectionSponsorExpectSuccess(collectionId, zeroBalance.address);
-    //   await confirmSponsorshipByKeyExpectSuccess(collectionId, zeroBalance);
-
-    //   const scheduledId = await makeScheduledId();
-    //   const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
-
-    //   const waitForBlocks = 5;
-    //   await scheduleTransferExpectSuccess(api, collectionId, tokenId, alice, zeroBalance, 1, waitForBlocks, scheduledId);
-
-    //   const emptyBalanceSponsorTx = api.tx.balances.setBalance(zeroBalance.address, 0, 0);
-    //   const sudoTx = api.tx.sudo.sudo(emptyBalanceSponsorTx as any);
-    //   const events = await submitTransactionAsync(alice, sudoTx);
-    //   expect(getGenericResult(events).success).to.be.true;
-
-    //   // Wait for a certain number of blocks, save for the ones that already happened while accepting the late transactions
-    //   await waitNewBlocks(waitForBlocks - 3);
-
-    //   expect(await getTokenOwner(api, collectionId, tokenId)).to.be.deep.equal(normalizeAccountId(zeroBalance.address));
-    // });
-  });
-
-  it('Exceeding sponsor rate limit without having enough funds prevents scheduling a periodic transaction', async () => {
-    // const collectionId = await createCollectionExpectSuccess();
-    // await setCollectionSponsorExpectSuccess(collectionId, bob.address);
-    // await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
-
-    // await usingApi(async (api, privateKey) => {
-    //   const zeroBalance = await findUnusedAddress(api, privateKey);
-
-    //   await enablePublicMintingExpectSuccess(alice, collectionId);
-    //   await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
-
-    //   const bobBalanceBefore = await getFreeBalance(bob);
-
-    //   const createData = {nft: {const_data: [], variable_data: []}};
-    //   const creationTx = api.tx.unique.createItem(collectionId, normalizeAccountId(zeroBalance), createData as any);
-    //   const scheduledId = await makeScheduledId();
-
-    //   /*const badTransaction = async function () {
-    //     await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
-    //   };
-    //   await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');*/
-
-    //   await expect(scheduleAfter(api, creationTx, zeroBalance, 3, scheduledId, 1, 3)).to.be.rejectedWith(/Inability to pay some fees/);
-
-    //   expect(await getFreeBalance(bob)).to.be.equal(bobBalanceBefore);
-    // });
-  });
-});
deletedjs-packages/tests/src/setCollectionLimits.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/setCollectionLimits.test.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-// 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/src/types.js';
-
-const accountTokenOwnershipLimit = 0;
-const sponsoredDataSize = 0;
-const sponsorTransferTimeout = 1;
-const tokenLimit = 10;
-
-describe('setCollectionLimits positive', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
-    });
-  });
-
-  itSub('execute setCollectionLimits with predefined params', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-1', tokenPrefix: 'SCL'});
-
-    await collection.setLimits(
-      alice,
-      {
-        accountTokenOwnershipLimit,
-        sponsoredDataSize,
-        tokenLimit,
-        sponsorTransferTimeout,
-        ownerCanTransfer: true,
-        ownerCanDestroy: true,
-      },
-    );
-
-    // get collection limits defined previously
-    const collectionInfo = await collection.getEffectiveLimits();
-
-    expect(collectionInfo.accountTokenOwnershipLimit).to.be.equal(accountTokenOwnershipLimit);
-    expect(collectionInfo.sponsoredDataSize).to.be.equal(sponsoredDataSize);
-    expect(collectionInfo.tokenLimit).to.be.equal(tokenLimit);
-    expect(collectionInfo.sponsorTransferTimeout).to.be.equal(sponsorTransferTimeout);
-    expect(collectionInfo.ownerCanTransfer).to.be.true;
-    expect(collectionInfo.ownerCanDestroy).to.be.true;
-  });
-
-  itSub('Set the same token limit twice', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-2', tokenPrefix: 'SCL'});
-
-    const collectionLimits = {
-      accountTokenOwnershipLimit,
-      sponsoredDataSize,
-      tokenLimit,
-      sponsorTransferTimeout,
-      ownerCanTransfer: true,
-      ownerCanDestroy: true,
-    };
-
-    await collection.setLimits(alice, collectionLimits);
-
-    const collectionInfo1 = await collection.getEffectiveLimits();
-
-    expect(collectionInfo1.tokenLimit).to.be.equal(tokenLimit);
-
-    await collection.setLimits(alice, collectionLimits);
-    const collectionInfo2 = await collection.getEffectiveLimits();
-    expect(collectionInfo2.tokenLimit).to.be.equal(tokenLimit);
-  });
-
-  itSub('execute setCollectionLimits from admin collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-3', tokenPrefix: 'SCL'});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-
-    const collectionLimits = {
-      accountTokenOwnershipLimit,
-      sponsoredDataSize,
-      // sponsoredMintSize,
-      tokenLimit,
-    };
-
-    await expect(collection.setLimits(alice, collectionLimits)).to.not.be.rejected;
-  });
-});
-
-describe('setCollectionLimits negative', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([20n, 10n], donor);
-    });
-  });
-
-  itSub('execute setCollectionLimits for not exists collection', async ({helper}) => {
-    const nonExistentCollectionId = NON_EXISTENT_COLLECTION_ID;
-    await expect(helper.collection.setLimits(
-      alice,
-      nonExistentCollectionId,
-      {
-        accountTokenOwnershipLimit,
-        sponsoredDataSize,
-        // sponsoredMintSize,
-        tokenLimit,
-      },
-    )).to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('execute setCollectionLimits from user who is not owner of this collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-1', tokenPrefix: 'SCL'});
-
-    await expect(collection.setLimits(bob, {
-      accountTokenOwnershipLimit,
-      sponsoredDataSize,
-      // sponsoredMintSize,
-      tokenLimit,
-    })).to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('fails when trying to enable OwnerCanTransfer after it was disabled', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-2', tokenPrefix: 'SCL'});
-
-    await collection.setLimits(alice, {
-      accountTokenOwnershipLimit,
-      sponsoredDataSize,
-      tokenLimit,
-      sponsorTransferTimeout,
-      ownerCanTransfer: false,
-      ownerCanDestroy: true,
-    });
-
-    await expect(collection.setLimits(alice, {
-      accountTokenOwnershipLimit,
-      sponsoredDataSize,
-      tokenLimit,
-      sponsorTransferTimeout,
-      ownerCanTransfer: true,
-      ownerCanDestroy: true,
-    })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/);
-  });
-
-  itSub('fails when trying to enable OwnerCanDestroy after it was disabled', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-3', tokenPrefix: 'SCL'});
-
-    await collection.setLimits(alice, {
-      accountTokenOwnershipLimit,
-      sponsoredDataSize,
-      tokenLimit,
-      sponsorTransferTimeout,
-      ownerCanTransfer: true,
-      ownerCanDestroy: false,
-    });
-
-    await expect(collection.setLimits(alice, {
-      accountTokenOwnershipLimit,
-      sponsoredDataSize,
-      tokenLimit,
-      sponsorTransferTimeout,
-      ownerCanTransfer: true,
-      ownerCanDestroy: true,
-    })).to.be.rejectedWith(/common\.OwnerPermissionsCantBeReverted/);
-  });
-
-  itSub('Setting the higher token limit fails', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionLimits-Neg-4', tokenPrefix: 'SCL'});
-
-    const collectionLimits = {
-      accountTokenOwnershipLimit: accountTokenOwnershipLimit,
-      sponsoredMintSize: sponsoredDataSize,
-      tokenLimit: tokenLimit,
-      sponsorTransferTimeout,
-      ownerCanTransfer: true,
-      ownerCanDestroy: true,
-    };
-
-    // The first time
-    await collection.setLimits(alice, collectionLimits);
-
-    // The second time - higher token limit
-    collectionLimits.tokenLimit += 1;
-    await expect(collection.setLimits(alice, collectionLimits)).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
-  });
-});
deletedjs-packages/tests/src/setCollectionSponsor.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/setCollectionSponsor.test.ts
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect, Pallets} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('integration test: ext. setCollectionSponsor():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('Set NFT collection sponsor', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-1-NFT', tokenPrefix: 'SCS'});
-    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
-
-    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
-      Unconfirmed: bob.address,
-    });
-  });
-
-  itSub('Set Fungible collection sponsor', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'SetCollectionSponsor-1-FT', tokenPrefix: 'SCS'});
-    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
-
-    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
-      Unconfirmed: bob.address,
-    });
-  });
-
-  itSub.ifWithPallets('Set ReFungible collection sponsor', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'SetCollectionSponsor-1-RFT', tokenPrefix: 'SCS'});
-    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
-
-    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
-      Unconfirmed: bob.address,
-    });
-  });
-
-  itSub('Set the same sponsor repeatedly', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-2', tokenPrefix: 'SCS'});
-    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
-    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
-
-    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
-      Unconfirmed: bob.address,
-    });
-  });
-
-  itSub('Replace collection sponsor', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-3', tokenPrefix: 'SCS'});
-    await expect(collection.setSponsor(alice, bob.address)).to.be.not.rejected;
-    await expect(collection.setSponsor(alice, charlie.address)).to.be.not.rejected;
-
-    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
-      Unconfirmed: charlie.address,
-    });
-  });
-
-  itSub('Collection admin add sponsor', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-4', tokenPrefix: 'SCS'});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await expect(collection.setSponsor(bob, charlie.address)).to.be.not.rejected;
-
-    expect((await collection.getData())?.raw.sponsorship).to.deep.equal({
-      Unconfirmed: charlie.address,
-    });
-  });
-});
-
-describe('(!negative test!) integration test: ext. setCollectionSponsor():', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([10n, 5n], donor);
-    });
-  });
-
-  itSub('(!negative test!) Add sponsor with a non-owner', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-1', tokenPrefix: 'SCS'});
-    await expect(collection.setSponsor(bob, bob.address))
-      .to.be.rejectedWith(/common\.NoPermission/);
-  });
-
-  itSub('(!negative test!) Add sponsor to a collection that never existed', async ({helper}) => {
-    const collectionId = NON_EXISTENT_COLLECTION_ID;
-    await expect(helper.collection.setSponsor(alice, collectionId, bob.address))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('(!negative test!) Add sponsor to a collection that was destroyed', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetCollectionSponsor-Neg-2', tokenPrefix: 'SCS'});
-    await collection.burn(alice);
-    await expect(collection.setSponsor(alice, bob.address))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-});
deletedjs-packages/tests/src/setPermissions.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/setPermissions.test.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('Integration Test: Set Permissions', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
-    });
-  });
-
-  itSub('can all be enabled twice', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-1', tokenPrefix: 'SP'});
-    expect((await collection.getData())?.raw.permissions.access).to.not.equal('AllowList');
-
-    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}});
-    await collection.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]}});
-
-    const permissions = (await collection.getData())?.raw.permissions;
-    expect(permissions).to.be.deep.equal({
-      access: 'AllowList',
-      mintMode: true,
-      nesting: {collectionAdmin: true, tokenOwner: true, restricted: [1, 2]},
-    });
-  });
-
-  itSub('can all be disabled twice', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'});
-    expect((await collection.getData())?.raw.permissions.access).to.equal('Normal');
-
-    await collection.setPermissions(alice, {access: 'AllowList', nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]}});
-    expect((await collection.getData())?.raw.permissions).to.be.deep.equal({
-      access: 'AllowList',
-      mintMode: false,
-      nesting: {collectionAdmin: false, tokenOwner: true, restricted: [1, 2]},
-    });
-
-    await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}});
-    await collection.setPermissions(alice, {access: 'Normal', mintMode: false, nesting: {}});
-    expect((await collection.getData())?.raw.permissions).to.be.deep.equal({
-      access: 'Normal',
-      mintMode: false,
-      nesting: {collectionAdmin: false, tokenOwner: false, restricted: null},
-    });
-  });
-
-  itSub('collection admin can set permissions', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-2', tokenPrefix: 'SP'});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    await collection.setPermissions(bob, {access: 'AllowList', mintMode: true});
-
-    expect((await collection.getData())?.raw.permissions.access).to.equal('AllowList');
-    expect((await collection.getData())?.raw.permissions.mintMode).to.equal(true);
-  });
-});
-
-describe('Negative Integration Test: Set Permissions', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([10n, 10n], donor);
-    });
-  });
-
-  itSub('fails on not existing collection', async ({helper}) => {
-    const collectionId = NON_EXISTENT_COLLECTION_ID;
-    await expect(helper.collection.setPermissions(alice, collectionId, {access: 'AllowList', mintMode: true}))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('fails on removed collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-1', tokenPrefix: 'SP'});
-    await collection.burn(alice);
-
-    await expect(collection.setPermissions(alice, {access: 'AllowList', mintMode: true}))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  itSub('fails when non-owner tries to set permissions', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'SetPermissions-Neg-2', tokenPrefix: 'SP'});
-
-    await expect(collection.setPermissions(bob, {access: 'AllowList', mintMode: true}))
-      .to.be.rejectedWith(/common\.NoPermission/);
-  });
-});
deletedjs-packages/tests/src/sub/appPromotion/appPromotion.seqtest.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/appPromotion/appPromotion.seqtest.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '../../util/index.js';
-import {expect} from '../../eth/util/index.js';
-
-let superuser: IKeyringPair;
-let donor: IKeyringPair;
-let palletAdmin: IKeyringPair;
-
-describe('App promotion', () => {
-  before(async function () {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]);
-      superuser = await privateKey('//Alice');
-      donor = await privateKey({url: import.meta.url});
-      palletAdmin = await privateKey('//PromotionAdmin');
-      const api = helper.getApi();
-      await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));
-    });
-  });
-
-  after(async function () {
-    await usingPlaygrounds(async (helper) => {
-      if(helper.fetchMissingPalletNames([Pallets.AppPromotion]).length != 0) return;
-      const api = helper.getApi();
-      await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));
-    });
-  });
-
-  describe('admin adress', () => {
-    itSub('can be set by sudo only', async ({helper}) => {
-      const api = helper.getApi();
-      const [nonAdmin] = await helper.arrange.createAccounts([10n], donor);
-      // nonAdmin can not set admin not from himself nor as a sudo
-      await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.rejected;
-      await expect(helper.signTransaction(nonAdmin, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.rejected;
-    });
-
-    itSub('can be any valid CrossAccountId', async ({helper}) => {
-      // We are not going to set an eth address as a sponsor,
-      // but we do want to check, it doesn't break anything;
-      const api = helper.getApi();
-      const [account] = await helper.arrange.createAccounts([10n], donor);
-      const ethAccount = helper.address.substrateToEth(account.address);
-      // Alice sets Ethereum address as a sudo. Then Substrate address back...
-      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.fulfilled;
-      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled;
-
-      // ...It doesn't break anything;
-      const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
-      await expect(helper.signTransaction(account, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
-    });
-
-    itSub('can be reassigned', async ({helper}) => {
-      const api = helper.getApi();
-      const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
-      const collection  = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
-
-      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: oldAdmin.address})))).to.be.fulfilled;
-      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: newAdmin.address})))).to.be.fulfilled;
-      await expect(helper.signTransaction(oldAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
-
-      await expect(helper.signTransaction(newAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled;
-    });
-  });
-});
-
deletedjs-packages/tests/src/sub/appPromotion/appPromotion.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/appPromotion/appPromotion.test.ts
+++ /dev/null
@@ -1,1008 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {
-  itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip, LOCKING_PERIOD, UNLOCKING_PERIOD,
-} from '../../util/index.js';
-import {DevUniqueHelper} from '@unique/playgrounds/src/unique.dev.js';
-import {itEth, expect, SponsoringMode} from '../../eth/util/index.js';
-
-let donor: IKeyringPair;
-let palletAdmin: IKeyringPair;
-let nominal: bigint;
-let palletAddress: string;
-let accounts: IKeyringPair[];
-let usedAccounts: IKeyringPair[] = [];
-
-async function getAccounts(accountsNumber: number, balance?: bigint) {
-  let accs: IKeyringPair[] = [];
-  if(balance) {
-    await usingPlaygrounds(async (helper) => {
-      accs = await helper.arrange.createAccounts(new Array(accountsNumber).fill(balance), donor);
-    });
-  } else {
-    accs = accounts.splice(0, accountsNumber);
-  }
-  usedAccounts.push(...accs);
-  return accs;
-}
-// App promotion periods:
-// LOCKING_PERIOD = 12 blocks of relay
-// UNLOCKING_PERIOD = 6 blocks of parachain
-
-describe('App promotion', () => {
-  before(async function () {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]);
-      donor = await privateKey({url: import.meta.url});
-      palletAddress = helper.arrange.calculatePalletAddress('appstake');
-      palletAdmin = await privateKey('//PromotionAdmin');
-
-      nominal = helper.balance.getOneTokenNominal();
-
-      const accountBalances = new Array(200).fill(1000n);
-      accounts = await helper.arrange.createAccounts(accountBalances, donor); // create accounts-pool to speed up tests
-    });
-  });
-
-  afterEach(async () => {
-    await usingPlaygrounds(async (helper) => {
-      let unstakeTxs = [];
-      for(const account of usedAccounts) {
-        if(unstakeTxs.length === 3) {
-          await Promise.all(unstakeTxs);
-          unstakeTxs = [];
-        }
-        unstakeTxs.push(helper.staking.unstakeAll(account));
-      }
-      await Promise.all(unstakeTxs);
-      usedAccounts = [];
-      expect(await helper.staking.getTotalStaked()).to.eq(0n); // there are no active stakes after each test
-      // Make sure previousCalculatedRecord is None to avoid problem with payout stakers;
-      await helper.admin.payoutStakers(palletAdmin, 100);
-      expect((await helper.getApi().query.appPromotion.previousCalculatedRecord() as any).isNone).to.be.true;
-    });
-  });
-
-  describe('stake extrinsic', () => {
-    itSub('should "freeze" staking balance, add it to "staked" map, and increase "totalStaked" amount', async ({helper}) => {
-      const [staker, recepient] = await getAccounts(2);
-      const totalStakedBefore = await helper.staking.getTotalStaked();
-
-      // Minimum stake amount is 100:
-      await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.rejected;
-      await helper.staking.stake(staker, 100n * nominal);
-
-      // Staker balance is: frozen: 100, reserved: 0n...
-      // ...so he can not transfer 900
-      expect(await helper.balance.getSubstrateFull(staker.address)).to.contain({frozen: 100n * nominal, reserved: 0n});
-      expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 100n * nominal}]);
-      await expect(helper.balance.transferToSubstrate(staker, recepient.address, 900n * nominal)).to.be.rejectedWith(/^Token: Frozen$/);
-
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(100n * nominal);
-      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
-      // it is potentially flaky test. Promotion can credited some tokens. Maybe we need to use closeTo?
-      expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + 100n * nominal); // total tokens amount staked in app-promotion increased
-
-      await helper.staking.stake(staker, 200n * nominal);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(300n * nominal);
-      const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      expect(totalStakedPerBlock[0].amount).to.equal(100n * nominal);
-      expect(totalStakedPerBlock[1].amount).to.equal(200n * nominal);
-    });
-
-    [
-      {unstake: 'unstakeAll' as const},
-      {unstake: 'unstakePartial' as const},
-    ].map(testCase => {
-      itSub(`[${testCase.unstake}] should allow to create maximum 10 stakes for account`, async ({helper}) => {
-        const [staker] = await getAccounts(1, 2000n);
-        const ONE_STAKE = 100n * nominal;
-        for(let i = 0; i < 10; i++) {
-          await helper.staking.stake(staker, ONE_STAKE);
-        }
-
-        // can have 10 stakes
-        expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(1000n * nominal);
-        expect(await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).to.have.length(10);
-
-        await expect(helper.staking.stake(staker, ONE_STAKE)).to.be.rejectedWith('appPromotion.NoPermission');
-
-        // After unstake can stake again
-
-        // CASE 1: unstakeAll
-        if(testCase.unstake === 'unstakeAll') {
-          await helper.staking.unstakeAll(staker);
-          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
-          await helper.staking.stake(staker, 100n * nominal);
-          expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(100n * nominal);
-        }
-        // CASE 2: unstakePartial
-        else {
-          await helper.staking.unstakePartial(staker, ONE_STAKE);
-          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9);
-          await helper.staking.stake(staker, 100n * nominal);
-          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(10);
-          await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.NoPermission');
-          await helper.staking.unstakePartial(staker, 150n * nominal);
-          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9);
-          expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(850n * nominal);
-        }
-      });
-    });
-    // TODO: Now AppPromo makes freezes. Probably this test should be changed\removed.
-    itSub.skip('should allow to stake() if balance is locked with different id', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-
-      // staker has tokens locked with vesting id:
-      await helper.balance.vestedTransfer(donor, staker.address, {start: 0n, period: 1n, periodCount: 1n, perPeriod: 200n * nominal});
-      expect(await helper.balance.getSubstrateFull(staker.address))
-        .to.deep.contain({free: 1200n * nominal, frozen: 200n * nominal, reserved: 0n});
-
-      // Locked balance can be staked. staker can stake 1200 tokens (minus fee):
-      await helper.staking.stake(staker, 1000n * nominal);
-      await helper.staking.stake(staker, 199n * nominal);
-      // check balances
-      expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]);
-      expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 1199n * nominal}]);
-      expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 1199n * nominal});
-      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(1199n * nominal);
-
-      // staker can unstake
-      await helper.staking.unstakeAll(staker);
-      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1199n * nominal);
-      const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
-      await helper.wait.forParachainBlockNumber(pendingUnstake.block);
-
-      // check balances
-      expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]);
-      expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 200n * nominal});
-      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n);
-
-      // staker can transfer balances now
-      await helper.balance.transferToSubstrate(staker, donor.address, 900n * nominal);
-    });
-
-    itSub('should not allow to stake(), if stake amount is more than total free balance minus locked by staking', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-
-      // Can't stake full balance because Alice needs to pay some fee
-      await expect(helper.staking.stake(staker, 1000n * nominal)).to.be.rejected; // With('Arithmetic')
-      await helper.staking.stake(staker, 500n * nominal);
-
-      // Can't stake 500 tkn because Alice has Less than 500 transferable;
-      await expect(helper.staking.stake(staker, 500n * nominal)).to.be.rejected; // With('Arithmetic');
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(500n * nominal);
-    });
-
-    itSub('for different accounts in one block is possible', async ({helper}) => {
-      const crowd = await getAccounts(4);
-
-      const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal));
-      await expect(Promise.all(crowdStartsToStake)).to.be.fulfilled;
-
-      const crowdStakes = await Promise.all(crowd.map(address => helper.staking.getTotalStaked({Substrate: address.address})));
-      expect(crowdStakes).to.deep.equal([100n * nominal, 100n * nominal, 100n * nominal, 100n * nominal]);
-    });
-  });
-
-  describe('Unstaking', () => {
-    [
-      {method: 'unstakeAll' as const},
-      {method: 'unstakePartial' as const},
-    ].map(testCase => {
-      itSub(`[${testCase.method}] should move tokens to "pendingUnstake" and subtract it from totalStaked`, async ({helper}) => {
-        const [staker, recepient] = await getAccounts(2);
-        const totalStakedBefore = await helper.staking.getTotalStaked();
-        const STAKE_AMOUNT = 900n * nominal;
-
-        await helper.staking.stake(staker, STAKE_AMOUNT);
-        testCase.method === 'unstakeAll'
-          ? await helper.staking.unstakeAll(staker)
-          : await helper.staking.unstakePartial(staker, STAKE_AMOUNT);
-
-        // Right after unstake tokens are still locked
-        expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
-        expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: STAKE_AMOUNT}]);
-        expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: STAKE_AMOUNT});
-        // Staker can not transfer
-        await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejectedWith(/^Token: Frozen$/);
-        expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(STAKE_AMOUNT);
-        expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
-        expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore);
-      });
-    });
-
-    [
-      {method: 'unstakeAll' as const},
-      {method: 'unstakePartial' as const},
-    ].map(testCase => {
-      itSub(`[${testCase.method}] should unlock balance after unlocking period ends and remove it from "pendingUnstake"`, async ({helper}) => {
-        const [staker] = await getAccounts(1);
-        await helper.staking.stake(staker, 100n * nominal);
-        testCase.method === 'unstakeAll'
-          ? await helper.staking.unstakeAll(staker)
-          : await helper.staking.unstakePartial(staker, 100n * nominal);
-        const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
-
-        // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n
-        await helper.wait.forParachainBlockNumber(pendingUnstake.block);
-        expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n});
-        expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
-
-        // staker can transfer:
-        await helper.balance.transferToSubstrate(staker, donor.address, 998n * nominal);
-        expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n);
-      });
-    });
-
-    [
-      {method: 'unstakeAll' as const},
-      {method: 'unstakePartial' as const},
-    ].map(testCase => {
-      itSub(`[${testCase.method}] should successfully unstake multiple stakes`, async ({helper}) => {
-        const [staker] = await getAccounts(1);
-        await helper.staking.stake(staker, 100n * nominal);
-        await helper.staking.stake(staker, 200n * nominal);
-        await helper.staking.stake(staker, 300n * nominal);
-
-        // staked: [100, 200, 300]; unstaked: 0
-        let totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
-        let pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
-        let stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-        expect(totalPendingUnstake).to.be.deep.equal(0n);
-        expect(pendingUnstake).to.be.deep.equal([]);
-        expect(stakes[0].amount).to.equal(100n * nominal);
-        expect(stakes[1].amount).to.equal(200n * nominal);
-        expect(stakes[2].amount).to.equal(300n * nominal);
-
-        // Can unstake multiple stakes
-        testCase.method === 'unstakeAll'
-          ? await helper.staking.unstakeAll(staker)
-          : await helper.staking.unstakePartial(staker, 600n * nominal);
-
-        pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
-        totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
-        stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-        expect(totalPendingUnstake).to.be.equal(600n * nominal);
-        expect(stakes).to.be.deep.equal([]);
-        expect(pendingUnstake[0].amount).to.equal(600n * nominal);
-
-        expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 600n * nominal});
-        expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
-        await helper.wait.forParachainBlockNumber(pendingUnstake[0].block);
-        expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n});
-        expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
-      });
-    });
-
-    [
-      {method: 'unstakeAll' as const},
-      {method: 'unstakePartial' as const},
-    ].map(testCase => {
-      itSub(`[${testCase.method}] should not have any effects if no active stakes`, async ({helper}) => {
-        const [staker] = await getAccounts(1);
-
-        // unstake has no effect if no stakes at all
-        testCase.method === 'unstakeAll'
-          ? await helper.staking.unstakeAll(staker)
-          : await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
-
-        expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n);
-        expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper
-
-        // TODO stake() unstake() waitUnstaked() unstake();
-
-        // can't unstake if there are only pendingUnstakes
-        await helper.staking.stake(staker, 100n * nominal);
-
-        if(testCase.method === 'unstakeAll') {
-          await helper.staking.unstakeAll(staker);
-          await helper.staking.unstakeAll(staker);
-        } else {
-          await helper.staking.unstakePartial(staker, 100n * nominal);
-          await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
-        }
-
-        expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
-        expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
-        expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
-      });
-    });
-
-    [
-      {method: 'unstakeAll' as const},
-      {method: 'unstakePartial' as const},
-    ].map(testCase => {
-      itSub(`[${testCase.method}] should create different pending-unlock for each unlocking stake`, async ({helper}) => {
-        const [staker] = await getAccounts(1);
-        await helper.staking.stake(staker, 100n * nominal);
-        testCase.method === 'unstakeAll'
-          ? await helper.staking.unstakeAll(staker)
-          : await helper.staking.unstakePartial(staker, 100n * nominal);
-        await helper.staking.stake(staker, 120n * nominal);
-        testCase.method === 'unstakeAll'
-          ? await helper.staking.unstakeAll(staker)
-          : await helper.staking.unstakePartial(staker, 120n * nominal);
-
-        const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
-        expect(unstakingPerBlock).has.length(2);
-        expect(unstakingPerBlock[0].amount).to.equal(100n * nominal);
-        expect(unstakingPerBlock[1].amount).to.equal(120n * nominal);
-        expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.equal(0);
-      });
-    });
-
-    [
-      {method: 'unstakeAll' as const},
-      {method: 'unstakePartial' as const},
-    ].map(testCase => {
-      itSub(`[${testCase.method}] should be possible for 3 accounts in one block`, async ({helper}) => {
-        const stakers = await getAccounts(3);
-
-        await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
-        await Promise.all(stakers.map(staker => testCase.method === 'unstakeAll'
-          ? helper.staking.unstakeAll(staker)
-          : helper.staking.unstakePartial(staker, 100n * nominal)));
-
-        await Promise.all(stakers.map(async (staker) => {
-          expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
-          expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
-        }));
-      });
-    });
-
-    itSub('should not be possible for more than 3 accounts in one block', async ({helper}) => {
-      if(!await helper.arrange.isDevNode()) {
-        const stakers = await getAccounts(10);
-
-        await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
-        const unstakingResults = await Promise.allSettled(stakers.map((staker, i) => i % 2 === 0
-          ? helper.staking.unstakeAll(staker)
-          : helper.staking.unstakePartial(staker, 100n * nominal)));
-
-        const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled');
-        expect(successfulUnstakes).to.have.length(3);
-      }
-    });
-
-    itSub('Cannot partially unstake more than staked', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-      // Staker stakes 300:
-      await helper.staking.stake(staker, 100n * nominal);
-      await helper.staking.stake(staker, 200n * nominal);
-
-      // cannot usntake 300.00000...1
-      await expect(helper.staking.unstakePartial(staker, 300n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(2);
-
-      await helper.staking.unstakePartial(staker, 150n * nominal);
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1);
-      await expect(helper.staking.unstakePartial(staker, 150n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1);
-
-      // nothing broken, can unstake full amount:
-      await helper.staking.unstakePartial(staker, 150n * nominal);
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(0);
-    });
-
-    itSub('Can partially unstake arbitrary amount', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-      await helper.staking.stake(staker, 100n * nominal);
-      await helper.staking.stake(staker, 200n * nominal);
-
-      // 0. Staker cannot unstake negative amount
-      await expect(helper.staking.unstakePartial(staker, -1n)).to.be.rejected;
-
-      // 1. Staker can unstake 0 wei
-      await helper.staking.unstakePartial(staker, 0n);
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal);
-      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n);
-
-      // 2. Staker can unstake 1 wei
-      await helper.staking.unstakePartial(staker, 1n);
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal - 1n);
-      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1n);
-      // 2.1 The oldest stake decreased:
-      let [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      expect(stake1.amount).to.eq(100n * nominal - 1n);
-      expect(stake2.amount).to.eq(200n * nominal);
-
-      // 3. Staker can unstake all but 1 wei
-      await helper.staking.unstakePartial(staker, 100n * nominal - 2n);
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(200n * nominal + 1n);
-      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(100n * nominal - 1n);
-      [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      expect(stake1.amount).to.eq(1n);
-      expect(stake2.amount).to.eq(200n * nominal);
-    });
-
-    itSub('can mix different type of unstakes', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-      await helper.staking.stake(staker, 100n * nominal);
-      await helper.staking.stake(staker, 200n * nominal);
-
-      await helper.staking.unstakePartial(staker, 50n * nominal);
-      await helper.staking.unstakeAll(staker);
-      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n);
-      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(300n * nominal);
-
-      const [_unstake1, unstake2] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
-      await helper.wait.forParachainBlockNumber(unstake2.block);
-
-      expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([]);
-      expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n});
-      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(999n);
-      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n);
-      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n);
-      expect(await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).to.deep.eq([]);
-    });
-  });
-
-  describe('collection sponsoring', () => {
-    itSub('should actually sponsor transactions', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner, tokenSender, receiver] = await getAccounts(3);
-      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'Name', description: 'Description', tokenPrefix: 'Prefix', limits: {sponsorTransferTimeout: 0}});
-      const token = await collection.mintToken(collectionOwner, {Substrate: tokenSender.address});
-      await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId));
-      const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress);
-
-      await token.transfer(tokenSender, {Substrate: receiver.address});
-      expect (await token.getOwner()).to.be.deep.equal({Substrate: receiver.address});
-      const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress);
-
-      // senders balance the same, transaction has sponsored
-      expect (await helper.balance.getSubstrate(tokenSender.address)).to.be.equal(1000n * nominal);
-      expect (palletBalanceBefore > palletBalanceAfter).to.be.true;
-    });
-
-    itSub('can not be set by non admin', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner, nonAdmin] = await getAccounts(2);
-
-      const collection  = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
-
-      await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
-      expect((await collection.getData())?.raw.sponsorship).to.equal('Disabled');
-    });
-
-    itSub('should set pallet address as confirmed admin', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner, oldSponsor] = await getAccounts(2);
-
-      // Can set sponsoring for collection without sponsor
-      const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'});
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithoutSponsor.collectionId))).to.be.fulfilled;
-      expect((await collectionWithoutSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
-
-      // Can set sponsoring for collection with unconfirmed sponsor
-      const collectionWithUnconfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Unconfirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}});
-      expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: oldSponsor.address});
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithUnconfirmedSponsor.collectionId))).to.be.fulfilled;
-      expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
-
-      // Can set sponsoring for collection with confirmed sponsor
-      const collectionWithConfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Confirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}});
-      await collectionWithConfirmedSponsor.confirmSponsorship(oldSponsor);
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithConfirmedSponsor.collectionId))).to.be.fulfilled;
-      expect((await collectionWithConfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
-    });
-
-    itSub('can be overwritten by collection owner', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner, newSponsor] = await getAccounts(2);
-      const collection  = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
-      const collectionId = collection.collectionId;
-
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionId))).to.be.fulfilled;
-
-      // Collection limits still can be changed by the owner
-      expect(await collection.setLimits(collectionOwner, {sponsorTransferTimeout: 0})).to.be.true;
-      expect((await collection.getData())?.raw.limits.sponsorTransferTimeout).to.be.equal(0);
-      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
-
-      // Collection sponsor can be changed too
-      expect((await collection.setSponsor(collectionOwner, newSponsor.address))).to.be.true;
-      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: newSponsor.address});
-    });
-
-    itSub('should not overwrite collection limits set by the owner earlier', async ({helper}) => {
-      const [owner] = await getAccounts(1);
-      const api = helper.getApi();
-      const limits = {ownerCanDestroy: true, ownerCanTransfer: true, sponsorTransferTimeout: 0};
-      const collectionWithLimits = await helper.nft.mintCollection(owner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits});
-
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithLimits.collectionId))).to.be.fulfilled;
-      expect((await collectionWithLimits.getData())?.raw.limits).to.be.deep.contain(limits);
-    });
-
-    itSub('should reject transaction if collection doesn\'t exist', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner] = await getAccounts(1);
-
-      // collection has never existed
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(999999999))).to.be.rejected;
-      // collection has been burned
-      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
-      await collection.burn(collectionOwner);
-
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
-    });
-  });
-
-  describe('stopSponsoringCollection', () => {
-    itSub('can not be called by non-admin', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner, nonAdmin] = await getAccounts(2);
-      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
-
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled;
-
-      await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected;
-      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
-    });
-
-    itSub('should set sponsoring as disabled', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner, recepient] = await getAccounts(2);
-      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits: {sponsorTransferTimeout: 0}});
-      const token = await collection.mintToken(collectionOwner, {Substrate: collectionOwner.address});
-
-      await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId));
-      await helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId));
-
-      expect((await collection.getData())?.raw.sponsorship).to.be.equal('Disabled');
-
-      // Transactions are not sponsored anymore:
-      const ownerBalanceBefore = await helper.balance.getSubstrate(collectionOwner.address);
-      await token.transfer(collectionOwner, {Substrate: recepient.address});
-      const ownerBalanceAfter = await helper.balance.getSubstrate(collectionOwner.address);
-      expect(ownerBalanceAfter < ownerBalanceBefore).to.be.equal(true);
-    });
-
-    itSub('should not affect collection which is not sponsored by pallete', async ({helper}) => {
-      const api = helper.getApi();
-      const [collectionOwner] = await getAccounts(1);
-      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: collectionOwner.address}});
-      await collection.confirmSponsorship(collectionOwner);
-
-      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected;
-
-      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: collectionOwner.address});
-    });
-
-    itSub('should reject transaction if collection does not exist', async ({helper}) => {
-      const [collectionOwner] = await getAccounts(1);
-      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
-
-      await collection.burn(collectionOwner);
-      await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [collection.collectionId], true)).to.be.rejectedWith('common.CollectionNotFound');
-      await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [999_999_999], true)).to.be.rejectedWith('common.CollectionNotFound');
-    });
-  });
-
-  describe('contract sponsoring', () => {
-    itEth('should set palletes address as a sponsor', async ({helper}) => {
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
-      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
-
-      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]);
-
-      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
-      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
-        confirmed: {
-          substrate: palletAddress,
-        },
-      });
-    });
-
-    itEth('should overwrite sponsoring mode and existed sponsor', async ({helper}) => {
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
-      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
-
-      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled;
-
-      // Contract is self sponsored
-      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.be.deep.equal({
-        confirmed: {
-          ethereum: flipper.options.address.toLowerCase(),
-        },
-      });
-
-      // set promotion sponsoring
-      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
-
-      // new sponsor is pallet address
-      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
-      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
-        confirmed: {
-          substrate: palletAddress,
-        },
-      });
-    });
-
-    itEth('can be overwritten by contract owner', async ({helper}) => {
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
-      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
-
-      // contract sponsored by pallet
-      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
-
-      // owner sets self sponsoring
-      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected;
-
-      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true;
-      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
-      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
-        confirmed: {
-          ethereum: flipper.options.address.toLowerCase(),
-        },
-      });
-    });
-
-    itEth('can not be set by non admin', async ({helper}) => {
-      const [nonAdmin] = await getAccounts(1);
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
-      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
-
-      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled;
-
-      // nonAdmin calls sponsorContract
-      await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission');
-
-      // contract still self-sponsored
-      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
-        confirmed: {
-          ethereum: flipper.options.address.toLowerCase(),
-        },
-      });
-    });
-
-    itEth('should actually sponsor transactions', async ({helper}) => {
-      // Contract caller
-      const caller = await helper.eth.createAccountWithBalance(donor, 1000n);
-      const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress);
-
-      // Deploy flipper
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
-      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
-
-      // Owner sets to sponsor every tx
-      await contractHelper.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: contractOwner});
-      await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner});
-      await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address, 1000n); // transferBalanceToEth(api, alice, flipper.options.address, 1000n);
-
-      // Set promotion to the Flipper
-      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
-
-      // Caller calls Flipper
-      await flipper.methods.flip().send({from: caller});
-      expect(await flipper.methods.getValue().call()).to.be.true;
-
-      // The contracts and caller balances have not changed
-      const callerBalance = await helper.balance.getEthereum(caller);
-      const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address);
-      expect(callerBalance).to.be.equal(1000n * nominal);
-      expect(1000n * nominal === contractBalanceAfter).to.be.true;
-
-      // The pallet balance has decreased
-      const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress);
-      expect(palletBalanceAfter < palletBalanceBefore).to.be.true;
-    });
-  });
-
-  describe('stopSponsoringContract', () => {
-    itEth('should remove pallet address from contract sponsors', async ({helper}) => {
-      const caller = await helper.eth.createAccountWithBalance(donor, 1000n);
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner);
-      await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
-      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
-
-      await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner});
-      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
-      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true);
-
-      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.false;
-      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
-      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
-        disabled: null,
-      });
-
-      await flipper.methods.flip().send({from: caller});
-      expect(await flipper.methods.getValue().call()).to.be.true;
-
-      const callerBalance = await helper.balance.getEthereum(caller);
-      const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address);
-
-      // caller payed for call
-      expect(1000n * nominal > callerBalance).to.be.true;
-      expect(contractBalanceAfter).to.be.equal(100n * nominal);
-    });
-
-    itEth('can not be called by non-admin', async ({helper}) => {
-      const [nonAdmin] = await getAccounts(1);
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner);
-
-      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]);
-      await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address]))
-        .to.be.rejectedWith(/appPromotion\.NoPermission/);
-    });
-
-    itEth('should not affect a contract which is not sponsored by pallete', async ({helper}) => {
-      const [nonAdmin] = await getAccounts(1);
-      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
-      const flipper = await helper.eth.deployFlipper(contractOwner);
-      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
-      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled;
-
-      await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission');
-    });
-  });
-
-  describe('payoutStakers', () => {
-    itSub('can not be called by non admin', async ({helper}) => {
-      const [nonAdmin] = await getAccounts(1);
-      await expect(helper.admin.payoutStakers(nonAdmin, 100)).to.be.rejectedWith('appPromotion.NoPermission');
-    });
-
-    itSub('should increase total staked', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-      const totalStakedBefore = await helper.staking.getTotalStaked();
-      await helper.staking.stake(staker, 100n * nominal);
-
-      // Wait for rewards and pay
-      const [stakedInBlock] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock.block));
-
-      const payout = await helper.admin.payoutStakers(palletAdmin, 100);
-      const totalPayout = payout.reduce((prev, payout) => prev + payout.payout, 0n);
-      const stakerReward = payout.find(p => p.staker === staker.address);
-
-      expect(stakerReward?.payout).to.eq(calculateIncome(100n * nominal) - (100n * nominal));
-
-      const totalStakedAfter = await helper.staking.getTotalStaked();
-      expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout);
-      // staker can unstake
-      await helper.staking.unstakeAll(staker);
-      expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal));
-    });
-
-    itSub('should credit 0.05% for staking period', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-
-      await waitPromotionPeriodDoesntEnd(helper);
-
-      await helper.staking.stake(staker, 100n * nominal);
-      await helper.staking.stake(staker, 200n * nominal);
-
-      // wait rewards are available:
-      const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));
-
-      const payoutToStaker = (await helper.admin.payoutStakers(palletAdmin, 100)).find((payout) => payout.staker === staker.address)!.payout;
-      expect(payoutToStaker + 300n * nominal).to.equal(calculateIncome(300n * nominal));
-
-      const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      const income1 = calculateIncome(100n * nominal);
-      const income2 = calculateIncome(200n * nominal);
-      expect(totalStakedPerBlock[0].amount).to.equal(income1);
-      expect(totalStakedPerBlock[1].amount).to.equal(income2);
-
-      const stakerBalance = await helper.balance.getSubstrateFull(staker.address);
-      expect(stakerBalance).to.contain({frozen: income1 + income2, reserved: 0n});
-      expect(stakerBalance.free / nominal).to.eq(999n);
-    });
-
-    itSub('shoud be paid for more than one period if payments was missed', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-
-      await helper.staking.stake(staker, 100n * nominal);
-      // wait for two rewards are available:
-      let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
-
-      await helper.admin.payoutStakers(palletAdmin, 100);
-      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      const frozenBalanceShouldBe = calculateIncome(100n * nominal, 2);
-      expect(stake.amount).to.be.equal(frozenBalanceShouldBe);
-
-      const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address);
-
-      expect(stakerFullBalance).to.contain({reserved: 0n, frozen: frozenBalanceShouldBe});
-    });
-
-    itSub('should not be credited for pending-unstaked tokens', async ({helper}) => {
-      // staker unstakes before rewards been payed
-      const [staker] = await getAccounts(1);
-      await helper.staking.stake(staker, 100n * nominal);
-      const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
-      await helper.staking.unstakeAll(staker);
-
-      // so he did not receive any rewards
-      const totalBalanceBefore = await helper.balance.getSubstrate(staker.address);
-      await helper.admin.payoutStakers(palletAdmin, 100);
-      const totalBalanceAfter = await helper.balance.getSubstrate(staker.address);
-
-      expect(totalBalanceBefore).to.be.equal(totalBalanceAfter);
-    });
-
-    itSub('should bring compound interest', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-
-      await helper.staking.stake(staker, 100n * nominal);
-
-      let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block));
-
-      await helper.admin.payoutStakers(palletAdmin, 100);
-      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      expect(stake.amount).to.equal(calculateIncome(100n * nominal));
-
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
-      await helper.admin.payoutStakers(palletAdmin, 100);
-      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      expect(stake.amount).to.equal(calculateIncome(100n * nominal, 2));
-    });
-
-    itSub('can calculate reward for tiny stake', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-      await helper.staking.stake(staker, 100n * nominal);
-      await helper.staking.stake(staker, 100n * nominal);
-      await helper.staking.unstakePartial(staker, 100n * nominal - 1n);
-
-      const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));
-
-      const stakerPayout = await payUntilRewardFor(staker.address, helper);
-      expect(stakerPayout.stake).to.eq(100n * nominal + 1n);
-    });
-
-    itSub('can eventually pay all rewards', async ({helper}) => {
-      const stakers = await getAccounts(30);
-      // Create 30 stakes:
-      await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
-
-      let unstakingTxs = [];
-      for(const staker of stakers) {
-        if(unstakingTxs.length == 3) {
-          await Promise.all(unstakingTxs);
-          unstakingTxs = [];
-        }
-        unstakingTxs.push(helper.staking.unstakePartial(staker, 100n * nominal - 1n));
-      }
-
-      const [staker] = await getAccounts(1);
-      await helper.staking.stake(staker, 100n * nominal);
-      const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block));
-
-      let payouts;
-      do {
-        payouts = await helper.admin.payoutStakers(palletAdmin, 20);
-      } while(payouts.length !== 0);
-    });
-  });
-
-  describe('events', () => {
-    [
-      {method: 'unstakePartial' as const},
-      {method: 'unstakeAll' as const},
-    ].map(testCase => {
-      itSub(testCase.method, async ({helper}) => {
-        const unstakeParams: [] | [bigint] = testCase.method === 'unstakePartial'
-          ? [100n * nominal - 1n]
-          : [];
-        const [staker] = await getAccounts(1);
-        await helper.staking.stake(staker, 100n * nominal);
-        await helper.staking.stake(staker, 200n * nominal);
-        const {result} = await helper.executeExtrinsic(staker, `api.tx.appPromotion.${testCase.method}`, unstakeParams);
-
-        const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Unstake');
-        const unstakerEvents = event?.event.data[0].toString();
-        const unstakedEvents = BigInt(event?.event.data[1].toString());
-        expect(unstakerEvents).to.eq(staker.address);
-        expect(unstakedEvents).to.eq(testCase.method === 'unstakeAll' ? 300n * nominal : 100n * nominal - 1n);
-      });
-    });
-
-    itSub('stake', async ({helper}) => {
-      const [staker] = await getAccounts(1);
-      const {result} = await helper.executeExtrinsic(staker, 'api.tx.appPromotion.stake', [100n * nominal]);
-
-      const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Stake');
-      const stakerEvents = event?.event.data[0].toString();
-      const stakedEvents = BigInt(event?.event.data[1].toString());
-      expect(stakerEvents).to.eq(staker.address);
-      expect(stakedEvents).to.eq(100n * nominal);
-    });
-
-    // Flaky
-    itSub.skip('payoutStakers', async ({helper}) => {
-      const [staker1, staker2] = await getAccounts(2);
-      const STAKE1 = 100n * nominal;
-      const STAKE2 = 200n * nominal;
-      await helper.staking.stake(staker1, STAKE1);
-      await helper.staking.stake(staker2, STAKE2);
-
-      const [stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker2.address});
-      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));
-
-      const results = await helper.admin.payoutStakers(palletAdmin, 100);
-      const stakersEvents = results.filter(ev => ev.staker === staker1.address || ev.staker === staker2.address);
-      expect(stakersEvents).has.length(2);
-      expect(stakersEvents).has.not.ordered.members([
-        {staker: staker1.address, stake: STAKE1, payout: calculateIncome(STAKE1) - STAKE1},
-        {staker: staker2.address, stake: STAKE2, payout: calculateIncome(STAKE2) - STAKE2},
-      ]);
-    });
-  });
-});
-
-
-// Sometimes is is required to make a cycle in order for the payment to be calculated for a specific account
-async function payUntilRewardFor(account: string, helper: DevUniqueHelper) {
-  for(let i = 0; i < 3; i++) {
-    const payouts = await helper.admin.payoutStakers(palletAdmin, 100);
-    const accountPayout = payouts.find(p => p.staker === account);
-    if(accountPayout) return accountPayout;
-  }
-  throw Error(`Cannot find payout for ${account}`);
-}
-
-function calculateIncome(base: bigint, iter = 0, calcPeriod: bigint = UNLOCKING_PERIOD): bigint {
-  const DAY = 7200n;
-  const ACCURACY = 1_000_000_000n;
-  // 5n / 10_000n = 0.05% p/day
-  const income = base + base * (ACCURACY * (calcPeriod * 5n) / (10_000n * DAY)) / ACCURACY ;
-
-  if(iter > 1) {
-    return calculateIncome(income, iter - 1, calcPeriod);
-  } else return income;
-}
-
-function rewardAvailableInBlock(stakedInBlock: bigint) {
-  if(stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + LOCKING_PERIOD;
-  return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n);
-}
-
-// Wait while promotion period less than specified block, to avoid boundary cases
-// 0 if this should be the beginning of the period.
-async function waitPromotionPeriodDoesntEnd(helper: DevUniqueHelper, waitBlockLessThan = LOCKING_PERIOD / 3n) {
-  const relayBlockNumber = (await helper.callRpc('api.query.parachainSystem.validationData', [])).value.relayParentNumber.toNumber(); // await helper.chain.getLatestBlockNumber();
-  const currentPeriodBlock = BigInt(relayBlockNumber) % LOCKING_PERIOD;
-
-  if(currentPeriodBlock > waitBlockLessThan) {
-    await helper.wait.forRelayBlockNumber(BigInt(relayBlockNumber) + LOCKING_PERIOD - currentPeriodBlock);
-  }
-}
deletedjs-packages/tests/src/sub/nesting/admin.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/nesting/admin.test.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/src/unique.js';
-
-describe('Nesting by collection admin', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 10n, 10n], donor);
-    });
-  });
-
-  [
-    {restricted: true},
-    {restricted: false},
-  ].map(testCase => {
-    itSub(`can nest tokens if "collectionAdmin" permission set ${testCase.restricted ? ', in restricted mode' : ''}`, async ({helper}) => {
-      const collectionA = await helper.nft.mintCollection(alice);
-      await collectionA.addAdmin(alice, {Substrate: bob.address});
-      const collectionB = await helper.nft.mintCollection(alice);
-      await collectionB.addAdmin(alice, {Substrate: bob.address});
-      // Collection has permission for collectionAdmin to nest:
-      await collectionA.setPermissions(alice, {nesting:
-        {collectionAdmin: true, restricted: testCase.restricted ? [collectionA.collectionId, collectionB.collectionId] : null},
-      });
-      // Token for nesting in from collectionA:
-      const targetTokenA = await collectionA.mintToken(alice, {Substrate: charlie.address});
-
-      // 1. Create an immediately nested tokens:
-      // 1.1 From own collection:
-      const nestedTokenA = await collectionA.mintToken(bob, targetTokenA.nestingAccount());
-      // 1.2 From different collection:
-      const nestedTokenB = await collectionB.mintToken(bob, targetTokenA.nestingAccount());
-      expect(await nestedTokenA.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
-      expect(await nestedTokenA.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount()));
-      expect(await nestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
-      expect(await nestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount()));
-
-      // 2. Create a token to be nested and nest:
-      const newNestedTokenA = await collectionA.mintToken(bob);
-      const newNestedTokenB = await collectionB.mintToken(bob);
-      // 2.1 From own collection:
-      await newNestedTokenA.nest(bob, targetTokenA);
-      // 2.2 From different collection:
-      await newNestedTokenB.nest(bob, targetTokenA);
-      expect(await newNestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
-      expect(await newNestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount()));
-    });
-  });
-
-  itSub('can operate together with token owner if "collectionAdmin" and "tokenOwner" permissions set', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    const targetToken = await collection.mintToken(alice, {Substrate: charlie.address});
-
-    // Admin can create an immediately nested token:
-    const nestedToken = await collection.mintToken(bob, targetToken.nestingAccount());
-    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
-    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-
-    // Owner can create and and nest:
-    const newToken = await collection.mintToken(alice, {Substrate: charlie.address});
-    await newToken.nest(charlie, targetToken);
-    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
-    expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-  });
-});
deletedjs-packages/tests/src/sub/nesting/common.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/nesting/common.test.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId, UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/src/unique.js';
-
-let alice: IKeyringPair;
-let bob: IKeyringPair;
-
-describe('Common nesting tests', () => {
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  [
-    {mode: 'nft' as const, restrictedMode: true, requiredPallets: []},
-    {mode: 'nft' as const, restrictedMode: false, requiredPallets: []},
-    {mode: 'rft' as const, restrictedMode: true, requiredPallets: [Pallets.ReFungible]},
-    {mode: 'rft' as const, restrictedMode: false, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase => {
-    itSub.ifWithPallets(`Token owner can nest ${testCase.mode.toUpperCase()} in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, testCase.requiredPallets, async ({helper}) => {
-      // Only NFT can be target for nesting in
-      const targetNFTCollection = await helper.nft.mintCollection(alice);
-      const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
-
-      const collectionForNesting = await helper[testCase.mode].mintCollection(bob);
-      // permissions should be set:
-      await targetNFTCollection.setPermissions(alice, {
-        nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null},
-      });
-
-      // 1. Bob can immediately create nested token:
-      const nestedToken1 = testCase.mode === 'nft'
-        ? await (collectionForNesting as UniqueNFTCollection).mintToken(bob, targetTokenBob.nestingAccount())
-        : await (collectionForNesting as UniqueRFTCollection).mintToken(bob, 10n, targetTokenBob.nestingAccount());
-      expect(await nestedToken1.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
-      expect(await nestedToken1.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount()));
-
-      // 2. Bob can mint and nest token:
-      const nestedToken2 = await collectionForNesting.mintToken(bob);
-      await nestedToken2.nest(bob, targetTokenBob);
-      expect(await nestedToken2.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
-      expect(await nestedToken2.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount()));
-    });
-  });
-
-
-  [
-    {restrictedMode: true},
-    {restrictedMode: false},
-  ].map(testCase => {
-    itSub(`Token owner can nest FT in NFT if "tokenOwner" permission set  ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => {
-      // Only NFT allows nesting, permissions should be set:
-      const targetNFTCollection = await helper.nft.mintCollection(alice);
-      const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
-
-      const collectionForNesting = await helper.ft.mintCollection(bob);
-      // permissions should be set:
-      await targetNFTCollection.setPermissions(alice, {
-        nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null},
-      });
-
-      // 1. Alice can immediately create nested tokens:
-      await collectionForNesting.mint(bob, 100n, targetTokenBob.nestingAccount());
-      expect(await collectionForNesting.getTop10Owners()).deep.eq([CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())]);
-      expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(100n);
-
-      // 2. Alice can mint and nest token:
-      await collectionForNesting.mint(bob, 100n);
-      await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n);
-      expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(150n);
-      expect(await targetTokenBob.getChildren()).to.be.deep.equal([{collectionId: collectionForNesting.collectionId, tokenId: 0}]);
-    });
-  });
-
-  [
-    {restrictedMode: true},
-    {restrictedMode: false},
-  ].map(testCase => {
-    itSub(`Token owner can nest Native FT in NFT if "tokenOwner" permission set  ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => {
-      // Only NFT allows nesting, permissions should be set:
-      const targetNFTCollection = await helper.nft.mintCollection(alice);
-      const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
-      expect(await targetTokenBob.getChildren()).to.be.empty;
-
-      const collectionForNesting = helper.ft.getCollectionObject(0);
-      // permissions should be set:
-      await targetNFTCollection.setPermissions(alice, {
-        nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null},
-      });
-
-      // Bob can nest Native FT into their NFT:
-      await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n);
-      expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(50n);
-      // Native FT should't be visible in NFT children:
-      expect(await targetTokenBob.getChildren()).to.be.deep.equal([]);
-    });
-  });
-
-
-  itSub.ifWithPallets('Owner can unnest tokens using transferFrom', [Pallets.ReFungible], async ({helper}) => {
-    const collectionToNest = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const tokenA = await collectionToNest.mintToken(alice);
-    const tokenB = await collectionToNest.mintToken(alice);
-
-    // Create a nested token
-    const nftCollectionToBeNested = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const rftCollectionToBeNested = await helper.rft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const ftCollectionToBeNested = await helper.ft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const nativeFtCollectionToBeNested = helper.ft.getCollectionObject(0);
-
-    const nestedNFT = await nftCollectionToBeNested.mintToken(alice, tokenA.nestingAccount());
-    const nestedRFT = await rftCollectionToBeNested.mintToken(alice, 100n, tokenA.nestingAccount());
-    await ftCollectionToBeNested.mint(alice, 100n, tokenA.nestingAccount());
-    await nativeFtCollectionToBeNested.transfer(alice, tokenA.nestingAccount(), 100n);
-    expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount()));
-    expect(await nestedRFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount()));
-    expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n);
-    expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n);
-
-    expect(await tokenA.getChildren()).to.be.length(3);
-    expect(await tokenB.getChildren()).to.be.length(0);
-
-    // Transfer the nested token to another token
-    await nestedNFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount());
-    await nestedRFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n);
-    await ftCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n);
-    await nativeFtCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n);
-
-    expect(await nestedNFT.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
-    expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenB.nestingAccount()));
-
-    expect(await nestedRFT.getBalance(tokenB.nestingAccount())).to.equal(25n);
-    expect(await nestedRFT.getBalance(tokenA.nestingAccount())).to.equal(75n);
-
-    expect(await ftCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n);
-    expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n);
-
-    expect(await nativeFtCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n);
-    expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n);
-
-    // RFT, FT, and without native FT
-    expect(await tokenA.getChildren()).to.be.length(2);
-    // NFT, RFT, FT, and without native FT
-    expect(await tokenB.getChildren()).to.be.length(3);
-  });
-});
deletedjs-packages/tests/src/sub/nesting/e2e.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/nesting/e2e.test.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/src/unique.js';
-
-describe('Composite nesting tests', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
-    });
-  });
-
-  itSub('Checks token children e2e', async ({helper}) => {
-    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const collectionB = await helper.ft.mintCollection(alice);
-    const collectionC = await helper.rft.mintCollection(alice);
-    const collectionNative = helper.ft.getCollectionObject(0);
-
-    const targetToken = await collectionA.mintToken(alice);
-    expect((await targetToken.getChildren()).length).to.be.equal(0, 'Children length check at creation');
-
-    // Create a nested NFT token
-    const tokenA = await collectionA.mintToken(alice, targetToken.nestingAccount());
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-    ]).and.has.length(1);
-
-    // Create then nest
-    const tokenB = await collectionA.mintToken(alice);
-    await tokenB.nest(alice, targetToken);
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-      {tokenId: tokenB.tokenId, collectionId: collectionA.collectionId},
-    ]).and.has.length(2);
-
-    // Move token B to a different user outside the nesting tree
-    await tokenB.unnest(alice, targetToken, {Substrate: bob.address});
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-    ]).and.has.length(1);
-
-    // Create a fungible token in another collection and then nest
-    await collectionB.mint(alice, 10n);
-    await collectionB.transfer(alice, targetToken.nestingAccount(), 2n);
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-      {tokenId: 0, collectionId: collectionB.collectionId},
-    ]).and.has.length(2);
-
-    // Create a refungible token in another collection and then nest
-    const tokenC = await collectionC.mintToken(alice, 10n);
-    await tokenC.transfer(alice, targetToken.nestingAccount(), 2n);
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-      {tokenId: 0, collectionId: collectionB.collectionId},
-      {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId},
-    ]).and.has.length(3);
-
-    // Nest native fungible token into another collection
-    await collectionNative.transfer(alice, targetToken.nestingAccount(), 2n);
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-      {tokenId: 0, collectionId: collectionB.collectionId},
-      {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId},
-    ]).and.has.length(3);
-
-    // Burn all nested pieces
-    await tokenC.burnFrom(alice, targetToken.nestingAccount(), 2n);
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-      {tokenId: 0, collectionId: collectionB.collectionId},
-    ])
-      .and.has.length(2);
-
-    // Move part of the fungible token inside token A deeper in the nesting tree
-    await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n);
-    expect(await targetToken.getChildren()).to.be.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-      {tokenId: 0, collectionId: collectionB.collectionId},
-    ]).and.has.length(2);
-    // Nested token also has children now:
-    expect(await tokenA.getChildren()).to.have.deep.members([
-      {tokenId: 0, collectionId: collectionB.collectionId},
-    ]).and.has.length(1);
-
-    // Move the remaining part of the fungible token inside token A deeper in the nesting tree
-    await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n);
-    expect(await targetToken.getChildren()).to.have.deep.members([
-      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
-    ]).and.has.length(1);
-    expect(await tokenA.getChildren()).to.have.deep.members([
-      {tokenId: 0, collectionId: collectionB.collectionId},
-    ]).and.has.length(1);
-  });
-
-  /// TODO review this test
-  itSub('Performs the full suite: bundles a token, transfers, and unnests', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    // Create an immediately nested token
-    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
-    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
-    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-
-    // Create a token to be nested
-    const newToken = await collection.mintToken(alice);
-
-    // Nest
-    await newToken.nest(alice, targetToken);
-    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
-    expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-
-    // Move bundle to different user
-    await targetToken.transfer(alice, {Substrate: bob.address});
-    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
-    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
-    expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-
-    // Unnest
-    await newToken.unnest(bob, targetToken, {Substrate: bob.address});
-    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
-    expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
-  });
-});
deletedjs-packages/tests/src/sub/nesting/nesting.negative.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/nesting/nesting.negative.test.ts
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
-import {UniqueFTCollection, UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection, UniqueRFToken} from '@unique/playgrounds/src/unique.js';
-import {itEth} from '../../eth/util/index.js';
-
-let alice: IKeyringPair;
-let bob: IKeyringPair;
-let charlie: IKeyringPair;
-
-describe('Negative Test: Nesting', () => {
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  [
-    {mode: 'nft' as const, requiredPallets: []},
-    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
-  ].map(testCase => {
-    itSub.ifWithPallets(`Owner cannot nest ${testCase.mode.toUpperCase()} if nesting is disabled`, testCase.requiredPallets, async ({helper}) => {
-      // Create default collection, permissions are not set:
-      const aliceNFTCollection = await helper.nft.mintCollection(alice);
-      const targetToken = await aliceNFTCollection.mintToken(alice);
-
-      const collectionForNesting = await helper[testCase.mode].mintCollection(alice);
-
-      // 1. Alice cannot create immediately nested tokens:
-      const nestingTx = testCase.mode === 'nft'
-        ? (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount())
-        : (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount());
-      await expect(nestingTx).to.be.rejectedWith('common.UserIsNotAllowedToNest');
-
-      // 2. Alice cannot mint and nest token:
-      const nestedToken2 = await collectionForNesting.mintToken(alice);
-      await expect(nestedToken2.nest(alice, targetToken)).to.be.rejectedWith('common.UserIsNotAllowedToNest');
-    });
-  });
-
-  [
-    {mode: 'ft'},
-    {mode: 'nativeFt'},
-  ].map(testCase => {
-    itSub(`Owner cannot nest [${testCase.mode}] if nesting is disabled (except for native fungible collection)`, async ({helper}) => {
-    // Create default collection, permissions are not set:
-      const aliceNFTCollection = await helper.nft.mintCollection(alice);
-      const targetToken = await aliceNFTCollection.mintToken(alice);
-
-      const collectionForNesting = testCase.mode === 'ft' ? await helper.ft.mintCollection(alice) : helper.ft.getCollectionObject(0);
-
-      // Alice cannot create immediately nested tokens:
-      if(testCase.mode === 'ft') {
-        await expect(collectionForNesting.mint(alice, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest');
-      } else {
-        await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 100n)).to.be.not.rejected;
-      }
-
-      // Alice can't mint and nest tokens:
-      if(testCase.mode === 'ft') {
-        await collectionForNesting.mint(alice, 100n);
-      }
-
-      if(testCase.mode === 'ft') {
-        await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.rejectedWith('common.UserIsNotAllowedToNest');
-      } else {
-        await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.not.rejected;
-      }
-    });
-  });
-
-  [
-    {mode: 'nft' as const},
-    {mode: 'rft' as const},
-    {mode: 'ft' as const},
-    {mode: 'native ft' as const},
-  ].map(testCase => {
-    itSub(`Non-owner and non-admin cannot nest ${testCase.mode.toUpperCase()} in someone else's tokens (except for native fungible collection)`, async ({helper}) => {
-      const targetCollection = await helper.nft.mintCollection(alice, {permissions:
-        {nesting: {tokenOwner: true, collectionAdmin: true}},
-      });
-      const targetToken = await targetCollection.mintToken(alice);
-
-      const nestedCollectionBob = await (
-        testCase.mode === 'native ft'
-          ? helper.ft.getCollectionObject(0)
-          : helper[testCase.mode].mintCollection(bob)
-      );
-
-      let nestedTokenBob: UniqueNFToken | UniqueRFToken;
-      switch (testCase.mode) {
-        case 'nft': nestedTokenBob = await (nestedCollectionBob as UniqueNFTCollection).mintToken(bob); break;
-        case 'rft': nestedTokenBob = await (nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n); break;
-        case 'ft': await (nestedCollectionBob as UniqueFTCollection).mint(bob, 100n); break;
-        case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); break;
-      }
-
-      // Bob non-owner of targetToken and non admin of targetCollection, so
-      // 1. cannot mint nested token:
-      switch (testCase.mode) {
-        case 'nft': await expect((nestedCollectionBob as UniqueNFTCollection).mintToken(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
-        case 'rft': await expect((nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
-        case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
-        case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UnsupportedOperation'); break;
-      }
-
-      // 2. cannot nest existing token:
-      switch (testCase.mode) {
-        case 'nft':
-        case 'rft': await expect(nestedTokenBob!.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
-        case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
-        case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.not.rejected; break;
-      }
-    });
-  });
-
-  [
-    {mode: 'nft' as const, nesting: {tokenOwner: true,  collectionAdmin: false}},
-    {mode: 'nft' as const, nesting: {tokenOwner: false, collectionAdmin: true}},
-  ].map(testCase => {
-    itSub(`${testCase.nesting.tokenOwner ? 'Admin' : 'Token owner'} cannot nest when only ${testCase.nesting.tokenOwner ? 'tokenOwner' : 'collectionAdmin'} is allowed`, async ({helper}) => {
-      // Create collection with tokenOwner or create collection with collectionAdmin permission:
-      const targetCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: testCase.nesting}});
-      const targetTokenCharlie = await targetCollection.mintToken(alice, {Substrate: charlie.address});
-      await targetCollection.addAdmin(alice, {Substrate: bob.address});
-
-      const nestedCollectionCharlie = await helper[testCase.mode].mintCollection(charlie);
-      const nestedCollectionBob = await helper[testCase.mode].mintCollection(bob);
-      // if nesting permissions restricted for token owner – minter is bob (admin),
-      // if collectionAdmin – charlie (owner)
-      testCase.nesting.tokenOwner
-        ? await expect(nestedCollectionBob.mintToken(bob, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/)
-        : await expect(nestedCollectionCharlie.mintToken(charlie, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
-    });
-  });
-
-  itSub.ifWithPallets('Cannot nest in non existing token (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice);
-    // To avoid UserIsNotAllowedToNest error
-    await helper.collection.setPermissions(alice, collection.collectionId, {nesting: {collectionAdmin: true}});
-
-    // The list of non-existing tokens:
-    const tokenFromNonExistingCollection = helper.nft.getTokenObject(9999999, 1);
-    const tokenBurnt = await collection.mintToken(alice);
-    await tokenBurnt.burn(alice);
-    const tokenNotMintedYet = helper.nft.getTokenObject(collection.collectionId, 2);
-
-    // The list of collections to nest tokens from:
-    const nftCollectionForNesting = await helper.nft.mintCollection(alice);
-    const rftCollectionForNesting = await helper.rft.mintCollection(alice);
-    const ftCollectionForNesting = await helper.ft.mintCollection(alice);
-    const nativeFtCollectionForNesting = helper.ft.getCollectionObject(0);
-
-    const testCases = [
-      {token: tokenFromNonExistingCollection, error: 'CollectionNotFound'},
-      {token: tokenBurnt, error: 'TokenNotFound'},
-      {token: tokenNotMintedYet, error: 'TokenNotFound'},
-    ];
-
-    for(const testCase of testCases) {
-      // 1. Alice cannot create nested token to non-existing token
-      await expect(nftCollectionForNesting.mintToken(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
-      await expect(rftCollectionForNesting.mintToken(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
-      await expect(ftCollectionForNesting.mint(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
-
-      // 2. Alice cannot mint and nest token:
-      const nft = await nftCollectionForNesting.mintToken(alice);
-      const rft = await rftCollectionForNesting.mintToken(alice, 100n);
-      await ftCollectionForNesting.mint(alice, 100n);
-      await expect(nft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
-      await expect(rft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
-      await expect(ftCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.rejectedWith(testCase.error);
-      await expect(nativeFtCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.not.rejected;
-    }
-  });
-
-  itEth.ifWithPallets('Cannot nest in collection address', [Pallets.ReFungible], async({helper}) => {
-    const existingCollection = await helper.nft.mintCollection(alice);
-    const existingCollectionAddress = helper.ethAddress.fromCollectionId(existingCollection.collectionId);
-    const futureCollectionAddress = helper.ethAddress.fromCollectionId(99999999);
-
-    const nftCollectionForNesting = await helper.nft.mintCollection(alice);
-
-    // 1. Alice cannot create nested token to collection address
-    await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
-    await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
-
-    // 2. Alice cannot mint and nest token to collection address:
-    const nft = await nftCollectionForNesting.mintToken(alice);
-    await expect(nft.transfer(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
-    await expect(nft.transfer(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
-  });
-
-  itEth.ifWithPallets('Cannot nest in RFT or FT (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => {
-    // Create default collection, permissions are not set:
-    const rftCollection = await helper.rft.mintCollection(alice);
-    const ftCollection = await helper.ft.mintCollection(alice);
-    const nativeFtCollection = helper.ft.getCollectionObject(0);
-
-    const rftToken = await rftCollection.mintToken(alice);
-    await ftCollection.mint(alice, 100n);
-
-    const collectionForNesting = await helper.nft.mintCollection(alice);
-
-    // 1. Alice cannot create immediately nested tokens:
-    await expect(collectionForNesting.mintToken(alice, rftToken.nestingAccount())).to.be.rejectedWith('refungible.RefungibleDisallowsNesting');
-    await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting');
-    await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.rejectedWith('common.UnsupportedOperation');
-
-    // 2. Alice cannot mint and nest token:
-    const nestedToken2 = await collectionForNesting.mintToken(alice);
-    await expect(nestedToken2.nest(alice, rftToken)).to.be.rejectedWith('refungible.RefungibleDisallowsNesting');
-    await expect(ftCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting');
-    await expect(nativeFtCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.not.rejected;
-  });
-
-  itSub('Cannot nest in restricted collection if collection is not in the list (except native fungible collection)', async ({helper}) => {
-    const {collectionId: allowedCollectionId} = await helper.nft.mintCollection(alice);
-    const notAllowedCollectionNFT = await helper.nft.mintCollection(alice);
-    const notAllowedCollectionRFT = await helper.rft.mintCollection(alice);
-    const notAllowedCollectionFT = await helper.ft.mintCollection(alice);
-    const allowedCollectionNativeFT = helper.ft.getCollectionObject(0);
-
-    // Collection restricted to allowedCollectionId
-    const restrictedCollectionA = await helper.nft.mintCollection(alice, {permissions:
-      {nesting: {tokenOwner: true, restricted: [allowedCollectionId]}},
-    });
-    // Create collection with restricted nesting -- even self is not allowed
-    const restrictedCollectionB = await helper.nft.mintCollection(alice, {permissions:
-      {nesting: {tokenOwner: true, restricted: []}},
-    });
-
-    const targetTokenA = await restrictedCollectionA.mintToken(alice);
-    const targetTokenB = await restrictedCollectionB.mintToken(alice);
-
-    // 1. Cannot mint in own collection after allowlisting the accounts:
-    await expect(restrictedCollectionA.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-    await expect(restrictedCollectionB.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-
-    // 2. Cannot mint from notAllowedCollection:
-    await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-    await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-    await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-    await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-    await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-    await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
-    await expect(allowedCollectionNativeFT.transfer(alice, targetTokenA.nestingAccount(), 100n)).to.be.not.rejected;
-    await expect(allowedCollectionNativeFT.transfer(alice, targetTokenB.nestingAccount(), 100n)).to.be.not.rejected;
-  });
-
-  itSub('Cannot create nesting chains greater than 5', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    let token = await collection.mintToken(alice);
-
-    const maxNestingLevel = 5;
-
-    // Create a nested-token matryoshka
-    for(let i = 0; i < maxNestingLevel; i++) {
-      token = await collection.mintToken(alice, token.nestingAccount());
-    }
-
-    // The nesting depth is limited by `maxNestingLevel`
-    // 1. Cannot mint:
-    await expect(collection.mintToken(alice, token.nestingAccount()))
-      .to.be.rejectedWith(/structure\.DepthLimit/);
-    // 2. Cannot transfer:
-    const anotherToken = await collection.mintToken(alice);
-    await expect(anotherToken.transfer(alice, token.nestingAccount()))
-      .to.be.rejectedWith(/structure\.DepthLimit/);
-    // 3. Cannot nest FT pieces:
-    const ftCollection = await helper.ft.mintCollection(alice);
-    await expect(ftCollection.mint(alice, 100n, token.nestingAccount()))
-      .to.be.rejectedWith(/structure\.DepthLimit/);
-
-    expect(await token.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
-    expect(await token.getChildren()).to.has.length(0);
-  });
-});
deletedjs-packages/tests/src/sub/nesting/refungible.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/nesting/refungible.test.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
-
-describe('ReFungible-specific nesting tests', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice] = await helper.arrange.createAccounts([200n], donor);
-    });
-  });
-
-  itSub.ifWithPallets('ReFungible: getTopmostOwner works correctly with Nesting', [Pallets.ReFungible], async({helper}) => {
-    const collectionNFT = await helper.nft.mintCollection(alice, {
-      permissions: {
-        nesting: {
-          tokenOwner: true,
-        },
-      },
-    });
-    const collectionRFT = await helper.rft.mintCollection(alice);
-
-    const nft = await collectionNFT.mintToken(alice, {Substrate: alice.address});
-    const rft = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address});
-
-    expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});
-
-    await rft.transfer(alice, nft.nestingAccount(), 40n);
-
-    expect(await rft.getTopmostOwner()).deep.equal(null);
-
-    await rft.transfer(alice, nft.nestingAccount(), 60n);
-
-    expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});
-
-    await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 30n);
-
-    expect(await rft.getTopmostOwner()).deep.equal(null);
-
-    await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 70n);
-
-    expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});
-  });
-});
deletedjs-packages/tests/src/sub/nesting/unnesting.negative.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/nesting/unnesting.negative.test.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
-import {CrossAccountId} from '@unique/playgrounds/src/unique.js';
-
-describe('Negative Test: Unnesting', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor);
-    });
-  });
-
-  // TODO: make this test a bit more generic
-  itSub('Admin (NFT): disallows an Admin to unnest someone else\'s token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {limits: {ownerCanTransfer: true}, permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}});
-    //await collection.addAdmin(alice, {Substrate: bob.address});
-    const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
-    await collection.addToAllowList(alice, {Substrate: bob.address});
-    await collection.addToAllowList(alice, targetToken.nestingAccount());
-
-    // Try to nest somebody else's token
-    const newToken = await collection.mintToken(bob);
-    await expect(newToken.nest(alice, targetToken))
-      .to.be.rejectedWith(/common\.NoPermission/);
-
-    // Try to unnest a token belonging to someone else as collection admin
-    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
-    await expect(nestedToken.unnest(alice, targetToken, {Substrate: bob.address}))
-      .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
-
-    expect(await targetToken.getChildren()).to.be.length(1);
-    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
-    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
-  });
-
-  [
-    {mode: 'ft' as const},
-    {mode: 'native ft' as const},
-  ].map(md => [
-    {mode: md.mode, restrictedMode: true},
-    {mode: md.mode, restrictedMode: false},
-  ].map(testCase => {
-    itSub(`Fungible: disallows a non-Owner to unnest someone else's token [${testCase.mode}${testCase.restrictedMode ? ' (Restricted nesting)' : ''}]`, async ({helper}) => {
-      const collectionNFT = await helper.nft.mintCollection(alice);
-      const collectionFT = await (
-        testCase.mode === 'ft'
-          ? helper.ft.mintCollection(alice)
-          : helper.ft.getCollectionObject(0)
-      );
-      const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address});
-
-      await collectionNFT.setPermissions(alice, {nesting: {
-        collectionAdmin: true, tokenOwner: true, restricted: testCase.restrictedMode ? [collectionFT.collectionId] : null,
-      }});
-
-      // Nest some tokens as Alice into Bob's token
-      await (
-        testCase.mode === 'ft'
-          ? collectionFT.mint(alice, 5n, targetToken.nestingAccount())
-          : collectionFT.transfer(alice, targetToken.nestingAccount(), 5n)
-      );
-
-      // Try to pull it out as Alice still
-      await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: bob.address}, 1n))
-        .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-      expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
-    });
-  }));
-});
deletedjs-packages/tests/src/sub/refungible/burn.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/refungible/burn.test.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
-
-describe('Refungible: burn', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
-    });
-  });
-
-  itSub('can burn some pieces', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
-    await token.burn(alice, 99n);
-    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n);
-  });
-
-  itSub('can burn all pieces', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-
-    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
-
-    await token.burn(alice, 100n);
-    expect(await collection.doesTokenExist(token.tokenId)).to.be.false;
-  });
-
-  itSub('burn pieces for multiple users', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-
-    await token.transfer(alice, {Substrate: bob.address}, 60n);
-
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n);
-    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n);
-
-    await token.burn(alice, 40n);
-
-    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n);
-
-    await token.burn(bob, 59n);
-
-    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n);
-    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
-
-    await token.burn(bob, 1n);
-
-    expect(await collection.doesTokenExist(token.tokenId)).to.be.false;
-  });
-
-  itSub('burn pieces by admin', async function({helper}) {
-    const collection = await helper.rft.mintCollection(alice);
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-    await collection.addAdmin(alice, {Substrate: bob.address});
-    const token = await collection.mintToken(alice, 100n);
-
-    await token.burnFrom(bob, {Substrate: alice.address}, 100n);
-    expect(await token.doesExist()).to.be.false;
-  });
-});
-
-describe('Refungible: burn negative tests', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
-    });
-  });
-
-  itSub('cannot burn non-owned token pieces', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice);
-    const aliceToken = await collection.mintToken(alice, 10n, {Substrate: alice.address});
-    const bobToken = await collection.mintToken(alice, 10n, {Substrate: bob.address});
-
-    // 1. Cannot burn non-owned token:
-    await expect(bobToken.burn(alice, 0n)).to.be.rejectedWith('common.TokenValueTooLow');
-    await expect(bobToken.burn(alice, 5n)).to.be.rejectedWith('common.TokenValueTooLow');
-    // 2. Cannot burn non-existing token:
-    await expect(helper.rft.burnToken(alice, 99999, 10)).to.be.rejectedWith('common.CollectionNotFound');
-    await expect(helper.rft.burnToken(alice, collection.collectionId, 99999)).to.be.rejectedWith('common.TokenValueTooLow');
-    // 3. Can burn zero amount of owned tokens (EIP-20)
-    await aliceToken.burn(alice, 0n);
-
-    // 4. Storage is not corrupted:
-    expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]);
-    expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]);
-
-    // 4.1 Tokens can be transfered:
-    await aliceToken.transfer(alice, {Substrate: bob.address}, 10n);
-    await bobToken.transfer(bob, {Substrate: alice.address}, 10n);
-    expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]);
-    expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]);
-  });
-});
deletedjs-packages/tests/src/sub/refungible/nesting.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/refungible/nesting.test.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../../util/index.js';
-
-describe('Refungible nesting', () => {
-  let alice: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      const donor = await privateKey({url: import.meta.url});
-      [alice, charlie] = await helper.arrange.createAccounts([50n, 10n], donor);
-    });
-  });
-
-  [
-    {restrictedMode: true},
-    {restrictedMode: false},
-  ].map(testCase => {
-    itSub(`Owner can nest their token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => {
-      const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
-      const collectionRFT = await helper.rft.mintCollection(alice);
-      const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
-
-      await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}});
-      await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
-      await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
-
-      await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
-      await collectionRFT.addToAllowList(alice, {Substrate: charlie.address});
-      await collectionRFT.addToAllowList(alice, targetToken.nestingAccount());
-
-      // Create an immediately nested token
-      const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAccount());
-      expect(await nestedToken.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
-
-      // Create a token to be nested and nest
-      const newToken = await collectionRFT.mintToken(charlie, 5n);
-      await newToken.transfer(charlie, targetToken.nestingAccount(), 2n);
-      expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(2n);
-      expect(await newToken.getBalance({Substrate: charlie.address})).to.be.equal(3n);
-    });
-  });
-
-  itSub('Owner can unnest nested token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    // Owner mints nested RFT token:
-    const collectionRFT = await helper.rft.mintCollection(alice);
-    const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount());
-
-    // 1.1 Owner can partially unnest token pieces with transferFrom:
-    await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n);
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n);
-    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
-
-    // 1.2 Owner can unnest all pieces:
-    await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 1n);
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(10n);
-    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
-  });
-
-  itSub('Owner can burn nested token pieces', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
-    const targetToken = await collection.mintToken(alice);
-
-    // Owner mints nested RFT token:
-    const collectionRFT = await helper.rft.mintCollection(alice);
-    const token = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address});
-    await token.transfer(alice, targetToken.nestingAccount(), 30n);
-
-    // 1.1 Owner can partially burnFrom nested pieces:
-    await token.burnFrom(alice, targetToken.nestingAccount(), 10n);
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(70n);
-    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(20n);
-    expect(await targetToken.getChildren()).to.has.length(1);
-
-    // 1.1 Owner can burnFrom all nested pieces:
-    await token.burnFrom(alice, targetToken.nestingAccount(), 20n);
-    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
-    expect(await targetToken.getChildren()).to.has.length(0);
-    expect(await token.doesExist()).to.be.true;
-
-    // 2. Target token does not contain any pieces and can be burnt:
-    await targetToken.burn(alice);
-    expect(await targetToken.doesExist()).to.be.false;
-  });
-});
-
-describe('Refungible nesting negative tests', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor);
-    });
-  });
-
-  [
-    {restrictedMode: true},
-    {restrictedMode: false},
-  ].map(testCase => {
-    itSub(`non-Owner cannot nest someone else's token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => {
-      const collectionNFT = await helper.nft.mintCollection(alice);
-      const collectionRFT = await helper.rft.mintCollection(alice);
-      const targetToken = await collectionNFT.mintToken(alice);
-
-      await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}});
-      await collectionNFT.addToAllowList(alice, {Substrate: bob.address});
-      await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
-
-      // Try to create a token to be nested and nest
-      const newToken = await collectionRFT.mintToken(alice);
-      await expect(newToken.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.TokenValueTooLow/);
-
-      expect(await targetToken.getChildren()).to.be.length(0);
-      expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n);
-
-      // Nest some tokens as Alice into Bob's token
-      await newToken.transfer(alice, targetToken.nestingAccount());
-
-      // Try to pull it out
-      await expect(newToken.transferFrom(bob, targetToken.nestingAccount(), {Substrate: alice.address}, 1n))
-        .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-      expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
-    });
-  });
-});
deletedjs-packages/tests/src/sub/refungible/repartition.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/refungible/repartition.test.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
-
-describe('integration test: Refungible functionality:', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
-    });
-  });
-
-  itSub('Repartition', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-
-    expect(await token.repartition(alice, 200n)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(200n);
-    expect(await token.getTotalPieces()).to.be.equal(200n);
-
-    expect(await token.transfer(alice, {Substrate: bob.address}, 110n)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(90n);
-    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(110n);
-
-    await expect(token.repartition(alice, 80n))
-      .to.eventually.be.rejectedWith(/refungible\.RepartitionWhileNotOwningAllPieces/);
-
-    expect(await token.transfer(alice, {Substrate: bob.address}, 90n)).to.be.true;
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n);
-    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(200n);
-
-    expect(await token.repartition(bob, 150n)).to.be.true;
-    await expect(token.transfer(bob, {Substrate: alice.address}, 160n))
-      .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/);
-  });
-
-  itSub('Repartition with increased amount', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-    await token.repartition(alice, 200n);
-    const chainEvents = helper.chainLog.slice(-1)[0].events;
-    const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemCreated');
-    expect(event).to.deep.include({
-      section: 'common',
-      method: 'ItemCreated',
-      index: [66, 2],
-      data: [
-        collection.collectionId,
-        token.tokenId,
-        {substrate: alice.address},
-        100n,
-      ],
-    });
-  });
-
-  itSub('Repartition with decreased amount', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-    await token.repartition(alice, 50n);
-    const chainEvents = helper.chainLog.slice(-1)[0].events;
-    const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemDestroyed');
-    expect(event).to.deep.include({
-      section: 'common',
-      method: 'ItemDestroyed',
-      index: [66, 3],
-      data: [
-        collection.collectionId,
-        token.tokenId,
-        {substrate: alice.address},
-        50n,
-      ],
-    });
-  });
-});
-
deletedjs-packages/tests/src/sub/refungible/transfer.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/sub/refungible/transfer.test.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
-
-describe('Refungible transfer tests', () => {
-  let donor: IKeyringPair;
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async function() {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
-
-      donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
-    });
-  });
-
-  itSub('Can transfer token pieces', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const token = await collection.mintToken(alice, 100n);
-
-    expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true;
-    // 1. Can transfer less or equal than have:
-    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n);
-    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n);
-  });
-
-  itSub('Cannot transfer incorrect amount of token pieces', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
-    const tokenAlice = await collection.mintToken(alice, 10n, {Substrate: alice.address});
-    const tokenBob = await collection.mintToken(alice, 10n, {Substrate: bob.address});
-
-    // 1. Alice cannot transfer Bob's token:
-    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow');
-    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow');
-    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 10n)).to.be.rejectedWith('common.TokenValueTooLow');
-    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow');
-
-    // 2. Alice cannot transfer non-existing token:
-    await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow');
-    await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow');
-
-    // 3. Cannot transfer more than have:
-    await expect(tokenAlice.transfer(alice, {Substrate: bob.address}, 11n))
-      .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/);
-
-    // 4. Zero transfer allowed (EIP-20):
-    await tokenAlice.transfer(alice, {Substrate: charlie.address}, 0n);
-
-    expect(await tokenAlice.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]);
-    expect(await tokenBob.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]);
-    expect(await tokenAlice.getBalance({Substrate: alice.address})).to.eq(10n);
-    expect(await tokenBob.getBalance({Substrate: bob.address})).to.eq(10n);
-    expect(await tokenBob.getBalance({Substrate: charlie.address})).to.eq(0n);
-  });
-});
deletedjs-packages/tests/src/transfer.test.tsdiffbeforeafterboth
before · js-packages/tests/src/transfer.test.ts
1// 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/src/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});
deletedjs-packages/tests/src/transferFrom.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/transferFrom.test.ts
+++ /dev/null
@@ -1,375 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js';
-import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/src/types.js';
-
-describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('[nft] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-1', description: '', tokenPrefix: 'TF'});
-    const nft = await collection.mintToken(alice);
-    await nft.approve(alice, {Substrate: bob.address});
-    expect(await nft.isApproved({Substrate: bob.address})).to.be.true;
-
-    await nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address});
-    expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address});
-  });
-
-  itSub('[fungible] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-2', description: '', tokenPrefix: 'TF'});
-    await collection.mint(alice, 10n);
-    await collection.approveTokens(alice, {Substrate: bob.address}, 7n);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n);
-
-    await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n);
-    expect(await collection.getBalance({Substrate: charlie.address})).to.be.equal(6n);
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(4n);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n);
-  });
-
-  itSub.ifWithPallets('[refungible] Execute the extrinsic and check nftItemList - owner of token', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-3', description: '', tokenPrefix: 'TF'});
-    const rft = await collection.mintToken(alice, 10n);
-    await rft.approve(alice, {Substrate: bob.address}, 7n);
-    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n);
-
-    await rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n);
-    expect(await rft.getBalance({Substrate: charlie.address})).to.be.equal(6n);
-    expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(4n);
-    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n);
-  });
-
-  itSub('Should reduce allowance if value is big', async ({helper}) => {
-    // fungible
-    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-4', description: '', tokenPrefix: 'TF'});
-    await collection.mint(alice, 500000n);
-
-    await collection.approveTokens(alice, {Substrate: bob.address}, 500000n);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(500000n);
-    await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 500000n);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(0n);
-  });
-
-  itSub('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-5', description: '', tokenPrefix: 'TF'});
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-
-    const nft = await collection.mintToken(alice, {Substrate: bob.address});
-    await nft.transferFrom(alice, {Substrate: bob.address}, {Substrate: charlie.address});
-    expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address});
-  });
-});
-
-describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-  let charlie: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      const donor = await privateKey({url: import.meta.url});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor);
-    });
-  });
-
-  itSub('transferFrom for a collection that does not exist', async ({helper}) => {
-    await expect(helper.collection.approveToken(alice, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: bob.address}, 1n))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-    await expect(helper.collection.transferTokenFrom(bob, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: alice.address}, {Substrate: bob.address}, 1n))
-      .to.be.rejectedWith(/common\.CollectionNotFound/);
-  });
-
-  /* itSub('transferFrom for a collection that was destroyed', async ({helper}) => {
-      this test copies approve negative test
-  }); */
-
-  /* itSub('transferFrom a token that does not exist', async ({helper}) => {
-    this test copies approve negative test
-  }); */
-
-  /* itSub('transferFrom a token that was deleted', async ({helper}) => {
-    this test copies approve negative test
-  }); */
-
-  itSub('[nft] transferFrom for not approved address', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'});
-    const nft = await collection.mintToken(alice);
-
-    await expect(nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}))
-      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itSub('[fungible] transferFrom for not approved address', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'});
-    await collection.mint(alice, 10n);
-
-    await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n))
-      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
-    expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
-  });
-
-  itSub.ifWithPallets('[refungible] transferFrom for not approved address', [Pallets.ReFungible], async({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-3', description: '', tokenPrefix: 'TF'});
-    const rft = await collection.mintToken(alice, 10n);
-
-    await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}))
-      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
-    expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
-    expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
-  });
-
-  itSub('[nft] transferFrom incorrect token count', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-4', description: '', tokenPrefix: 'TF'});
-    const nft = await collection.mintToken(alice);
-
-    await nft.approve(alice, {Substrate: bob.address});
-    expect(await nft.isApproved({Substrate: bob.address})).to.be.true;
-
-    await expect(helper.collection.transferTokenFrom(
-      bob,
-      collection.collectionId,
-      nft.tokenId,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-      2n,
-    )).to.be.rejectedWith(/nonfungible\.NonfungibleItemsHaveNoAmount/);
-    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itSub('[fungible] transferFrom incorrect token count', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-5', description: '', tokenPrefix: 'TF'});
-    await collection.mint(alice, 10n);
-
-    await collection.approveTokens(alice, {Substrate: bob.address}, 2n);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(2n);
-
-    await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n))
-      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
-    expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
-  });
-
-  itSub.ifWithPallets('[refungible] transferFrom incorrect token count', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-6', description: '', tokenPrefix: 'TF'});
-    const rft = await collection.mintToken(alice, 10n);
-
-    await rft.approve(alice, {Substrate: bob.address}, 5n);
-    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(5n);
-
-    await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 7n))
-      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
-    expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
-    expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
-  });
-
-  itSub('[nft] execute transferFrom from account that is not owner of collection', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-7', description: '', tokenPrefix: 'TF'});
-    const nft = await collection.mintToken(alice);
-
-    await expect(nft.approve(charlie, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
-    expect(await nft.isApproved({Substrate: bob.address})).to.be.false;
-
-    await expect(nft.transferFrom(
-      charlie,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
-  });
-
-  itSub('[fungible] execute transferFrom from account that is not owner of collection', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-8', description: '', tokenPrefix: 'TF'});
-    await collection.mint(alice, 10000n);
-
-    await expect(collection.approveTokens(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n);
-    expect(await collection.getApprovedTokens({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n);
-
-    await expect(collection.transferFrom(
-      charlie,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n);
-    expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
-    expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
-  });
-
-  itSub.ifWithPallets('[refungible] execute transferFrom from account that is not owner of collection', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-9', description: '', tokenPrefix: 'TF'});
-    const rft = await collection.mintToken(alice, 10000n);
-
-    await expect(rft.approve(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
-    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n);
-    expect(await rft.getApprovedPieces({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n);
-
-    await expect(rft.transferFrom(
-      charlie,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-    expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n);
-    expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
-    expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
-  });
-
-  itSub('transferFrom burnt token before approve NFT', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-10', description: '', tokenPrefix: 'TF'});
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-    const nft = await collection.mintToken(alice);
-
-    await nft.burn(alice);
-    await expect(nft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TokenNotFound/);
-
-    await expect(nft.transferFrom(
-      bob,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-  });
-
-  itSub('transferFrom burnt token before approve Fungible', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-11', description: '', tokenPrefix: 'TF'});
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-    await collection.mint(alice, 10n);
-
-    await collection.burnTokens(alice, 10n);
-    await expect(collection.approveTokens(alice, {Substrate: bob.address})).to.be.not.rejected;
-
-    await expect(collection.transferFrom(
-      alice,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.TokenValueTooLow/);
-  });
-
-  itSub.ifWithPallets('transferFrom burnt token before approve ReFungible', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-12', description: '', tokenPrefix: 'TF'});
-    await collection.setLimits(alice, {ownerCanTransfer: true});
-    const rft = await collection.mintToken(alice, 10n);
-
-    await rft.burn(alice, 10n);
-    await expect(rft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
-
-    await expect(rft.transferFrom(
-      alice,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.TokenValueTooLow/);
-  });
-
-  itSub('transferFrom burnt token after approve NFT', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-13', description: '', tokenPrefix: 'TF'});
-    const nft = await collection.mintToken(alice);
-
-    await nft.approve(alice, {Substrate: bob.address});
-    expect(await nft.isApproved({Substrate: bob.address})).to.be.true;
-
-    await nft.burn(alice);
-
-    await expect(nft.transferFrom(
-      bob,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-  });
-
-  itSub('transferFrom burnt token after approve Fungible', async ({helper}) => {
-    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-14', description: '', tokenPrefix: 'TF'});
-    await collection.mint(alice, 10n);
-
-    await collection.approveTokens(alice, {Substrate: bob.address});
-    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(1n);
-
-    await collection.burnTokens(alice, 10n);
-
-    await expect(collection.transferFrom(
-      bob,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.TokenValueTooLow/);
-  });
-
-  itSub.ifWithPallets('transferFrom burnt token after approve ReFungible', [Pallets.ReFungible], async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-15', description: '', tokenPrefix: 'TF'});
-    const rft = await collection.mintToken(alice, 10n);
-
-    await rft.approve(alice, {Substrate: bob.address}, 10n);
-    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(10n);
-
-    await rft.burn(alice, 10n);
-
-    await expect(rft.transferFrom(
-      bob,
-      {Substrate: alice.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-  });
-
-  itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-16', description: '', tokenPrefix: 'TF'});
-    const nft = await collection.mintToken(alice, {Substrate: bob.address});
-
-    await collection.setLimits(alice, {ownerCanTransfer: false});
-
-    await expect(nft.transferFrom(
-      alice,
-      {Substrate: bob.address},
-      {Substrate: charlie.address},
-    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
-  });
-
-  itSub('zero transfer NFT', async ({helper}) => {
-    const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'});
-    const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address});
-    const approvedNft = await collection.mintToken(alice, {Substrate: bob.address});
-    await approvedNft.approve(bob, {Substrate: alice.address});
-
-    // 1. Cannot zero transferFrom (non-existing token)
-    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
-    // 2. Cannot zero transferFrom (not approved token)
-    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
-    // 3. Can zero transferFrom (approved token):
-    await helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, approvedNft.tokenId, 0]);
-
-    // 4.1 approvedNft still approved:
-    expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true;
-    // 4.2 bob is still the owner:
-    expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address});
-    expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address});
-    // 4.3 Alice can transfer approved nft:
-    await approvedNft.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address});
-    expect(await approvedNft.getOwner()).to.deep.eq({Substrate: alice.address});
-  });
-});
deletedjs-packages/tests/src/tx-version-presence.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/tx-version-presence.test.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 {Metadata} from '@polkadot/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-
-let metadata: Metadata;
-
-describe('TxVersion is present', () => {
-  before(async () => {
-    await usingPlaygrounds(async helper => {
-      metadata = await helper.callRpc('api.rpc.state.getMetadata', []);
-    });
-  });
-
-  itSub('Signed extension CheckTxVersion is present', () => {
-    expect(metadata.asLatest.extrinsic.signedExtensions.map(se => se.identifier.toString())).to.include('CheckTxVersion');
-  });
-});
deletedjs-packages/tests/src/util/authorizeEnactUpgrade.tsdiffbeforeafterboth
--- a/js-packages/tests/src/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);
deletedjs-packages/tests/src/util/createHrmp.tsdiffbeforeafterboth
--- a/js-packages/tests/src/util/createHrmp.ts
+++ /dev/null
@@ -1,36 +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);
-      break;
-    default:
-      throw new Error(`unknown hrmp config profile: ${profile}`);
-  }
-}, config.relayUrl);
-// We miss disconnect/unref somewhere.
-process.exit(0);
deletedjs-packages/tests/src/util/frankensteinMigrate.tsdiffbeforeafterboth
--- a/js-packages/tests/src/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,
-};
deletedjs-packages/tests/src/util/globalSetup.tsdiffbeforeafterboth
--- a/js-packages/tests/src/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.transfer',
-            [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);
-});
deletedjs-packages/tests/src/util/identitySetter.tsdiffbeforeafterboth
--- a/js-packages/tests/src/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/src/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));
deletedjs-packages/tests/src/util/index.tsdiffbeforeafterboth
--- a/js-packages/tests/src/util/index.ts
+++ /dev/null
@@ -1,221 +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/src/unique.js';
-import type {ILogger} from '@unique/playgrounds/src/types.js';
-import {DevUniqueHelper, SilentLogger, SilentConsole, DevMoonbeamHelper, DevMoonriverHelper, DevAcalaHelper, DevKaruraHelper, DevRelayHelper, DevWestmintHelper, DevStatemineHelper, DevStatemintHelper, DevAstarHelper, DevShidenHelper, DevPolkadexHelper} from '@unique/playgrounds/src/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 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),
-  };
-}
deletedjs-packages/tests/src/util/relayIdentitiesChecker.tsdiffbeforeafterboth
--- a/js-packages/tests/src/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));
deletedjs-packages/tests/src/util/setCode.tsdiffbeforeafterboth
--- a/js-packages/tests/src/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);
deletedjs-packages/tests/src/vesting.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/vesting.test.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, usingPlaygrounds, expect} from './util/index.js';
-
-describe('Vesting', () => {
-  let donor: IKeyringPair;
-  let nominal: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      donor = await privateKey({url: import.meta.url});
-      nominal = helper.balance.getOneTokenNominal();
-    });
-  });
-
-  itSub('can perform vestedTransfer and claim tokens', async ({helper}) => {
-    // arrange
-    const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor);
-    const currentRelayBlock = await helper.chain.getRelayBlockNumber();
-    const SCHEDULE_1_PERIOD = 6n; // 6 blocks one period
-    const SCHEDULE_1_START = currentRelayBlock + 6n; // Block when 1 schedule starts
-    const SCHEDULE_2_PERIOD = 12n; // 12 blocks one period
-    const SCHEDULE_2_START = currentRelayBlock + 12n; // Block when 2 schedule starts
-    const schedule1 = {start: SCHEDULE_1_START, period: SCHEDULE_1_PERIOD, periodCount: 2n, perPeriod: 50n * nominal};
-    const schedule2 = {start: SCHEDULE_2_START, period: SCHEDULE_2_PERIOD, periodCount: 2n, perPeriod: 100n * nominal};
-
-    // act
-    await helper.balance.vestedTransfer(sender, recepient.address, schedule1);
-    await helper.balance.vestedTransfer(sender, recepient.address, schedule2);
-    let schedule = await helper.balance.getVestingSchedules(recepient.address);
-
-    // check senders balance after vesting:
-    let balanceSender = await helper.balance.getSubstrateFull(sender.address);
-    expect(balanceSender.free / nominal).to.eq(699n);
-    expect(balanceSender.frozen).to.eq(0n);
-    expect(balanceSender.reserved).to.eq(0n);
-
-    // check recepient balance after vesting:
-    let balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
-    expect(balanceRecepient.free).to.eq(301n * nominal);
-    expect(balanceRecepient.frozen).to.eq(300n * nominal);
-    expect(balanceRecepient.reserved).to.eq(0n);
-
-    // Schedules list correct:
-    expect(schedule).to.has.length(2);
-    expect(schedule[0]).to.deep.eq(schedule1);
-    expect(schedule[1]).to.deep.eq(schedule2);
-
-    // Wait first part available:
-    await helper.wait.forRelayBlockNumber(SCHEDULE_1_START + SCHEDULE_1_PERIOD);
-    await helper.balance.claim(recepient);
-
-    // check recepient balance after claim (50 tokens claimed, 250 left):
-    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
-    expect(balanceRecepient.free / nominal).to.eq(300n);
-    expect(balanceRecepient.frozen).to.eq(250n * nominal);
-    expect(balanceRecepient.reserved).to.eq(0n);
-
-    // Wait first schedule ends and first part od second schedule:
-    await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD);
-    await helper.balance.claim(recepient);
-
-    // check recepient balance after second claim (150 tokens claimed, 100 left):
-    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
-    expect(balanceRecepient.free / nominal).to.eq(300n);
-    expect(balanceRecepient.frozen).to.eq(100n * nominal);
-    expect(balanceRecepient.reserved).to.eq(0n);
-
-    // Schedules list contain 1 vesting:
-    schedule = await helper.balance.getVestingSchedules(recepient.address);
-    expect(schedule).to.has.length(1);
-    expect(schedule[0]).to.deep.eq(schedule2);
-
-    // Wait 2 schedule ends:
-    await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD * 2n);
-    await helper.balance.claim(recepient);
-
-    // check recepient balance after second claim (100 tokens claimed, 0 left):
-    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
-    expect(balanceRecepient.free / nominal).to.eq(300n);
-    expect(balanceRecepient.frozen).to.eq(0n);
-    expect(balanceRecepient.reserved).to.eq(0n);
-
-    // check sender balance does not changed:
-    balanceSender = await helper.balance.getSubstrateFull(sender.address);
-    expect(balanceSender.free / nominal).to.eq(699n);
-    expect(balanceSender.frozen).to.eq(0n);
-    expect(balanceSender.reserved).to.eq(0n);
-  });
-
-  itSub('cannot send more tokens than have', async ({helper}) => {
-    const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor);
-    const schedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 100n * nominal};
-    const manyPeriodsSchedule = {start: 0n, period: 1n, periodCount: 100n, perPeriod: 10n * nominal};
-    const oneBigSumSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 5000n * nominal};
-
-    // Sender cannot send vestedTransfer to self or other
-    await expect(helper.balance.vestedTransfer(sender, sender.address, manyPeriodsSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/);
-    await expect(helper.balance.vestedTransfer(sender, receiver.address, manyPeriodsSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/);
-    await expect(helper.balance.vestedTransfer(sender, sender.address, oneBigSumSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/);
-    await expect(helper.balance.vestedTransfer(sender, receiver.address, oneBigSumSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/);
-
-    const balanceSender = await helper.balance.getSubstrateFull(sender.address);
-    const balanceReceiver = await helper.balance.getSubstrateFull(receiver.address);
-
-    // Sender's balance has not changed
-    expect(balanceSender.free / nominal).to.eq(999n);
-    expect(balanceSender.frozen).to.eq(0n);
-    expect(balanceSender.reserved).to.eq(0n);
-
-    // Receiver's balance has not changed
-    expect(balanceReceiver.free).to.be.eq(1n * nominal);
-    expect(balanceReceiver.frozen).to.be.eq(0n);
-    expect(balanceReceiver.reserved).to.be.eq(0n);
-
-    // Receiver cannot send vestedTransfer back because of freeze
-    await expect(helper.balance.vestedTransfer(receiver, sender.address, schedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/);
-  });
-
-  itSub('cannot send vestedTransfer with incorrect parameters', async ({helper}) => {
-    const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor);
-    const incorrectperiodSchedule = {start: 0n, period: 0n, periodCount: 10n, perPeriod: 10n * nominal};
-    const incorrectPeriodCountSchedule = {start: 0n, period: 1n, periodCount: 0n, perPeriod: 10n * nominal};
-    const incorrectPerPeriodSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 0n * nominal};
-
-    await expect(helper.balance.vestedTransfer(sender, sender.address, incorrectperiodSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/);
-    await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPeriodCountSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/);
-    await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPerPeriodSchedule)).to.be.rejectedWith(/vesting.AmountLow/);
-
-    const balanceSender = await helper.balance.getSubstrateFull(sender.address);
-    // Sender's balance has not changed
-    expect(balanceSender.free / nominal).to.eq(999n);
-    expect(balanceSender.frozen).to.eq(0n);
-    expect(balanceSender.reserved).to.eq(0n);
-  });
-});
deletedjs-packages/tests/src/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/xcm/lowLevelXcmQuartz.test.ts
+++ /dev/null
@@ -1,364 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds,  usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util/index.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';
-
-const testHelper = new XcmTestHelper('quartz');
-
-describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: QUARTZ_CHAIN,
-            },
-          },
-        },
-      };
-
-      const metadata = {
-        name: 'Quartz',
-        symbol: 'QTZ',
-        decimals: 18,
-        minimalBalance: 1000000000000000000n,
-      };
-
-      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
-        hexToString(v.toJSON()['symbol'])) as string[];
-
-      if(!assets.includes('QTZ')) {
-        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
-      } else {
-        console.log('QTZ token already registered on Karura assetRegistry pallet');
-      }
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
-    });
-  });
-
-  itSub('Should connect and send QTZ to Karura', async () => {
-    await testHelper.sendUnqTo('karura', randomAccount);
-  });
-
-  itSub('Should connect to Karura and send QTZ back', async () => {
-    await testHelper.sendUnqBack('karura', alice, randomAccount);
-  });
-
-  itSub('Karura can send only up to its balance', async () => {
-    await testHelper.sendOnlyOwnedBalance('karura', alice);
-  });
-});
-// These tests are relevant only when
-// the the corresponding foreign assets are not registered
-describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {
-  let alice: IKeyringPair;
-
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-
-
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-  });
-
-  itSub('Quartz rejects KAR tokens from Karura', async () => {
-    await testHelper.rejectNativeTokensFrom('karura', alice);
-  });
-
-  itSub('Quartz rejects MOVR tokens from Moonriver', async () => {
-    await testHelper.rejectNativeTokensFrom('moonriver', alice);
-  });
-
-  itSub('Quartz rejects SDN tokens from Shiden', async () => {
-    await testHelper.rejectNativeTokensFrom('shiden', alice);
-  });
-});
-
-describeXCM('[XCMLL] Integration test: Exchanging QTZ with Moonriver', () => {
-  // Quartz constants
-  let alice: IKeyringPair;
-  let quartzAssetLocation;
-
-  let randomAccountQuartz: IKeyringPair;
-  let randomAccountMoonriver: IKeyringPair;
-
-  // Moonriver constants
-  let assetId: string;
-
-  const quartzAssetMetadata = {
-    name: 'xcQuartz',
-    symbol: 'xcQTZ',
-    decimals: 18,
-    isFrozen: false,
-    minimalBalance: 1n,
-  };
-
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
-
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const alithAccount = helper.account.alithAccount();
-      const baltatharAccount = helper.account.baltatharAccount();
-      const dorothyAccount = helper.account.dorothyAccount();
-
-      randomAccountMoonriver = helper.account.create();
-
-      // >>> Sponsoring Dorothy >>>
-      console.log('Sponsoring Dorothy.......');
-      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring Dorothy.......DONE');
-      // <<< Sponsoring Dorothy <<<
-
-      quartzAssetLocation = {
-        XCM: {
-          parents: 1,
-          interior: {X1: {Parachain: QUARTZ_CHAIN}},
-        },
-      };
-      const existentialDeposit = 1n;
-      const isSufficient = true;
-      const unitsPerSecond = 1n;
-      const numAssetsWeightHint = 0;
-      if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
-        console.log('Quartz asset already registered on Moonriver');
-      } else {
-        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
-          location: quartzAssetLocation,
-          metadata: quartzAssetMetadata,
-          existentialDeposit,
-          isSufficient,
-          unitsPerSecond,
-          numAssetsWeightHint,
-        });
-
-        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
-
-        await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
-      }
-      // >>> Acquire Quartz AssetId Info on Moonriver >>>
-      console.log('Acquire Quartz AssetId Info on Moonriver.......');
-
-      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();
-
-      console.log('QTZ asset ID is %s', assetId);
-      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');
-      // >>> Acquire Quartz AssetId Info on Moonriver >>>
-
-      // >>> Sponsoring random Account >>>
-      console.log('Sponsoring random Account.......');
-      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring random Account.......DONE');
-      // <<< Sponsoring random Account <<<
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);
-    });
-  });
-
-  itSub('Should connect and send QTZ to Moonriver', async () => {
-    await testHelper.sendUnqTo('moonriver', randomAccountQuartz, randomAccountMoonriver);
-  });
-
-  itSub('Should connect to Moonriver and send QTZ back', async () => {
-    await testHelper.sendUnqBack('moonriver', alice, randomAccountQuartz);
-  });
-
-  itSub('Moonriver can send only up to its balance', async () => {
-    await testHelper.sendOnlyOwnedBalance('moonriver', alice);
-  });
-
-  itSub('Should not accept reserve transfer of QTZ from Moonriver', async () => {
-    await testHelper.rejectReserveTransferUNQfrom('moonriver', alice);
-  });
-});
-
-describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden
-  const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden
-
-  // Quartz -> Shiden
-  const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden
-  const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      randomAccount = helper.arrange.createEmptyAccount();
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
-      console.log('sender: ', randomAccount.address);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
-        console.log('1. Create foreign asset and metadata');
-        await helper.getSudo().assets.forceCreate(
-          alice,
-          QTZ_ASSET_ID_ON_SHIDEN,
-          alice.address,
-          QTZ_MINIMAL_BALANCE_ON_SHIDEN,
-        );
-
-        await helper.assets.setMetadata(
-          alice,
-          QTZ_ASSET_ID_ON_SHIDEN,
-          'Quartz',
-          'QTZ',
-          Number(QTZ_DECIMALS),
-        );
-
-        console.log('2. Register asset location on Shiden');
-        const assetLocation = {
-          V2: {
-            parents: 1,
-            interior: {
-              X1: {
-                Parachain: QUARTZ_CHAIN,
-              },
-            },
-          },
-        };
-
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
-
-        console.log('3. Set QTZ payment for XCM execution on Shiden');
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
-      } else {
-        console.log('QTZ is already registered on Shiden');
-      }
-      console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance);
-    });
-  });
-
-  itSub('Should connect and send QTZ to Shiden', async () => {
-    await testHelper.sendUnqTo('shiden', randomAccount);
-  });
-
-  itSub('Should connect to Shiden and send QTZ back', async () => {
-    await testHelper.sendUnqBack('shiden', alice, randomAccount);
-  });
-
-  itSub('Shiden can send only up to its balance', async () => {
-    await testHelper.sendOnlyOwnedBalance('shiden', alice);
-  });
-
-  itSub('Should not accept reserve transfer of QTZ from Shiden', async () => {
-    await testHelper.rejectReserveTransferUNQfrom('shiden', alice);
-  });
-});
-
-describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => {
-  let sudoer: IKeyringPair;
-
-  before(async function () {
-    await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => {
-      sudoer = await privateKey('//Alice');
-    });
-  });
-
-  // At the moment there is no reliable way
-  // to establish the correspondence between the `ExecutedDownward` event
-  // and the relay's sent message due to `SetTopic` instruction
-  // containing an unpredictable topic silently added by the relay's messages on the router level.
-  // This changes the message hash on arrival to our chain.
-  //
-  // See:
-  // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83
-  // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36
-  //
-  // Because of this, we insert time gaps between tests so
-  // different `ExecutedDownward` events won't interfere with each other.
-  afterEach(async () => {
-    await usingPlaygrounds(async (helper) => {
-      await helper.wait.newBlocks(3);
-    });
-  });
-
-  itSub('The relay can set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain');
-  });
-
-  itSub('The relay can batch set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch');
-  });
-
-  itSub('The relay can batchAll set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll');
-  });
-
-  itSub('The relay can forceBatch set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch');
-  });
-
-  itSub('[negative] The relay cannot set balance', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain');
-  });
-
-  itSub('[negative] The relay cannot set balance via batch', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch');
-  });
-
-  itSub('[negative] The relay cannot set balance via batchAll', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll');
-  });
-
-  itSub('[negative] The relay cannot set balance via forceBatch', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch');
-  });
-
-  itSub('[negative] The relay cannot set balance via dispatchAs', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs');
-  });
-});
deletedjs-packages/tests/src/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/xcm/lowLevelXcmUnique.test.ts
+++ /dev/null
@@ -1,430 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import config from '../config.js';
-import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds} from '../util/index.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,  moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId} from './xcm.types.js';
-
-const testHelper = new XcmTestHelper('unique');
-
-
-
-
-describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      console.log(config.acalaUrl);
-      randomAccount = helper.arrange.createEmptyAccount();
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: UNIQUE_CHAIN,
-            },
-          },
-        },
-      };
-
-      const metadata = {
-        name: 'Unique Network',
-        symbol: 'UNQ',
-        decimals: 18,
-        minimalBalance: 1250_000_000_000_000_000n,
-      };
-      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>
-        hexToString(v.toJSON()['symbol'])) as string[];
-
-      if(!assets.includes('UNQ')) {
-        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
-      } else {
-        console.log('UNQ token already registered on Acala assetRegistry pallet');
-      }
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Acala', async () => {
-    await testHelper.sendUnqTo('acala', randomAccount);
-  });
-
-  itSub('Should connect to Acala and send UNQ back', async () => {
-    await testHelper.sendUnqBack('acala', alice, randomAccount);
-  });
-
-  itSub('Acala can send only up to its balance', async () => {
-    await testHelper.sendOnlyOwnedBalance('acala', alice);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Acala', async () => {
-    await testHelper.rejectReserveTransferUNQfrom('acala', alice);
-  });
-});
-
-describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      randomAccount = helper.arrange.createEmptyAccount();
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))
-        .toJSON() as [])
-        .map(nToBigInt).length != 0;
-      /*
-      Check whether the Unique token has been added
-      to the whitelist, since an error will occur
-      if it is added again. Needed for debugging
-      when this test is run multiple times.
-      */
-      if(isWhitelisted) {
-        console.log('UNQ token is already whitelisted on Polkadex');
-      } else {
-        await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);
-      }
-
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Polkadex', async () => {
-    await testHelper.sendUnqTo('polkadex', randomAccount);
-  });
-
-
-  itSub('Should connect to Polkadex and send UNQ back', async () => {
-    await testHelper.sendUnqBack('polkadex', alice, randomAccount);
-  });
-
-  itSub('Polkadex can send only up to its balance', async () => {
-    await testHelper.sendOnlyOwnedBalance('polkadex', alice);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => {
-    await testHelper.rejectReserveTransferUNQfrom('polkadex', alice);
-  });
-});
-
-// These tests are relevant only when
-// the the corresponding foreign assets are not registered
-describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => {
-  let alice: IKeyringPair;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-  });
-
-  itSub('Unique rejects ACA tokens from Acala', async () => {
-    await testHelper.rejectNativeTokensFrom('acala', alice);
-  });
-
-  itSub('Unique rejects GLMR tokens from Moonbeam', async () => {
-    await testHelper.rejectNativeTokensFrom('moonbeam', alice);
-  });
-
-  itSub('Unique rejects ASTR tokens from Astar', async () => {
-    await testHelper.rejectNativeTokensFrom('astar', alice);
-  });
-
-  itSub('Unique rejects PDX tokens from Polkadex', async () => {
-    await testHelper.rejectNativeTokensFrom('polkadex', alice);
-  });
-});
-
-describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => {
-  // Unique constants
-  let alice: IKeyringPair;
-  let uniqueAssetLocation;
-
-  let randomAccountUnique: IKeyringPair;
-  let randomAccountMoonbeam: IKeyringPair;
-
-  // Moonbeam constants
-  let assetId: string;
-
-  const uniqueAssetMetadata = {
-    name: 'xcUnique',
-    symbol: 'xcUNQ',
-    decimals: 18,
-    isFrozen: false,
-    minimalBalance: 1n,
-  };
-
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);
-
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const alithAccount = helper.account.alithAccount();
-      const baltatharAccount = helper.account.baltatharAccount();
-      const dorothyAccount = helper.account.dorothyAccount();
-
-      randomAccountMoonbeam = helper.account.create();
-
-      // >>> Sponsoring Dorothy >>>
-      console.log('Sponsoring Dorothy.......');
-      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring Dorothy.......DONE');
-      // <<< Sponsoring Dorothy <<<
-      uniqueAssetLocation = {
-        XCM: {
-          parents: 1,
-          interior: {X1: {Parachain: UNIQUE_CHAIN}},
-        },
-      };
-      const existentialDeposit = 1n;
-      const isSufficient = true;
-      const unitsPerSecond = 1n;
-      const numAssetsWeightHint = 0;
-
-      if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {
-        console.log('Unique asset already registered on Moonbeam');
-      } else {
-        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
-          location: uniqueAssetLocation,
-          metadata: uniqueAssetMetadata,
-          existentialDeposit,
-          isSufficient,
-          unitsPerSecond,
-          numAssetsWeightHint,
-        });
-
-        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
-
-        await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
-      }
-
-      // >>> Acquire Unique AssetId Info on Moonbeam >>>
-      console.log('Acquire Unique AssetId Info on Moonbeam.......');
-
-      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
-
-      console.log('UNQ asset ID is %s', assetId);
-      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');
-
-      // >>> Sponsoring random Account >>>
-      console.log('Sponsoring random Account.......');
-      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring random Account.......DONE');
-      // <<< Sponsoring random Account <<<
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Moonbeam', async () => {
-    await testHelper.sendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);
-  });
-
-  itSub('Should connect to Moonbeam and send UNQ back', async () => {
-    await testHelper.sendUnqBack('moonbeam', alice, randomAccountUnique);
-  });
-
-  itSub('Moonbeam can send only up to its balance', async () => {
-    await testHelper.sendOnlyOwnedBalance('moonbeam', alice);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => {
-    await testHelper.rejectReserveTransferUNQfrom('moonbeam', alice);
-  });
-});
-
-describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar
-  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar
-
-  // Unique -> Astar
-  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
-  const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      randomAccount = helper.arrange.createEmptyAccount();
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
-      console.log('randomAccount', randomAccount.address);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {
-        console.log('1. Create foreign asset and metadata');
-        await helper.getSudo().assets.forceCreate(
-          alice,
-          UNQ_ASSET_ID_ON_ASTAR,
-          alice.address,
-          UNQ_MINIMAL_BALANCE_ON_ASTAR,
-        );
-
-        await helper.assets.setMetadata(
-          alice,
-          UNQ_ASSET_ID_ON_ASTAR,
-          'Unique Network',
-          'UNQ',
-          Number(UNQ_DECIMALS),
-        );
-
-        console.log('2. Register asset location on Astar');
-        const assetLocation = {
-          V2: {
-            parents: 1,
-            interior: {
-              X1: {
-                Parachain: UNIQUE_CHAIN,
-              },
-            },
-          },
-        };
-
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
-
-        console.log('3. Set UNQ payment for XCM execution on Astar');
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
-      } else {
-        console.log('UNQ is already registered on Astar');
-      }
-      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Astar', async () => {
-    await testHelper.sendUnqTo('astar', randomAccount);
-  });
-
-  itSub('Should connect to Astar and send UNQ back', async () => {
-    await testHelper.sendUnqBack('astar', alice, randomAccount);
-  });
-
-  itSub('Astar can send only up to its balance', async () => {
-    await testHelper.sendOnlyOwnedBalance('astar', alice);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Astar', async () => {
-    await testHelper.rejectReserveTransferUNQfrom('astar', alice);
-  });
-});
-
-describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => {
-  let sudoer: IKeyringPair;
-
-  before(async function () {
-    await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => {
-      sudoer = await privateKey('//Alice');
-    });
-  });
-
-  // At the moment there is no reliable way
-  // to establish the correspondence between the `ExecutedDownward` event
-  // and the relay's sent message due to `SetTopic` instruction
-  // containing an unpredictable topic silently added by the relay's messages on the router level.
-  // This changes the message hash on arrival to our chain.
-  //
-  // See:
-  // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83
-  // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36
-  //
-  // Because of this, we insert time gaps between tests so
-  // different `ExecutedDownward` events won't interfere with each other.
-  afterEach(async () => {
-    await usingPlaygrounds(async (helper) => {
-      await helper.wait.newBlocks(3);
-    });
-  });
-
-  itSub('The relay can set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain');
-  });
-
-  itSub('The relay can batch set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch');
-  });
-
-  itSub('The relay can batchAll set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll');
-  });
-
-  itSub('The relay can forceBatch set storage', async () => {
-    await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch');
-  });
-
-  itSub('[negative] The relay cannot set balance', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain');
-  });
-
-  itSub('[negative] The relay cannot set balance via batch', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch');
-  });
-
-  itSub('[negative] The relay cannot set balance via batchAll', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll');
-  });
-
-  itSub('[negative] The relay cannot set balance via forceBatch', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch');
-  });
-
-  itSub('[negative] The relay cannot set balance via dispatchAs', async () => {
-    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs');
-  });
-});
deletedjs-packages/tests/src/xcm/xcm.types.tsdiffbeforeafterboth
--- a/js-packages/tests/src/xcm/xcm.types.ts
+++ /dev/null
@@ -1,621 +0,0 @@
-import type {IKeyringPair} from '@polkadot/types/types';
-import {hexToString} from '@polkadot/util';
-import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js';
-import {DevUniqueHelper, Event} from '@unique/playgrounds/src/unique.dev.js';
-import config from '../config.js';
-
-export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
-export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);
-export const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);
-export const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);
-export const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);
-export const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);
-
-export const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095);
-export const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000);
-export const KARURA_CHAIN = +(process.env.RELAY_KARURA_ID || 2000);
-export const MOONRIVER_CHAIN = +(process.env.RELAY_MOONRIVER_ID || 2023);
-export const SHIDEN_CHAIN = +(process.env.RELAY_SHIDEN_ID || 2007);
-
-export const relayUrl = config.relayUrl;
-export const statemintUrl = config.statemintUrl;
-export const statemineUrl = config.statemineUrl;
-
-export const acalaUrl = config.acalaUrl;
-export const moonbeamUrl = config.moonbeamUrl;
-export const astarUrl = config.astarUrl;
-export const polkadexUrl = config.polkadexUrl;
-
-export const karuraUrl = config.karuraUrl;
-export const moonriverUrl = config.moonriverUrl;
-export const shidenUrl = config.shidenUrl;
-
-export const SAFE_XCM_VERSION = 3;
-
-
-export const RELAY_DECIMALS = 12;
-export const STATEMINE_DECIMALS = 12;
-export const KARURA_DECIMALS = 12;
-export const SHIDEN_DECIMALS = 18n;
-export const QTZ_DECIMALS = 18n;
-
-export const ASTAR_DECIMALS = 18n;
-export const UNQ_DECIMALS = 18n;
-
-export const maxWaitBlocks = 6;
-
-export const uniqueMultilocation = {
-  parents: 1,
-  interior: {
-    X1: {
-      Parachain: UNIQUE_CHAIN,
-    },
-  },
-};
-export const uniqueVersionedMultilocation = {
-  V3: uniqueMultilocation,
-};
-
-export const uniqueAssetId = {
-  Concrete: uniqueMultilocation,
-};
-
-export const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
-  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-};
-export const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {
-  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
-         && event.outcome.isUntrustedReserveLocation);
-};
-
-export const expectDownwardXcmNoPermission = async (helper: DevUniqueHelper) => {
-  // The correct messageHash for downward messages can't be reliably obtained
-  await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.asIncomplete[1].isNoPermission);
-};
-
-export const expectDownwardXcmComplete = async (helper: DevUniqueHelper) => {
-  // The correct messageHash for downward messages can't be reliably obtained
-  await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.isComplete);
-};
-
-export const NETWORKS = {
-  acala: usingAcalaPlaygrounds,
-  astar: usingAstarPlaygrounds,
-  polkadex: usingPolkadexPlaygrounds,
-  moonbeam: usingMoonbeamPlaygrounds,
-  moonriver: usingMoonriverPlaygrounds,
-  karura: usingKaruraPlaygrounds,
-  shiden: usingShidenPlaygrounds,
-} as const;
-type NetworkNames = keyof typeof NETWORKS;
-
-type NativeRuntime = 'opal' | 'quartz' | 'unique';
-
-export function mapToChainId(networkName: keyof typeof NETWORKS): number {
-  switch (networkName) {
-    case 'acala':
-      return ACALA_CHAIN;
-    case 'astar':
-      return ASTAR_CHAIN;
-    case 'moonbeam':
-      return MOONBEAM_CHAIN;
-    case 'polkadex':
-      return POLKADEX_CHAIN;
-    case 'moonriver':
-      return MOONRIVER_CHAIN;
-    case 'karura':
-      return KARURA_CHAIN;
-    case 'shiden':
-      return SHIDEN_CHAIN;
-  }
-}
-
-export function mapToChainUrl(networkName: NetworkNames): string {
-  switch (networkName) {
-    case 'acala':
-      return acalaUrl;
-    case 'astar':
-      return astarUrl;
-    case 'moonbeam':
-      return moonbeamUrl;
-    case 'polkadex':
-      return polkadexUrl;
-    case 'moonriver':
-      return moonriverUrl;
-    case 'karura':
-      return karuraUrl;
-    case 'shiden':
-      return shidenUrl;
-  }
-}
-
-export function getDevPlayground(name: NetworkNames) {
-  return NETWORKS[name];
-}
-
-export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;
-export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;
-export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;
-export const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;
-export const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;
-
-export class XcmTestHelper {
-  private _balanceUniqueTokenInit: bigint = 0n;
-  private _balanceUniqueTokenMiddle: bigint = 0n;
-  private _balanceUniqueTokenFinal: bigint = 0n;
-  private _unqFees: bigint = 0n;
-  private _nativeRuntime: NativeRuntime;
-
-  constructor(runtime: NativeRuntime) {
-    this._nativeRuntime = runtime;
-  }
-
-  private _getNativeId() {
-    switch (this._nativeRuntime) {
-      case 'opal':
-        // To-Do
-        return 1001;
-      case 'quartz':
-        return QUARTZ_CHAIN;
-      case 'unique':
-        return UNIQUE_CHAIN;
-    }
-  }
-
-  private _isAddress20FormatFor(network: NetworkNames) {
-    switch (network) {
-      case 'moonbeam':
-      case 'moonriver':
-        return true;
-      default:
-        return false;
-    }
-  }
-
-  private _runtimeVersionedMultilocation() {
-    return {
-      V3: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: this._getNativeId(),
-          },
-        },
-      },
-    };
-  }
-
-  private _uniqueChainMultilocationForRelay() {
-    return {
-      V3: {
-        parents: 0,
-        interior: {
-          X1: {Parachain: this._getNativeId()},
-        },
-      },
-    };
-  }
-
-  async sendUnqTo(
-    networkName: keyof typeof NETWORKS,
-    randomAccount: IKeyringPair,
-    randomAccountOnTargetChain = randomAccount,
-  ) {
-    const networkUrl = mapToChainUrl(networkName);
-    const targetPlayground = getDevPlayground(networkName);
-    await usingPlaygrounds(async (helper) => {
-      this._balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: mapToChainId(networkName),
-            },
-          },
-        },
-      };
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {
-            X1: (
-              this._isAddress20FormatFor(networkName) ?
-                {
-                  AccountKey20: {
-                    network: 'Any',
-                    key: randomAccountOnTargetChain.address,
-                  },
-                }
-                :
-                {
-                  AccountId32: {
-                    network: 'Any',
-                    id: randomAccountOnTargetChain.addressRaw,
-                  },
-                }
-            ),
-          },
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT,
-            },
-          },
-        ],
-      };
-      const feeAssetItem = 0;
-
-      await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-      const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-      this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
-
-      this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
-      console.log('[%s -> %s] transaction fees: %s', this._nativeRuntime, networkName, helper.util.bigIntToDecimals(this._unqFees));
-      expect(this._unqFees > 0n, 'Negative fees, looks like nothing was transferred').to.be.true;
-
-      await targetPlayground(networkUrl, async (helper) => {
-      /*
-        Since only the parachain part of the Polkadex
-        infrastructure is launched (without their
-        solochain validators), processing incoming
-        assets will lead to an error.
-        This error indicates that the Polkadex chain
-        received a message from the Unique network,
-        since the hash is being checked to ensure
-        it matches what was sent.
-      */
-        if(networkName == 'polkadex') {
-          await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);
-        } else {
-          await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash);
-        }
-      });
-
-    });
-  }
-
-  async sendUnqBack(
-    networkName: keyof typeof NETWORKS,
-    sudoer: IKeyringPair,
-    randomAccountOnUnq: IKeyringPair,
-  ) {
-    const networkUrl = mapToChainUrl(networkName);
-
-    const targetPlayground = getDevPlayground(networkName);
-    await usingPlaygrounds(async (helper) => {
-
-      const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-        randomAccountOnUnq.addressRaw,
-        {
-          Concrete: {
-            parents: 1,
-            interior: {
-              X1: {Parachain: this._getNativeId()},
-            },
-          },
-        },
-        SENDBACK_AMOUNT,
-      );
-
-      let xcmProgramSent: any;
-
-
-      await targetPlayground(networkUrl, async (helper) => {
-        if('getSudo' in helper) {
-          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), xcmProgram);
-          xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        } else if('fastDemocracy' in helper) {
-          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), xcmProgram]);
-          // Needed to bypass the call filter.
-          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-          await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
-          xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        }
-      });
-
-      await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);
-
-      this._balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address);
-
-      expect(this._balanceUniqueTokenFinal).to.be.equal(this._balanceUniqueTokenInit - this._unqFees - STAYED_ON_TARGET_CHAIN);
-
-    });
-  }
-
-  async sendOnlyOwnedBalance(
-    networkName: keyof typeof NETWORKS,
-    sudoer: IKeyringPair,
-  ) {
-    const networkUrl = mapToChainUrl(networkName);
-    const targetPlayground = getDevPlayground(networkName);
-
-    const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS);
-
-    await usingPlaygrounds(async (helper) => {
-      const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName));
-      await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance);
-      const moreThanTargetChainHas = 2n * targetChainBalance;
-
-      const targetAccount = helper.arrange.createEmptyAccount();
-
-      const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-        targetAccount.addressRaw,
-        {
-          Concrete: {
-            parents: 0,
-            interior: 'Here',
-          },
-        },
-        moreThanTargetChainHas,
-      );
-
-      let maliciousXcmProgramSent: any;
-
-
-      await targetPlayground(networkUrl, async (helper) => {
-        if('getSudo' in helper) {
-          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgram);
-          maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        } else if('fastDemocracy' in helper) {
-          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgram]);
-          // Needed to bypass the call filter.
-          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-          await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
-          maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        }
-      });
-
-      await expectFailedToTransact(helper, maliciousXcmProgramSent);
-
-      const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-      expect(targetAccountBalance).to.be.equal(0n);
-    });
-  }
-
-  async rejectReserveTransferUNQfrom(networkName: keyof typeof NETWORKS, sudoer: IKeyringPair) {
-    const networkUrl = mapToChainUrl(networkName);
-    const targetPlayground = getDevPlayground(networkName);
-
-    await usingPlaygrounds(async (helper) => {
-      const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
-      const targetAccount = helper.arrange.createEmptyAccount();
-
-      const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-        targetAccount.addressRaw,
-        {
-          Concrete: {
-            parents: 1,
-            interior: {
-              X1: {
-                Parachain: this._getNativeId(),
-              },
-            },
-          },
-        },
-        testAmount,
-      );
-
-      const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-        targetAccount.addressRaw,
-        {
-          Concrete: {
-            parents: 0,
-            interior: 'Here',
-          },
-        },
-        testAmount,
-      );
-
-      let maliciousXcmProgramFullIdSent: any;
-      let maliciousXcmProgramHereIdSent: any;
-      const maxWaitBlocks = 3;
-
-      // Try to trick Unique using full UNQ identification
-      await targetPlayground(networkUrl, async (helper) => {
-        if('getSudo' in helper) {
-          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);
-          maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        }
-        // Moonbeam case
-        else if('fastDemocracy' in helper) {
-          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
-          // Needed to bypass the call filter.
-          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-          await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using path asset identification`,batchCall);
-
-          maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        }
-      });
-
-
-      await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);
-
-      let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-      expect(accountBalance).to.be.equal(0n);
-
-      // Try to trick Unique using shortened UNQ identification
-      await targetPlayground(networkUrl, async (helper) => {
-        if('getSudo' in helper) {
-          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId);
-          maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        }
-        else if('fastDemocracy' in helper) {
-          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId]);
-          // Needed to bypass the call filter.
-          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-          await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall);
-
-          maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        }
-      });
-
-      await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);
-
-      accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-      expect(accountBalance).to.be.equal(0n);
-    });
-  }
-
-  async rejectNativeTokensFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {
-    const networkUrl = mapToChainUrl(networkName);
-    const targetPlayground = getDevPlayground(networkName);
-    let messageSent: any;
-
-    await usingPlaygrounds(async (helper) => {
-      const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-        helper.arrange.createEmptyAccount().addressRaw,
-        {
-          Concrete: {
-            parents: 1,
-            interior: {
-              X1: {
-                Parachain: mapToChainId(networkName),
-              },
-            },
-          },
-        },
-        TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT,
-      );
-      await targetPlayground(networkUrl, async (helper) => {
-        if('getSudo' in helper) {
-          await helper.getSudo().xcm.send(sudoerOnTargetChain, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);
-          messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        } else if('fastDemocracy' in helper) {
-          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
-          // Needed to bypass the call filter.
-          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-          await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);
-
-          messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-        }
-      });
-      await expectFailedToTransact(helper, messageSent);
-    });
-  }
-
-  private async _relayXcmTransactSetStorage(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') {
-    // eslint-disable-next-line require-await
-    return await usingPlaygrounds(async (helper) => {
-      const relayForceKV = () => {
-        const random = Math.random();
-        const key = `relay-forced-key (instance: ${random})`;
-        const val = `relay-forced-value (instance: ${random})`;
-        const call = helper.constructApiCall('api.tx.system.setStorage', [[[key, val]]]).method.toHex();
-
-        return {
-          call,
-          key,
-          val,
-        };
-      };
-
-      if(variant == 'plain') {
-        const kv = relayForceKV();
-        return {
-          program: helper.arrange.makeUnpaidSudoTransactProgram({
-            weightMultiplier: 1,
-            call: kv.call,
-          }),
-          kvs: [kv],
-        };
-      } else {
-        const kv0 = relayForceKV();
-        const kv1 = relayForceKV();
-
-        const batchCall = helper.constructApiCall(`api.tx.utility.${variant}`, [[kv0.call, kv1.call]]).method.toHex();
-        return {
-          program: helper.arrange.makeUnpaidSudoTransactProgram({
-            weightMultiplier: 2,
-            call: batchCall,
-          }),
-          kvs: [kv0, kv1],
-        };
-      }
-    });
-  }
-
-  async relayIsPermittedToSetStorage(relaySudoer: IKeyringPair, variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') {
-    const {program, kvs} = await this._relayXcmTransactSetStorage(variant);
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [
-        this._uniqueChainMultilocationForRelay(),
-        program,
-      ]);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await expectDownwardXcmComplete(helper);
-
-      for(const kv of kvs) {
-        const forcedValue = await helper.callRpc('api.rpc.state.getStorage', [kv.key]);
-        expect(hexToString(forcedValue.toHex())).to.be.equal(kv.val);
-      }
-    });
-  }
-
-  private async _relayXcmTransactSetBalance(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs') {
-    // eslint-disable-next-line require-await
-    return await usingPlaygrounds(async (helper) => {
-      const emptyAccount = helper.arrange.createEmptyAccount().address;
-
-      const forceSetBalanceCall = helper.constructApiCall('api.tx.balances.forceSetBalance', [emptyAccount, 10_000n]).method.toHex();
-
-      let call;
-
-      if(variant == 'plain') {
-        call = forceSetBalanceCall;
-
-      } else if(variant == 'dispatchAs') {
-        call = helper.constructApiCall('api.tx.utility.dispatchAs', [
-          {
-            system: 'Root',
-          },
-          forceSetBalanceCall,
-        ]).method.toHex();
-      } else {
-        call = helper.constructApiCall(`api.tx.utility.${variant}`, [[forceSetBalanceCall]]).method.toHex();
-      }
-
-      return {
-        program: helper.arrange.makeUnpaidSudoTransactProgram({
-          weightMultiplier: 1,
-          call,
-        }),
-        emptyAccount,
-      };
-    });
-  }
-
-  async relayIsNotPermittedToSetBalance(
-    relaySudoer: IKeyringPair,
-    variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs',
-  ) {
-    const {program, emptyAccount} = await this._relayXcmTransactSetBalance(variant);
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [
-        this._uniqueChainMultilocationForRelay(),
-        program,
-      ]);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await expectDownwardXcmNoPermission(helper);
-      expect(await helper.balance.getSubstrate(emptyAccount)).to.be.equal(0n);
-    });
-  }
-}
deletedjs-packages/tests/src/xcm/xcmOpal.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/xcm/xcmOpal.test.ts
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 type {IKeyringPair} from '@polkadot/types/types';
-import config from '../config.js';
-import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';
-
-const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);
-const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095);
-
-const relayUrl = config.relayUrl;
-const westmintUrl = config.westmintUrl;
-
-const STATEMINE_PALLET_INSTANCE = 50;
-const ASSET_ID = 100;
-const ASSET_METADATA_DECIMALS = 18;
-const ASSET_METADATA_NAME = 'USDT';
-const ASSET_METADATA_DESCRIPTION = 'USDT';
-const ASSET_METADATA_MINIMAL_BALANCE = 1n;
-
-const RELAY_DECIMALS = 12;
-const WESTMINT_DECIMALS = 12;
-
-const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;
-
-// 10,000.00 (ten thousands) USDT
-const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;
-
-describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  let balanceStmnBefore: bigint;
-  let balanceStmnAfter: bigint;
-
-  let balanceOpalBefore: bigint;
-  let balanceOpalAfter: bigint;
-  let balanceOpalFinal: bigint;
-
-  let balanceBobBefore: bigint;
-  let balanceBobAfter: bigint;
-  let balanceBobFinal: bigint;
-
-  let balanceBobRelayTokenBefore: bigint;
-  let balanceBobRelayTokenAfter: bigint;
-
-
-  before(async () => {
-    await usingPlaygrounds(async (_helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      bob = await privateKey('//Bob'); // funds donor
-    });
-
-    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
-      // 350.00 (three hundred fifty) DOT
-      const fundingAmount = 3_500_000_000_000n;
-
-      await helper.assets.create(alice, ASSET_ID, alice.address, ASSET_METADATA_MINIMAL_BALANCE);
-      await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);
-      await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT);
-
-      // funding parachain sovereing account (Parachain: 2095)
-      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);
-      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount);
-    });
-
-
-    await usingPlaygrounds(async (helper) => {
-      const location = {
-        V2: {
-          parents: 1,
-          interior: {X3: [
-            {
-              Parachain: STATEMINE_CHAIN,
-            },
-            {
-              PalletInstance: STATEMINE_PALLET_INSTANCE,
-            },
-            {
-              GeneralIndex: ASSET_ID,
-            },
-          ]},
-        },
-      };
-
-      const metadata =
-      {
-        name: ASSET_ID,
-        symbol: ASSET_METADATA_NAME,
-        decimals: ASSET_METADATA_DECIMALS,
-        minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,
-      };
-      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
-      balanceOpalBefore = await helper.balance.getSubstrate(alice.address);
-    });
-
-
-    // Providing the relay currency to the unique sender account
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: 50_000_000_000_000_000n,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    });
-
-  });
-
-  itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => {
-    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
-      const dest = {
-        V2: {
-          parents: 1,
-          interior: {X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: {
-                  X2: [
-                    {
-                      PalletInstance: STATEMINE_PALLET_INSTANCE,
-                    },
-                    {
-                      GeneralIndex: ASSET_ID,
-                    },
-                  ]},
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
-      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
-
-      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
-
-      // common good parachain take commission in it native token
-      console.log(
-        '[Westmint -> Opal] transaction fees on Westmint: %s WND',
-        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),
-      );
-      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
-
-    });
-
-
-    // ensure that asset has been delivered
-    await helper.wait.newBlocks(3);
-
-    // expext collection id will be with id 1
-    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
-
-    balanceOpalAfter = await helper.balance.getSubstrate(alice.address);
-
-    console.log(
-      '[Westmint -> Opal] transaction fees on Opal: %s USDT',
-      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS),
-    );
-    console.log(
-      '[Westmint -> Opal] transaction fees on Opal: %s OPL',
-      helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore),
-    );
-
-    // commission has not paid in USDT token
-    expect(free == TRANSFER_AMOUNT).to.be.true;
-    // ... and parachain native token
-    expect(balanceOpalAfter == balanceOpalBefore).to.be.true;
-  });
-
-  itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {X2: [
-          {
-            Parachain: STATEMINE_CHAIN,
-          },
-          {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          },
-        ]},
-      },
-    };
-
-    const currencies: [any, bigint][] = [
-      [
-        {
-          ForeignAssetId: 0,
-        },
-        //10_000_000_000_000_000n,
-        TRANSFER_AMOUNT,
-      ],
-      [
-        {
-          NativeAssetId: 'Parent',
-        },
-        400_000_000_000_000n,
-      ],
-    ];
-
-    const feeItem = 1;
-
-    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
-
-    // the commission has been paid in parachain native token
-    balanceOpalFinal = await helper.balance.getSubstrate(alice.address);
-    expect(balanceOpalAfter > balanceOpalFinal).to.be.true;
-
-    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-
-      // The USDT token never paid fees. Its amount not changed from begin value.
-      // Also check that xcm transfer has been succeeded
-      expect((await helper.assets.account(ASSET_ID, alice.address))! == ASSET_AMOUNT).to.be.true;
-    });
-  });
-
-  itSub('Should connect and send Relay token to Unique', async ({helper}) => {
-    const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
-
-    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
-    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
-
-    // Providing the relay currency to the unique sender account
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: bob.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT_RELAY,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    });
-
-    await helper.wait.newBlocks(3);
-
-    balanceBobAfter = await helper.balance.getSubstrate(bob.address);
-    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
-
-    const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
-    console.log(
-      'Relay (Westend) to Opal transaction fees: %s OPL',
-      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
-    );
-    console.log(
-      'Relay (Westend) to Opal transaction fees: %s WND',
-      helper.util.bigIntToDecimals(wndFee, WESTMINT_DECIMALS),
-    );
-    expect(balanceBobBefore == balanceBobAfter).to.be.true;
-    expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;
-  });
-
-  itSub('Should connect and send Relay token back', async ({helper}) => {
-    let relayTokenBalanceBefore: bigint;
-    let relayTokenBalanceAfter: bigint;
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    });
-
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1:{
-            AccountId32: {
-              network: 'Any',
-              id: bob.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const currencies: any = [
-      [
-        {
-          NativeAssetId: 'Parent',
-        },
-        50_000_000_000_000_000n,
-      ],
-    ];
-
-    const feeItem = 0;
-
-    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
-
-    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
-    console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      await helper.wait.newBlocks(10);
-      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
-      console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
-      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
-    });
-  });
-});
deletedjs-packages/tests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/xcm/xcmQuartz.test.ts
+++ /dev/null
@@ -1,1636 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 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/src/unique.dev.js';
-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';
-
-
-
-const STATEMINE_PALLET_INSTANCE = 50;
-
-const TRANSFER_AMOUNT = 2000000000000000000000000n;
-
-const FUNDING_AMOUNT = 3_500_000_0000_000_000n;
-
-const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
-
-const USDT_ASSET_ID = 100;
-const USDT_ASSET_METADATA_DECIMALS = 18;
-const USDT_ASSET_METADATA_NAME = 'USDT';
-const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
-const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
-const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
-
-const SAFE_XCM_VERSION = 2;
-
-describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  let balanceStmnBefore: bigint;
-  let balanceStmnAfter: bigint;
-
-  let balanceQuartzBefore: bigint;
-  let balanceQuartzAfter: bigint;
-  let balanceQuartzFinal: bigint;
-
-  let balanceBobBefore: bigint;
-  let balanceBobAfter: bigint;
-  let balanceBobFinal: bigint;
-
-  let balanceBobRelayTokenBefore: bigint;
-  let balanceBobRelayTokenAfter: bigint;
-
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      // Fund accounts on Statemine(t)
-      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
-      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
-    });
-
-    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
-      const sovereignFundingAmount = 3_500_000_000n;
-
-      await helper.assets.create(
-        alice,
-        USDT_ASSET_ID,
-        alice.address,
-        USDT_ASSET_METADATA_MINIMAL_BALANCE,
-      );
-      await helper.assets.setMetadata(
-        alice,
-        USDT_ASSET_ID,
-        USDT_ASSET_METADATA_NAME,
-        USDT_ASSET_METADATA_DESCRIPTION,
-        USDT_ASSET_METADATA_DECIMALS,
-      );
-      await helper.assets.mint(
-        alice,
-        USDT_ASSET_ID,
-        alice.address,
-        USDT_ASSET_AMOUNT,
-      );
-
-      // funding parachain sovereing account on Statemine(t).
-      // The sovereign account should be created before any action
-      // (the assets pallet on Statemine(t) check if the sovereign account exists)
-      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);
-      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
-    });
-
-
-    await usingPlaygrounds(async (helper) => {
-      const location = {
-        V2: {
-          parents: 1,
-          interior: {X3: [
-            {
-              Parachain: STATEMINE_CHAIN,
-            },
-            {
-              PalletInstance: STATEMINE_PALLET_INSTANCE,
-            },
-            {
-              GeneralIndex: USDT_ASSET_ID,
-            },
-          ]},
-        },
-      };
-
-      const metadata =
-      {
-        name: USDT_ASSET_ID,
-        symbol: USDT_ASSET_METADATA_NAME,
-        decimals: USDT_ASSET_METADATA_DECIMALS,
-        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
-      };
-      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
-      balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);
-    });
-
-
-    // Providing the relay currency to the quartz sender account
-    // (fee for USDT XCM are paid in relay tokens)
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: QUARTZ_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT_RELAY,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    });
-
-  });
-
-  itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {
-    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
-      const dest = {
-        V2: {
-          parents: 1,
-          interior: {X1: {
-            Parachain: QUARTZ_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: {
-                  X2: [
-                    {
-                      PalletInstance: STATEMINE_PALLET_INSTANCE,
-                    },
-                    {
-                      GeneralIndex: USDT_ASSET_ID,
-                    },
-                  ]},
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
-      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
-
-      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
-
-      // common good parachain take commission in it native token
-      console.log(
-        '[Statemine -> Quartz] transaction fees on Statemine: %s WND',
-        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),
-      );
-      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
-
-    });
-
-
-    // ensure that asset has been delivered
-    await helper.wait.newBlocks(3);
-
-    // expext collection id will be with id 1
-    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
-
-    balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);
-
-    console.log(
-      '[Statemine -> Quartz] transaction fees on Quartz: %s USDT',
-      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
-    );
-    console.log(
-      '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ',
-      helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),
-    );
-    // commission has not paid in USDT token
-    expect(free).to.be.equal(TRANSFER_AMOUNT);
-    // ... and parachain native token
-    expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;
-  });
-
-  itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {X2: [
-          {
-            Parachain: STATEMINE_CHAIN,
-          },
-          {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          },
-        ]},
-      },
-    };
-
-    const relayFee = 400_000_000_000_000n;
-    const currencies: [any, bigint][] = [
-      [
-        {
-          ForeignAssetId: 0,
-        },
-        TRANSFER_AMOUNT,
-      ],
-      [
-        {
-          NativeAssetId: 'Parent',
-        },
-        relayFee,
-      ],
-    ];
-
-    const feeItem = 1;
-
-    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
-
-    // the commission has been paid in parachain native token
-    balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);
-    console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzFinal));
-    expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;
-
-    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-
-      // The USDT token never paid fees. Its amount not changed from begin value.
-      // Also check that xcm transfer has been succeeded
-      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
-    });
-  });
-
-  itSub('Should connect and send Relay token to Quartz', async ({helper}) => {
-    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
-    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: QUARTZ_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: bob.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT_RELAY,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    });
-
-    await helper.wait.newBlocks(3);
-
-    balanceBobAfter = await helper.balance.getSubstrate(bob.address);
-    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
-
-    const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
-    const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
-    console.log(
-      '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',
-      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
-    );
-    console.log(
-      '[Relay (Westend) -> Quartz] transaction fees: %s WND',
-      helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),
-    );
-    console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz);
-    expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;
-    expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;
-  });
-
-  itSub('Should connect and send Relay token back', async ({helper}) => {
-    let relayTokenBalanceBefore: bigint;
-    let relayTokenBalanceAfter: bigint;
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    });
-
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1:{
-            AccountId32: {
-              network: 'Any',
-              id: bob.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const currencies: any = [
-      [
-        {
-          NativeAssetId: 'Parent',
-        },
-        TRANSFER_AMOUNT_RELAY,
-      ],
-    ];
-
-    const feeItem = 0;
-
-    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
-
-    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
-    console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      await helper.wait.newBlocks(10);
-      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
-      console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
-      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
-    });
-  });
-});
-
-describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  let balanceQuartzTokenInit: bigint;
-  let balanceQuartzTokenMiddle: bigint;
-  let balanceQuartzTokenFinal: bigint;
-  let balanceKaruraTokenInit: bigint;
-  let balanceKaruraTokenMiddle: bigint;
-  let balanceKaruraTokenFinal: bigint;
-  let balanceQuartzForeignTokenInit: bigint;
-  let balanceQuartzForeignTokenMiddle: bigint;
-  let balanceQuartzForeignTokenFinal: bigint;
-
-  // computed by a test transfer from prod Quartz to prod Karura.
-  // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e9
-  // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block)
-  const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n;
-  const karuraEps = 8n * 10n ** 16n;
-
-  let karuraBackwardTransferAmount: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: QUARTZ_CHAIN,
-            },
-          },
-        },
-      };
-
-      const metadata = {
-        name: 'Quartz',
-        symbol: 'QTZ',
-        decimals: 18,
-        minimalBalance: 1000000000000000000n,
-      };
-
-      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
-        hexToString(v.toJSON()['symbol'])) as string[];
-
-      if(!assets.includes('QTZ')) {
-        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
-      } else {
-        console.log('QTZ token already registered on Karura assetRegistry pallet');
-      }
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
-      balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);
-      balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
-      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);
-    });
-  });
-
-  itSub('Should connect and send QTZ to Karura', async ({helper}) => {
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: KARURA_CHAIN,
-          },
-        },
-      },
-    };
-
-    const beneficiary = {
-      V2: {
-        parents: 0,
-        interior: {
-          X1: {
-            AccountId32: {
-              network: 'Any',
-              id: randomAccount.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const assets = {
-      V2: [
-        {
-          id: {
-            Concrete: {
-              parents: 0,
-              interior: 'Here',
-            },
-          },
-          fun: {
-            Fungible: TRANSFER_AMOUNT,
-          },
-        },
-      ],
-    };
-
-    const feeAssetItem = 0;
-
-    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
-
-    const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
-    expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
-    console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
-
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-
-      balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
-      balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
-
-      const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;
-      const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;
-      karuraBackwardTransferAmount = qtzIncomeTransfer;
-
-      const karUnqFees = TRANSFER_AMOUNT - qtzIncomeTransfer;
-
-      console.log(
-        '[Quartz -> Karura] transaction fees on Karura: %s KAR',
-        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),
-      );
-      console.log(
-        '[Quartz -> Karura] transaction fees on Karura: %s QTZ',
-        helper.util.bigIntToDecimals(karUnqFees),
-      );
-      console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));
-      expect(karFees == 0n).to.be.true;
-
-      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;
-
-      expect(
-        bigintAbs(karUnqFees - expectedKaruraIncomeFee) < karuraEps,
-        'Karura took different income fee, check the Karura foreign asset config',
-      ).to.be.true;
-    });
-  });
-
-  itSub('Should connect to Karura and send QTZ back', async ({helper}) => {
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X2: [
-              {Parachain: QUARTZ_CHAIN},
-              {
-                AccountId32: {
-                  network: 'Any',
-                  id: randomAccount.addressRaw,
-                },
-              },
-            ],
-          },
-        },
-      };
-
-      const id = {
-        ForeignAsset: 0,
-      };
-
-      await helper.xTokens.transfer(randomAccount, id, karuraBackwardTransferAmount, destination, 'Unlimited');
-      balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
-      balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
-
-      const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;
-      const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;
-
-      console.log(
-        '[Karura -> Quartz] transaction fees on Karura: %s KAR',
-        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),
-      );
-      console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));
-
-      expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;
-      expect(qtzOutcomeTransfer == karuraBackwardTransferAmount).to.be.true;
-    });
-
-    await helper.wait.newBlocks(3);
-
-    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
-    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;
-    expect(actuallyDelivered > 0).to.be.true;
-
-    console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));
-
-    const qtzFees = karuraBackwardTransferAmount - actuallyDelivered;
-    console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
-    expect(qtzFees == 0n).to.be.true;
-  });
-
-  itSub('Karura can send only up to its balance', async ({helper}) => {
-    // set Karura's sovereign account's balance
-    const karuraBalance = 10000n * (10n ** QTZ_DECIMALS);
-    const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN);
-    await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance);
-
-    const moreThanKaruraHas = karuraBalance * 2n;
-
-    let targetAccountBalance = 0n;
-    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
-    const quartzMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {Parachain: QUARTZ_CHAIN},
-        },
-      },
-    };
-
-    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      moreThanKaruraHas,
-    );
-
-    let maliciousXcmProgramSent: any;
-    const maxWaitBlocks = 5;
-
-    // Try to trick Quartz
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
-
-      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(0n);
-
-    // But Karura still can send the correct amount
-    const validTransferAmount = karuraBalance / 2n;
-    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      validTransferAmount,
-    );
-
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
-    });
-
-    await helper.wait.newBlocks(maxWaitBlocks);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(validTransferAmount);
-  });
-
-  itSub('Should not accept reserve transfer of QTZ from Karura', async ({helper}) => {
-    const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
-    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
-
-    const quartzMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: QUARTZ_CHAIN,
-          },
-        },
-      },
-    };
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: QUARTZ_CHAIN,
-            },
-          },
-        },
-      },
-      testAmount,
-    );
-
-    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      testAmount,
-    );
-
-    let maliciousXcmProgramFullIdSent: any;
-    let maliciousXcmProgramHereIdSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Quartz using full QTZ identification
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);
-
-      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-
-    // Try to trick Quartz using shortened QTZ identification
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);
-
-      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-  });
-});
-
-// These tests are relevant only when
-// the the corresponding foreign assets are not registered
-describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {
-  let alice: IKeyringPair;
-  let alith: IKeyringPair;
-
-  const testAmount = 100_000_000_000n;
-  let quartzParachainJunction;
-  let quartzAccountJunction;
-
-  let quartzParachainMultilocation: any;
-  let quartzAccountMultilocation: any;
-  let quartzCombinedMultilocation: any;
-
-  let messageSent: any;
-
-  const maxWaitBlocks = 3;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-
-      quartzParachainJunction = {Parachain: QUARTZ_CHAIN};
-      quartzAccountJunction = {
-        AccountId32: {
-          network: 'Any',
-          id: alice.addressRaw,
-        },
-      };
-
-      quartzParachainMultilocation = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: quartzParachainJunction,
-          },
-        },
-      };
-
-      quartzAccountMultilocation = {
-        V2: {
-          parents: 0,
-          interior: {
-            X1: quartzAccountJunction,
-          },
-        },
-      };
-
-      quartzCombinedMultilocation = {
-        V2: {
-          parents: 1,
-          interior: {
-            X2: [quartzParachainJunction, quartzAccountJunction],
-          },
-        },
-      };
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    // eslint-disable-next-line require-await
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      alith = helper.account.alithAccount();
-    });
-  });
-
-  const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-  };
-
-  itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
-    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
-      const id = {
-        Token: 'KAR',
-      };
-      const destination = quartzCombinedMultilocation;
-      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');
-
-      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, messageSent);
-  });
-
-  itSub('Quartz rejects MOVR tokens from Moonriver', async ({helper}) => {
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const id = 'SelfReserve';
-      const destination = quartzCombinedMultilocation;
-      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');
-
-      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, messageSent);
-  });
-
-  itSub('Quartz rejects SDN tokens from Shiden', async ({helper}) => {
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      const destinationParachain = quartzParachainMultilocation;
-      const beneficiary = quartzAccountMultilocation;
-      const assets = {
-        V2: [{
-          id: {
-            Concrete: {
-              parents: 0,
-              interior: 'Here',
-            },
-          },
-          fun: {
-            Fungible: testAmount,
-          },
-        }],
-      };
-      const feeAssetItem = 0;
-
-      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [
-        destinationParachain,
-        beneficiary,
-        assets,
-        feeAssetItem,
-      ]);
-
-      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, messageSent);
-  });
-});
-
-describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {
-  // Quartz constants
-  let alice: IKeyringPair;
-  let quartzAssetLocation;
-
-  let randomAccountQuartz: IKeyringPair;
-  let randomAccountMoonriver: IKeyringPair;
-
-  // Moonriver constants
-  let assetId: string;
-
-  const quartzAssetMetadata = {
-    name: 'xcQuartz',
-    symbol: 'xcQTZ',
-    decimals: 18,
-    isFrozen: false,
-    minimalBalance: 1n,
-  };
-
-  let balanceQuartzTokenInit: bigint;
-  let balanceQuartzTokenMiddle: bigint;
-  let balanceQuartzTokenFinal: bigint;
-  let balanceForeignQtzTokenInit: bigint;
-  let balanceForeignQtzTokenMiddle: bigint;
-  let balanceForeignQtzTokenFinal: bigint;
-  let balanceMovrTokenInit: bigint;
-  let balanceMovrTokenMiddle: bigint;
-  let balanceMovrTokenFinal: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
-
-      balanceForeignQtzTokenInit = 0n;
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const alithAccount = helper.account.alithAccount();
-      const baltatharAccount = helper.account.baltatharAccount();
-      const dorothyAccount = helper.account.dorothyAccount();
-
-      randomAccountMoonriver = helper.account.create();
-
-      // >>> Sponsoring Dorothy >>>
-      console.log('Sponsoring Dorothy.......');
-      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring Dorothy.......DONE');
-      // <<< Sponsoring Dorothy <<<
-
-      quartzAssetLocation = {
-        XCM: {
-          parents: 1,
-          interior: {X1: {Parachain: QUARTZ_CHAIN}},
-        },
-      };
-      const existentialDeposit = 1n;
-      const isSufficient = true;
-      const unitsPerSecond = 1n;
-      const numAssetsWeightHint = 0;
-
-      if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
-        console.log('Quartz asset already registered on Moonriver');
-      } else {
-        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
-          location: quartzAssetLocation,
-          metadata: quartzAssetMetadata,
-          existentialDeposit,
-          isSufficient,
-          unitsPerSecond,
-          numAssetsWeightHint,
-        });
-
-        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
-
-        await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
-      }
-      // >>> Acquire Quartz AssetId Info on Moonriver >>>
-      console.log('Acquire Quartz AssetId Info on Moonriver.......');
-
-      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();
-
-      console.log('QTZ asset ID is %s', assetId);
-      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');
-      // >>> Acquire Quartz AssetId Info on Moonriver >>>
-
-      // >>> Sponsoring random Account >>>
-      console.log('Sponsoring random Account.......');
-      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring random Account.......DONE');
-      // <<< Sponsoring random Account <<<
-
-      balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);
-      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);
-    });
-  });
-
-  itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {
-    const currencyId = {
-      NativeAssetId: 'Here',
-    };
-    const dest = {
-      V2: {
-        parents: 1,
-        interior: {
-          X2: [
-            {Parachain: MOONRIVER_CHAIN},
-            {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},
-          ],
-        },
-      },
-    };
-    const amount = TRANSFER_AMOUNT;
-
-    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited');
-
-    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);
-    expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;
-
-    const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
-    console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));
-    expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
-
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-
-      balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);
-
-      const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;
-      console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));
-      expect(movrFees == 0n).to.be.true;
-
-      balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);
-      const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;
-      console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));
-      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;
-    });
-  });
-
-  itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const asset = {
-        V2: {
-          id: {
-            Concrete: {
-              parents: 1,
-              interior: {
-                X1: {Parachain: QUARTZ_CHAIN},
-              },
-            },
-          },
-          fun: {
-            Fungible: TRANSFER_AMOUNT,
-          },
-        },
-      };
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X2: [
-              {Parachain: QUARTZ_CHAIN},
-              {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},
-            ],
-          },
-        },
-      };
-
-      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited');
-
-      balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);
-
-      const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;
-      console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));
-      expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;
-
-      const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);
-
-      expect(qtzRandomAccountAsset).to.be.null;
-
-      balanceForeignQtzTokenFinal = 0n;
-
-      const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;
-      console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));
-      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
-    });
-
-    await helper.wait.newBlocks(3);
-
-    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);
-    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;
-    expect(actuallyDelivered > 0).to.be.true;
-
-    console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));
-
-    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;
-    console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
-    expect(qtzFees == 0n).to.be.true;
-  });
-
-  itSub('Moonriver can send only up to its balance', async ({helper}) => {
-    // set Moonriver's sovereign account's balance
-    const moonriverBalance = 10000n * (10n ** QTZ_DECIMALS);
-    const moonriverSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONRIVER_CHAIN);
-    await helper.getSudo().balance.setBalanceSubstrate(alice, moonriverSovereignAccount, moonriverBalance);
-
-    const moreThanMoonriverHas = moonriverBalance * 2n;
-
-    let targetAccountBalance = 0n;
-    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
-    const quartzMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {Parachain: QUARTZ_CHAIN},
-        },
-      },
-    };
-
-    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      moreThanMoonriverHas,
-    );
-
-    let maliciousXcmProgramSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Quartz
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgram]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('try to spend more QTZ than Moonriver has', batchCall);
-
-      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(0n);
-
-    // But Moonriver still can send the correct amount
-    const validTransferAmount = moonriverBalance / 2n;
-    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      validTransferAmount,
-    );
-
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, validXcmProgram]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('Spend the correct amount of QTZ', batchCall);
-    });
-
-    await helper.wait.newBlocks(maxWaitBlocks);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(validTransferAmount);
-  });
-
-  itSub('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => {
-    const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
-    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
-
-    const quartzMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: QUARTZ_CHAIN,
-          },
-        },
-      },
-    };
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: {
-            X1: {
-              Parachain: QUARTZ_CHAIN,
-            },
-          },
-        },
-      },
-      testAmount,
-    );
-
-    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      testAmount,
-    );
-
-    let maliciousXcmProgramFullIdSent: any;
-    let maliciousXcmProgramHereIdSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Quartz using full QTZ identification
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramFullId]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using path asset identification', batchCall);
-
-      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-
-    // Try to trick Quartz using shortened QTZ identification
-    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramHereId]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using "here" asset identification', batchCall);
-
-      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-  });
-});
-
-describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {
-  let alice: IKeyringPair;
-  let sender: IKeyringPair;
-
-  const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden
-  const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden
-
-  // Quartz -> Shiden
-  const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden
-  const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden
-  const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ
-  const qtzToShidenArrived = 7_998_196_000_000_000_000n; // 7.99 ... QTZ, Shiden takes a commision in foreign tokens
-
-  // Shiden -> Quartz
-  const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ
-  const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 2.99 ... QTZ
-
-  let balanceAfterQuartzToShidenXCM: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [sender] = await helper.arrange.createAccounts([100n], alice);
-      console.log('sender', sender.address);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
-        console.log('1. Create foreign asset and metadata');
-        await helper.getSudo().assets.forceCreate(
-          alice,
-          QTZ_ASSET_ID_ON_SHIDEN,
-          alice.address,
-          QTZ_MINIMAL_BALANCE_ON_SHIDEN,
-        );
-
-        await helper.assets.setMetadata(
-          alice,
-          QTZ_ASSET_ID_ON_SHIDEN,
-          'Quartz',
-          'QTZ',
-          Number(QTZ_DECIMALS),
-        );
-
-        console.log('2. Register asset location on Shiden');
-        const assetLocation = {
-          V2: {
-            parents: 1,
-            interior: {
-              X1: {
-                Parachain: QUARTZ_CHAIN,
-              },
-            },
-          },
-        };
-
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
-
-        console.log('3. Set QTZ payment for XCM execution on Shiden');
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
-      } else {
-        console.log('QTZ is already registered on Shiden');
-      }
-      console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
-      await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);
-    });
-  });
-
-  itSub('Should connect and send QTZ to Shiden', async ({helper}) => {
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: SHIDEN_CHAIN,
-          },
-        },
-      },
-    };
-
-    const beneficiary = {
-      V2: {
-        parents: 0,
-        interior: {
-          X1: {
-            AccountId32: {
-              network: 'Any',
-              id: sender.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const assets = {
-      V2: [
-        {
-          id: {
-            Concrete: {
-              parents: 0,
-              interior: 'Here',
-            },
-          },
-          fun: {
-            Fungible: qtzToShidenTransferred,
-          },
-        },
-      ],
-    };
-
-    // Initial balance is 100 QTZ
-    const balanceBefore = await helper.balance.getSubstrate(sender.address);
-    console.log(`Initial balance is: ${balanceBefore}`);
-
-    const feeAssetItem = 0;
-    await helper.xcm.limitedReserveTransferAssets(sender, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-
-    // Balance after reserve transfer is less than 90
-    balanceAfterQuartzToShidenXCM = await helper.balance.getSubstrate(sender.address);
-    console.log(`QTZ Balance on Quartz after XCM is: ${balanceAfterQuartzToShidenXCM}`);
-    console.log(`Quartz's QTZ commission is: ${balanceBefore - balanceAfterQuartzToShidenXCM}`);
-    expect(balanceBefore - balanceAfterQuartzToShidenXCM > 0).to.be.true;
-
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-      const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address);
-      const shidenBalance = await helper.balance.getSubstrate(sender.address);
-
-      console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`);
-      console.log(`Shiden's QTZ commission is: ${qtzToShidenTransferred - xcQTZbalance!}`);
-
-      expect(xcQTZbalance).to.eq(qtzToShidenArrived);
-      // SHD balance does not changed:
-      expect(shidenBalance).to.eq(shidenInitialBalance);
-    });
-  });
-
-  itSub('Should connect to Shiden and send QTZ back', async ({helper}) => {
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: QUARTZ_CHAIN,
-            },
-          },
-        },
-      };
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {
-            X1: {
-              AccountId32: {
-                network: 'Any',
-                id: sender.addressRaw,
-              },
-            },
-          },
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 1,
-                interior: {
-                  X1: {
-                    Parachain: QUARTZ_CHAIN,
-                  },
-                },
-              },
-            },
-            fun: {
-              Fungible: qtzFromShidenTransfered,
-            },
-          },
-        ],
-      };
-
-      // Initial balance is 1 SDN
-      const balanceSDNbefore = await helper.balance.getSubstrate(sender.address);
-      console.log(`SDN balance is: ${balanceSDNbefore}, it does not changed`);
-      expect(balanceSDNbefore).to.eq(shidenInitialBalance);
-
-      const feeAssetItem = 0;
-      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw
-      await helper.executeExtrinsic(sender, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);
-
-      // Balance after reserve transfer is less than 1 SDN
-      const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address);
-      const balanceSDN = await helper.balance.getSubstrate(sender.address);
-      console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`);
-
-      // Assert: xcQTZ balance correctly decreased
-      expect(xcQTZbalance).to.eq(qtzOnShidenLeft);
-      // Assert: SDN balance is 0.996...
-      expect(balanceSDN / (10n ** (SHIDEN_DECIMALS - 3n))).to.eq(996n);
-    });
-
-    await helper.wait.newBlocks(3);
-    const balanceQTZ = await helper.balance.getSubstrate(sender.address);
-    console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`);
-    expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered);
-  });
-
-  itSub('Shiden can send only up to its balance', async ({helper}) => {
-    // set Shiden's sovereign account's balance
-    const shidenBalance = 10000n * (10n ** QTZ_DECIMALS);
-    const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN);
-    await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance);
-
-    const moreThanShidenHas = shidenBalance * 2n;
-
-    let targetAccountBalance = 0n;
-    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
-    const quartzMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {Parachain: QUARTZ_CHAIN},
-        },
-      },
-    };
-
-    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      moreThanShidenHas,
-    );
-
-    let maliciousXcmProgramSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Quartz
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
-
-      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(0n);
-
-    // But Shiden still can send the correct amount
-    const validTransferAmount = shidenBalance / 2n;
-    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      validTransferAmount,
-    );
-
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
-    });
-
-    await helper.wait.newBlocks(maxWaitBlocks);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(validTransferAmount);
-  });
-
-  itSub('Should not accept reserve transfer of QTZ from Shiden', async ({helper}) => {
-    const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
-    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
-
-    const quartzMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: QUARTZ_CHAIN,
-          },
-        },
-      },
-    };
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: QUARTZ_CHAIN,
-            },
-          },
-        },
-      },
-      testAmount,
-    );
-
-    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      testAmount,
-    );
-
-    let maliciousXcmProgramFullIdSent: any;
-    let maliciousXcmProgramHereIdSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Quartz using full QTZ identification
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);
-
-      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-
-    // Try to trick Quartz using shortened QTZ identification
-    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);
-
-      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-  });
-});
deletedjs-packages/tests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
--- a/js-packages/tests/src/xcm/xcmUnique.test.ts
+++ /dev/null
@@ -1,1834 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// 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 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/src/unique.dev.js';
-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';
-
-
-const STATEMINT_PALLET_INSTANCE = 50;
-
-const relayUrl = config.relayUrl;
-const statemintUrl = config.statemintUrl;
-const acalaUrl = config.acalaUrl;
-const moonbeamUrl = config.moonbeamUrl;
-const astarUrl = config.astarUrl;
-const polkadexUrl = config.polkadexUrl;
-
-const RELAY_DECIMALS = 12;
-const STATEMINT_DECIMALS = 12;
-const ACALA_DECIMALS = 12;
-const ASTAR_DECIMALS = 18n;
-const UNQ_DECIMALS = 18n;
-
-const TRANSFER_AMOUNT = 2000000000000000000000000n;
-
-const FUNDING_AMOUNT = 3_500_000_0000_000_000n;
-
-const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
-
-const USDT_ASSET_ID = 100;
-const USDT_ASSET_METADATA_DECIMALS = 18;
-const USDT_ASSET_METADATA_NAME = 'USDT';
-const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
-const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
-const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
-
-describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {
-  let alice: IKeyringPair;
-  let bob: IKeyringPair;
-
-  let balanceStmnBefore: bigint;
-  let balanceStmnAfter: bigint;
-
-  let balanceUniqueBefore: bigint;
-  let balanceUniqueAfter: bigint;
-  let balanceUniqueFinal: bigint;
-
-  let balanceBobBefore: bigint;
-  let balanceBobAfter: bigint;
-  let balanceBobFinal: bigint;
-
-  let balanceBobRelayTokenBefore: bigint;
-  let balanceBobRelayTokenAfter: bigint;
-
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      // Fund accounts on Statemint
-      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
-      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
-    });
-
-    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
-      const sovereignFundingAmount = 3_500_000_000n;
-
-      await helper.assets.create(
-        alice,
-        USDT_ASSET_ID,
-        alice.address,
-        USDT_ASSET_METADATA_MINIMAL_BALANCE,
-      );
-      await helper.assets.setMetadata(
-        alice,
-        USDT_ASSET_ID,
-        USDT_ASSET_METADATA_NAME,
-        USDT_ASSET_METADATA_DESCRIPTION,
-        USDT_ASSET_METADATA_DECIMALS,
-      );
-      await helper.assets.mint(
-        alice,
-        USDT_ASSET_ID,
-        alice.address,
-        USDT_ASSET_AMOUNT,
-      );
-
-      // funding parachain sovereing account on Statemint.
-      // The sovereign account should be created before any action
-      // (the assets pallet on Statemint check if the sovereign account exists)
-      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);
-      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
-    });
-
-
-    await usingPlaygrounds(async (helper) => {
-      const location = {
-        V2: {
-          parents: 1,
-          interior: {X3: [
-            {
-              Parachain: STATEMINT_CHAIN,
-            },
-            {
-              PalletInstance: STATEMINT_PALLET_INSTANCE,
-            },
-            {
-              GeneralIndex: USDT_ASSET_ID,
-            },
-          ]},
-        },
-      };
-
-      const metadata =
-      {
-        name: USDT_ASSET_ID,
-        symbol: USDT_ASSET_METADATA_NAME,
-        decimals: USDT_ASSET_METADATA_DECIMALS,
-        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
-      };
-      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
-      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);
-    });
-
-
-    // Providing the relay currency to the unique sender account
-    // (fee for USDT XCM are paid in relay tokens)
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT_RELAY,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    });
-
-  });
-
-  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {
-    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
-      const dest = {
-        V2: {
-          parents: 1,
-          interior: {X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: {
-                  X2: [
-                    {
-                      PalletInstance: STATEMINT_PALLET_INSTANCE,
-                    },
-                    {
-                      GeneralIndex: USDT_ASSET_ID,
-                    },
-                  ]},
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
-      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
-
-      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
-
-      // common good parachain take commission in it native token
-      console.log(
-        '[Statemint -> Unique] transaction fees on Statemint: %s WND',
-        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),
-      );
-      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
-
-    });
-
-
-    // ensure that asset has been delivered
-    await helper.wait.newBlocks(3);
-
-    // expext collection id will be with id 1
-    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
-
-    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);
-
-    console.log(
-      '[Statemint -> Unique] transaction fees on Unique: %s USDT',
-      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
-    );
-    console.log(
-      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',
-      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),
-    );
-    // commission has not paid in USDT token
-    expect(free).to.be.equal(TRANSFER_AMOUNT);
-    // ... and parachain native token
-    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;
-  });
-
-  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {X2: [
-          {
-            Parachain: STATEMINT_CHAIN,
-          },
-          {
-            AccountId32: {
-              network: 'Any',
-              id: alice.addressRaw,
-            },
-          },
-        ]},
-      },
-    };
-
-    const relayFee = 400_000_000_000_000n;
-    const currencies: [any, bigint][] = [
-      [
-        {
-          ForeignAssetId: 0,
-        },
-        TRANSFER_AMOUNT,
-      ],
-      [
-        {
-          NativeAssetId: 'Parent',
-        },
-        relayFee,
-      ],
-    ];
-
-    const feeItem = 1;
-
-    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
-
-    // the commission has been paid in parachain native token
-    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);
-    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));
-    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;
-
-    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-
-      // The USDT token never paid fees. Its amount not changed from begin value.
-      // Also check that xcm transfer has been succeeded
-      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
-    });
-  });
-
-  itSub('Should connect and send Relay token to Unique', async ({helper}) => {
-    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
-    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-          },
-        }};
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {X1: {
-            AccountId32: {
-              network: 'Any',
-              id: bob.addressRaw,
-            },
-          }},
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 0,
-                interior: 'Here',
-              },
-            },
-            fun: {
-              Fungible: TRANSFER_AMOUNT_RELAY,
-            },
-          },
-        ],
-      };
-
-      const feeAssetItem = 0;
-
-      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    });
-
-    await helper.wait.newBlocks(3);
-
-    balanceBobAfter = await helper.balance.getSubstrate(bob.address);
-    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
-
-    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
-    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
-    console.log(
-      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',
-      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
-    );
-    console.log(
-      '[Relay (Westend) -> Unique] transaction fees: %s WND',
-      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),
-    );
-    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);
-    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;
-    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;
-  });
-
-  itSub('Should connect and send Relay token back', async ({helper}) => {
-    let relayTokenBalanceBefore: bigint;
-    let relayTokenBalanceAfter: bigint;
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
-    });
-
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1:{
-            AccountId32: {
-              network: 'Any',
-              id: bob.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const currencies: any = [
-      [
-        {
-          NativeAssetId: 'Parent',
-        },
-        TRANSFER_AMOUNT_RELAY,
-      ],
-    ];
-
-    const feeItem = 0;
-
-    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
-
-    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
-    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
-
-    await usingRelayPlaygrounds(relayUrl, async (helper) => {
-      await helper.wait.newBlocks(10);
-      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
-
-      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
-      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
-      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
-    });
-  });
-});
-
-describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  let balanceUniqueTokenInit: bigint;
-  let balanceUniqueTokenMiddle: bigint;
-  let balanceUniqueTokenFinal: bigint;
-  let balanceAcalaTokenInit: bigint;
-  let balanceAcalaTokenMiddle: bigint;
-  let balanceAcalaTokenFinal: bigint;
-  let balanceUniqueForeignTokenInit: bigint;
-  let balanceUniqueForeignTokenMiddle: bigint;
-  let balanceUniqueForeignTokenFinal: bigint;
-
-  // computed by a test transfer from prod Unique to prod Acala.
-  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48
-  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)
-  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;
-  const acalaEps = 8n * 10n ** 16n;
-
-  let acalaBackwardTransferAmount: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: UNIQUE_CHAIN,
-            },
-          },
-        },
-      };
-
-      const metadata = {
-        name: 'Unique Network',
-        symbol: 'UNQ',
-        decimals: 18,
-        minimalBalance: 1250000000000000000n,
-      };
-      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>
-        hexToString(v.toJSON()['symbol'])) as string[];
-
-      if(!assets.includes('UNQ')) {
-        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
-      } else {
-        console.log('UNQ token already registered on Acala assetRegistry pallet');
-      }
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
-      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);
-      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
-      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Acala', async ({helper}) => {
-
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: ACALA_CHAIN,
-          },
-        },
-      },
-    };
-
-    const beneficiary = {
-      V2: {
-        parents: 0,
-        interior: {
-          X1: {
-            AccountId32: {
-              network: 'Any',
-              id: randomAccount.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const assets = {
-      V2: [
-        {
-          id: {
-            Concrete: {
-              parents: 0,
-              interior: 'Here',
-            },
-          },
-          fun: {
-            Fungible: TRANSFER_AMOUNT,
-          },
-        },
-      ],
-    };
-
-    const feeAssetItem = 0;
-
-    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
-
-    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
-    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
-    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
-
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-
-      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
-      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
-
-      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;
-      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;
-      acalaBackwardTransferAmount = unqIncomeTransfer;
-
-      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;
-
-      console.log(
-        '[Unique -> Acala] transaction fees on Acala: %s ACA',
-        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),
-      );
-      console.log(
-        '[Unique -> Acala] transaction fees on Acala: %s UNQ',
-        helper.util.bigIntToDecimals(acaUnqFees),
-      );
-      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));
-      expect(acaFees == 0n).to.be.true;
-
-      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;
-
-      expect(
-        bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,
-        'Acala took different income fee, check the Acala foreign asset config',
-      ).to.be.true;
-    });
-  });
-
-  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X2: [
-              {Parachain: UNIQUE_CHAIN},
-              {
-                AccountId32: {
-                  network: 'Any',
-                  id: randomAccount.addressRaw,
-                },
-              },
-            ],
-          },
-        },
-      };
-
-      const id = {
-        ForeignAsset: 0,
-      };
-
-      await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');
-      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
-      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
-
-      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;
-      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;
-
-      console.log(
-        '[Acala -> Unique] transaction fees on Acala: %s ACA',
-        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),
-      );
-      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));
-
-      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;
-      expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;
-    });
-
-    await helper.wait.newBlocks(3);
-
-    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
-    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
-    expect(actuallyDelivered > 0).to.be.true;
-
-    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));
-
-    const unqFees = acalaBackwardTransferAmount - actuallyDelivered;
-    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
-    expect(unqFees == 0n).to.be.true;
-  });
-
-  itSub('Acala can send only up to its balance', async ({helper}) => {
-    // set Acala's sovereign account's balance
-    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);
-    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);
-    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);
-
-    const moreThanAcalaHas = acalaBalance * 2n;
-
-    let targetAccountBalance = 0n;
-    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
-    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      moreThanAcalaHas,
-    );
-
-    let maliciousXcmProgramSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Unique
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);
-
-      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(0n);
-
-    // But Acala still can send the correct amount
-    const validTransferAmount = acalaBalance / 2n;
-    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      validTransferAmount,
-    );
-
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram);
-    });
-
-    await helper.wait.newBlocks(maxWaitBlocks);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(validTransferAmount);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {
-    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
-    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
-
-    const uniqueMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-        },
-      },
-    };
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      uniqueAssetId,
-      testAmount,
-    );
-
-    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      testAmount,
-    );
-
-    let maliciousXcmProgramFullIdSent: any;
-    let maliciousXcmProgramHereIdSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Unique using full UNQ identification
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);
-
-      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-
-    // Try to trick Unique using shortened UNQ identification
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);
-
-      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-  });
-});
-
-describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-  let unqFees: bigint;
-  let balanceUniqueTokenInit: bigint;
-  let balanceUniqueTokenMiddle: bigint;
-  let balanceUniqueTokenFinal: bigint;
-  const maxWaitBlocks = 6;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))
-        .toJSON() as [])
-        .map(nToBigInt).length != 0;
-      /*
-      Check whether the Unique token has been added
-      to the whitelist, since an error will occur
-      if it is added again. Needed for debugging
-      when this test is run multiple times.
-      */
-      if(isWhitelisted) {
-        console.log('UNQ token is already whitelisted on Polkadex');
-      } else {
-        await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);
-      }
-
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
-      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {
-
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: POLKADEX_CHAIN,
-          },
-        },
-      },
-    };
-
-    const beneficiary = {
-      V2: {
-        parents: 0,
-        interior: {
-          X1: {
-            AccountId32: {
-              network: 'Any',
-              id: randomAccount.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const assets = {
-      V2: [
-        {
-          id: {
-            Concrete: {
-              parents: 0,
-              interior: 'Here',
-            },
-          },
-          fun: {
-            Fungible: TRANSFER_AMOUNT,
-          },
-        },
-      ],
-    };
-
-    const feeAssetItem = 0;
-
-    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-    const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
-
-    unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
-    console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
-    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
-
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      /*
-        Since only the parachain part of the Polkadex
-        infrastructure is launched (without their
-        solochain validators), processing incoming
-        assets will lead to an error.
-        This error indicates that the Polkadex chain
-        received a message from the Unique network,
-        since the hash is being checked to ensure
-        it matches what was sent.
-      */
-      await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);
-    });
-  });
-
-
-  itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {
-
-    const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      randomAccount.addressRaw,
-      uniqueAssetId,
-      TRANSFER_AMOUNT,
-    );
-
-    let xcmProgramSent: any;
-
-
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, xcmProgram);
-
-      xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);
-
-    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
-
-    expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);
-  });
-
-  itSub('Polkadex can send only up to its balance', async ({helper}) => {
-    const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);
-    const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);
-    await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);
-    const moreThanPolkadexHas = 2n * polkadexBalance;
-
-    const targetAccount = helper.arrange.createEmptyAccount();
-
-    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      moreThanPolkadexHas,
-    );
-
-    let maliciousXcmProgramSent: any;
-
-
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);
-
-      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, maliciousXcmProgramSent);
-
-    const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(0n);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => {
-    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
-    const targetAccount = helper.arrange.createEmptyAccount();
-
-    const uniqueMultilocation = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: UNIQUE_CHAIN,
-          },
-        },
-      },
-    };
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      uniqueAssetId,
-      testAmount,
-    );
-
-    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      testAmount,
-    );
-
-    let maliciousXcmProgramFullIdSent: any;
-    let maliciousXcmProgramHereIdSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Unique using full UNQ identification
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);
-
-      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);
-
-    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-
-    // Try to trick Unique using shortened UNQ identification
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);
-
-      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);
-
-    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-  });
-});
-
-// These tests are relevant only when
-// the the corresponding foreign assets are not registered
-describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {
-  let alice: IKeyringPair;
-  let alith: IKeyringPair;
-
-  const testAmount = 100_000_000_000n;
-  let uniqueParachainJunction;
-  let uniqueAccountJunction;
-
-  let uniqueParachainMultilocation: any;
-  let uniqueAccountMultilocation: any;
-  let uniqueCombinedMultilocation: any;
-  let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V2
-
-  let messageSent: any;
-
-  const maxWaitBlocks = 3;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-
-      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};
-      uniqueAccountJunction = {
-        AccountId32: {
-          network: 'Any',
-          id: alice.addressRaw,
-        },
-      };
-
-      uniqueParachainMultilocation = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: uniqueParachainJunction,
-          },
-        },
-      };
-
-      uniqueAccountMultilocation = {
-        V2: {
-          parents: 0,
-          interior: {
-            X1: uniqueAccountJunction,
-          },
-        },
-      };
-
-      uniqueCombinedMultilocation = {
-        V2: {
-          parents: 1,
-          interior: {
-            X2: [uniqueParachainJunction, uniqueAccountJunction],
-          },
-        },
-      };
-
-      uniqueCombinedMultilocationAcala = {
-        V2: {
-          parents: 1,
-          interior: {
-            X2: [uniqueParachainJunction, uniqueAccountJunction],
-          },
-        },
-      };
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    // eslint-disable-next-line require-await
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      alith = helper.account.alithAccount();
-    });
-  });
-
-
-
-  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {
-    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
-      const id = {
-        Token: 'ACA',
-      };
-      const destination = uniqueCombinedMultilocationAcala;
-      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');
-
-      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, messageSent);
-  });
-
-  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const id = 'SelfReserve';
-      const destination = uniqueCombinedMultilocation;
-      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');
-
-      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, messageSent);
-  });
-
-  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      const destinationParachain = uniqueParachainMultilocation;
-      const beneficiary = uniqueAccountMultilocation;
-      const assets = {
-        V2: [{
-          id: {
-            Concrete: {
-              parents: 0,
-              interior: 'Here',
-            },
-          },
-          fun: {
-            Fungible: testAmount,
-          },
-        }],
-      };
-      const feeAssetItem = 0;
-
-      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [
-        destinationParachain,
-        beneficiary,
-        assets,
-        feeAssetItem,
-      ]);
-
-      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, messageSent);
-  });
-
-  itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      helper.arrange.createEmptyAccount().addressRaw,
-      {
-        Concrete: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: POLKADEX_CHAIN,
-            },
-          },
-        },
-      },
-      testAmount,
-    );
-
-    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);
-      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await expectFailedToTransact(helper, messageSent);
-  });
-});
-
-describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {
-  // Unique constants
-  let alice: IKeyringPair;
-  let uniqueAssetLocation;
-
-  let randomAccountUnique: IKeyringPair;
-  let randomAccountMoonbeam: IKeyringPair;
-
-  // Moonbeam constants
-  let assetId: string;
-
-  const uniqueAssetMetadata = {
-    name: 'xcUnique',
-    symbol: 'xcUNQ',
-    decimals: 18,
-    isFrozen: false,
-    minimalBalance: 1n,
-  };
-
-  let balanceUniqueTokenInit: bigint;
-  let balanceUniqueTokenMiddle: bigint;
-  let balanceUniqueTokenFinal: bigint;
-  let balanceForeignUnqTokenInit: bigint;
-  let balanceForeignUnqTokenMiddle: bigint;
-  let balanceForeignUnqTokenFinal: bigint;
-  let balanceGlmrTokenInit: bigint;
-  let balanceGlmrTokenMiddle: bigint;
-  let balanceGlmrTokenFinal: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);
-
-      balanceForeignUnqTokenInit = 0n;
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const alithAccount = helper.account.alithAccount();
-      const baltatharAccount = helper.account.baltatharAccount();
-      const dorothyAccount = helper.account.dorothyAccount();
-
-      randomAccountMoonbeam = helper.account.create();
-
-      // >>> Sponsoring Dorothy >>>
-      console.log('Sponsoring Dorothy.......');
-      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring Dorothy.......DONE');
-      // <<< Sponsoring Dorothy <<<
-
-      uniqueAssetLocation = {
-        XCM: {
-          parents: 1,
-          interior: {X1: {Parachain: UNIQUE_CHAIN}},
-        },
-      };
-      const existentialDeposit = 1n;
-      const isSufficient = true;
-      const unitsPerSecond = 1n;
-      const numAssetsWeightHint = 0;
-
-      if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {
-        console.log('Unique asset is already registered on MoonBeam');
-      } else {
-        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
-          location: uniqueAssetLocation,
-          metadata: uniqueAssetMetadata,
-          existentialDeposit,
-          isSufficient,
-          unitsPerSecond,
-          numAssetsWeightHint,
-        });
-
-        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
-
-        await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
-      }
-
-      // >>> Acquire Unique AssetId Info on Moonbeam >>>
-      console.log('Acquire Unique AssetId Info on Moonbeam.......');
-
-      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
-      console.log('UNQ asset ID is %s', assetId);
-      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');
-      // >>> Acquire Unique AssetId Info on Moonbeam >>>
-
-      // >>> Sponsoring random Account >>>
-      console.log('Sponsoring random Account.......');
-      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);
-      console.log('Sponsoring random Account.......DONE');
-      // <<< Sponsoring random Account <<<
-
-      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);
-    });
-
-    await usingPlaygrounds(async (helper) => {
-      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);
-      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {
-    const currencyId = {
-      NativeAssetId: 'Here',
-    };
-    const dest = {
-      V2: {
-        parents: 1,
-        interior: {
-          X2: [
-            {Parachain: MOONBEAM_CHAIN},
-            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},
-          ],
-        },
-      },
-    };
-    const amount = TRANSFER_AMOUNT;
-
-    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');
-
-    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);
-    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;
-
-    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
-    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));
-    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
-
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);
-
-      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;
-      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));
-      expect(glmrFees == 0n).to.be.true;
-
-      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;
-
-      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;
-      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));
-      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;
-    });
-  });
-
-  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const asset = {
-        V2: {
-          id: {
-            Concrete: {
-              parents: 1,
-              interior: {
-                X1: {Parachain: UNIQUE_CHAIN},
-              },
-            },
-          },
-          fun: {
-            Fungible: TRANSFER_AMOUNT,
-          },
-        },
-      };
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X2: [
-              {Parachain: UNIQUE_CHAIN},
-              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},
-            ],
-          },
-        },
-      };
-
-      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');
-
-      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);
-
-      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;
-      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));
-      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;
-
-      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);
-
-      expect(unqRandomAccountAsset).to.be.null;
-
-      balanceForeignUnqTokenFinal = 0n;
-
-      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;
-      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));
-      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
-    });
-
-    await helper.wait.newBlocks(3);
-
-    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);
-    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
-    expect(actuallyDelivered > 0).to.be.true;
-
-    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));
-
-    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;
-    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
-    expect(unqFees == 0n).to.be.true;
-  });
-
-  itSub('Moonbeam can send only up to its balance', async ({helper}) => {
-    // set Moonbeam's sovereign account's balance
-    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);
-    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);
-    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);
-
-    const moreThanMoonbeamHas = moonbeamBalance * 2n;
-
-    let targetAccountBalance = 0n;
-    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
-    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      moreThanMoonbeamHas,
-    );
-
-    let maliciousXcmProgramSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Unique
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);
-
-      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(0n);
-
-    // But Moonbeam still can send the correct amount
-    const validTransferAmount = moonbeamBalance / 2n;
-    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      validTransferAmount,
-    );
-
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, validXcmProgram]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);
-    });
-
-    await helper.wait.newBlocks(maxWaitBlocks);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(validTransferAmount);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {
-    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
-    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      uniqueAssetId,
-      testAmount,
-    );
-
-    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      testAmount,
-    );
-
-    let maliciousXcmProgramFullIdSent: any;
-    let maliciousXcmProgramHereIdSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Unique using full UNQ identification
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);
-
-      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-
-    // Try to trick Unique using shortened UNQ identification
-    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
-      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]);
-
-      // Needed to bypass the call filter.
-      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
-      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);
-
-      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-  });
-});
-
-describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {
-  let alice: IKeyringPair;
-  let randomAccount: IKeyringPair;
-
-  const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar
-  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar
-
-  // Unique -> Astar
-  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
-  const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar
-  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ
-  const unqToAstarArrived = 9_962_196_000_000_000_000n; // 9.962 ... UNQ, Astar takes a commision in foreign tokens
-
-  // Astar -> Unique
-  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ
-  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.962_219_600_000_000_000n UNQ
-
-  let balanceAfterUniqueToAstarXCM: bigint;
-
-  before(async () => {
-    await usingPlaygrounds(async (helper, privateKey) => {
-      alice = await privateKey('//Alice');
-      [randomAccount] = await helper.arrange.createAccounts([100n], alice);
-      console.log('randomAccount', randomAccount.address);
-
-      // Set the default version to wrap the first message to other chains.
-      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
-    });
-
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {
-        console.log('1. Create foreign asset and metadata');
-        await helper.getSudo().assets.forceCreate(
-          alice,
-          UNQ_ASSET_ID_ON_ASTAR,
-          alice.address,
-          UNQ_MINIMAL_BALANCE_ON_ASTAR,
-        );
-
-        await helper.assets.setMetadata(
-          alice,
-          UNQ_ASSET_ID_ON_ASTAR,
-          'Unique Network',
-          'UNQ',
-          Number(UNQ_DECIMALS),
-        );
-
-        console.log('2. Register asset location on Astar');
-        const assetLocation = {
-          V2: {
-            parents: 1,
-            interior: {
-              X1: {
-                Parachain: UNIQUE_CHAIN,
-              },
-            },
-          },
-        };
-
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
-
-        console.log('3. Set UNQ payment for XCM execution on Astar');
-        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
-      } else {
-        console.log('UNQ is already registered on Astar');
-      }
-      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');
-      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);
-    });
-  });
-
-  itSub('Should connect and send UNQ to Astar', async ({helper}) => {
-    const destination = {
-      V2: {
-        parents: 1,
-        interior: {
-          X1: {
-            Parachain: ASTAR_CHAIN,
-          },
-        },
-      },
-    };
-
-    const beneficiary = {
-      V2: {
-        parents: 0,
-        interior: {
-          X1: {
-            AccountId32: {
-              network: 'Any',
-              id: randomAccount.addressRaw,
-            },
-          },
-        },
-      },
-    };
-
-    const assets = {
-      V2: [
-        {
-          id: {
-            Concrete: {
-              parents: 0,
-              interior: 'Here',
-            },
-          },
-          fun: {
-            Fungible: unqToAstarTransferred,
-          },
-        },
-      ],
-    };
-
-    // Initial balance is 100 UNQ
-    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);
-    console.log(`Initial balance is: ${balanceBefore}`);
-
-    const feeAssetItem = 0;
-    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
-
-    // Balance after reserve transfer is less than 90
-    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);
-    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);
-    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);
-    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;
-
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      await helper.wait.newBlocks(3);
-      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);
-      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);
-
-      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);
-      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);
-
-      expect(xcUNQbalance).to.eq(unqToAstarArrived);
-      // Astar balance does not changed
-      expect(astarBalance).to.eq(astarInitialBalance);
-    });
-  });
-
-  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      const destination = {
-        V2: {
-          parents: 1,
-          interior: {
-            X1: {
-              Parachain: UNIQUE_CHAIN,
-            },
-          },
-        },
-      };
-
-      const beneficiary = {
-        V2: {
-          parents: 0,
-          interior: {
-            X1: {
-              AccountId32: {
-                network: 'Any',
-                id: randomAccount.addressRaw,
-              },
-            },
-          },
-        },
-      };
-
-      const assets = {
-        V2: [
-          {
-            id: {
-              Concrete: {
-                parents: 1,
-                interior: {
-                  X1: {
-                    Parachain: UNIQUE_CHAIN,
-                  },
-                },
-              },
-            },
-            fun: {
-              Fungible: unqFromAstarTransfered,
-            },
-          },
-        ],
-      };
-
-      // Initial balance is 1 ASTR
-      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);
-      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);
-      expect(balanceASTRbefore).to.eq(astarInitialBalance);
-
-      const feeAssetItem = 0;
-      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw
-      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);
-
-      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);
-      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);
-      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);
-
-      // Assert: xcUNQ balance correctly decreased
-      expect(xcUNQbalance).to.eq(unqOnAstarLeft);
-      // Assert: ASTR balance is 0.996...
-      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);
-    });
-
-    await helper.wait.newBlocks(3);
-    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);
-    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);
-    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);
-  });
-
-  itSub('Astar can send only up to its balance', async ({helper}) => {
-    // set Astar's sovereign account's balance
-    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);
-    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);
-    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);
-
-    const moreThanAstarHas = astarBalance * 2n;
-
-    let targetAccountBalance = 0n;
-    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
-
-    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      moreThanAstarHas,
-    );
-
-    let maliciousXcmProgramSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Unique
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);
-
-      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome.isFailedToTransactAsset);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(0n);
-
-    // But Astar still can send the correct amount
-    const validTransferAmount = astarBalance / 2n;
-    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      validTransferAmount,
-    );
-
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram);
-    });
-
-    await helper.wait.newBlocks(maxWaitBlocks);
-
-    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(targetAccountBalance).to.be.equal(validTransferAmount);
-  });
-
-  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {
-    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
-    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
-
-    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      uniqueAssetId,
-      testAmount,
-    );
-
-    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
-      targetAccount.addressRaw,
-      {
-        Concrete: {
-          parents: 0,
-          interior: 'Here',
-        },
-      },
-      testAmount,
-    );
-
-    let maliciousXcmProgramFullIdSent: any;
-    let maliciousXcmProgramHereIdSent: any;
-    const maxWaitBlocks = 3;
-
-    // Try to trick Unique using full UNQ identification
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramFullId);
-
-      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-
-    // Try to trick Unique using shortened UNQ identification
-    await usingAstarPlaygrounds(astarUrl, async (helper) => {
-      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramHereId);
-
-      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
-    });
-
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome.isUntrustedReserveLocation);
-
-    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
-    expect(accountBalance).to.be.equal(0n);
-  });
-});
addedjs-packages/tests/sub/appPromotion/appPromotion.seqtest.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/appPromotion/appPromotion.seqtest.ts
@@ -0,0 +1,82 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, Pallets, requirePalletsOrSkip} from '../../util/index.js';
+import {expect} from '../../eth/util/index.js';
+
+let superuser: IKeyringPair;
+let donor: IKeyringPair;
+let palletAdmin: IKeyringPair;
+
+describe('App promotion', () => {
+  before(async function () {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]);
+      superuser = await privateKey('//Alice');
+      donor = await privateKey({url: import.meta.url});
+      palletAdmin = await privateKey('//PromotionAdmin');
+      const api = helper.getApi();
+      await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));
+    });
+  });
+
+  after(async function () {
+    await usingPlaygrounds(async (helper) => {
+      if(helper.fetchMissingPalletNames([Pallets.AppPromotion]).length != 0) return;
+      const api = helper.getApi();
+      await helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})));
+    });
+  });
+
+  describe('admin adress', () => {
+    itSub('can be set by sudo only', async ({helper}) => {
+      const api = helper.getApi();
+      const [nonAdmin] = await helper.arrange.createAccounts([10n], donor);
+      // nonAdmin can not set admin not from himself nor as a sudo
+      await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address}))).to.be.rejected;
+      await expect(helper.signTransaction(nonAdmin, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: nonAdmin.address})))).to.be.rejected;
+    });
+
+    itSub('can be any valid CrossAccountId', async ({helper}) => {
+      // We are not going to set an eth address as a sponsor,
+      // but we do want to check, it doesn't break anything;
+      const api = helper.getApi();
+      const [account] = await helper.arrange.createAccounts([10n], donor);
+      const ethAccount = helper.address.substrateToEth(account.address);
+      // Alice sets Ethereum address as a sudo. Then Substrate address back...
+      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Ethereum: ethAccount})))).to.be.fulfilled;
+      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: palletAdmin.address})))).to.be.fulfilled;
+
+      // ...It doesn't break anything;
+      const collection = await helper.nft.mintCollection(account, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
+      await expect(helper.signTransaction(account, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
+    });
+
+    itSub('can be reassigned', async ({helper}) => {
+      const api = helper.getApi();
+      const [oldAdmin, newAdmin, collectionOwner] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
+      const collection  = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
+
+      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: oldAdmin.address})))).to.be.fulfilled;
+      await expect(helper.signTransaction(superuser, api.tx.sudo.sudo(api.tx.appPromotion.setAdminAddress({Substrate: newAdmin.address})))).to.be.fulfilled;
+      await expect(helper.signTransaction(oldAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
+
+      await expect(helper.signTransaction(newAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled;
+    });
+  });
+});
+
addedjs-packages/tests/sub/appPromotion/appPromotion.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/appPromotion/appPromotion.test.ts
@@ -0,0 +1,1008 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+import {itEth, expect, SponsoringMode} from '../../eth/util/index.js';
+
+let donor: IKeyringPair;
+let palletAdmin: IKeyringPair;
+let nominal: bigint;
+let palletAddress: string;
+let accounts: IKeyringPair[];
+let usedAccounts: IKeyringPair[] = [];
+
+async function getAccounts(accountsNumber: number, balance?: bigint) {
+  let accs: IKeyringPair[] = [];
+  if(balance) {
+    await usingPlaygrounds(async (helper) => {
+      accs = await helper.arrange.createAccounts(new Array(accountsNumber).fill(balance), donor);
+    });
+  } else {
+    accs = accounts.splice(0, accountsNumber);
+  }
+  usedAccounts.push(...accs);
+  return accs;
+}
+// App promotion periods:
+// LOCKING_PERIOD = 12 blocks of relay
+// UNLOCKING_PERIOD = 6 blocks of parachain
+
+describe('App promotion', () => {
+  before(async function () {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.AppPromotion]);
+      donor = await privateKey({url: import.meta.url});
+      palletAddress = helper.arrange.calculatePalletAddress('appstake');
+      palletAdmin = await privateKey('//PromotionAdmin');
+
+      nominal = helper.balance.getOneTokenNominal();
+
+      const accountBalances = new Array(200).fill(1000n);
+      accounts = await helper.arrange.createAccounts(accountBalances, donor); // create accounts-pool to speed up tests
+    });
+  });
+
+  afterEach(async () => {
+    await usingPlaygrounds(async (helper) => {
+      let unstakeTxs = [];
+      for(const account of usedAccounts) {
+        if(unstakeTxs.length === 3) {
+          await Promise.all(unstakeTxs);
+          unstakeTxs = [];
+        }
+        unstakeTxs.push(helper.staking.unstakeAll(account));
+      }
+      await Promise.all(unstakeTxs);
+      usedAccounts = [];
+      expect(await helper.staking.getTotalStaked()).to.eq(0n); // there are no active stakes after each test
+      // Make sure previousCalculatedRecord is None to avoid problem with payout stakers;
+      await helper.admin.payoutStakers(palletAdmin, 100);
+      expect((await helper.getApi().query.appPromotion.previousCalculatedRecord() as any).isNone).to.be.true;
+    });
+  });
+
+  describe('stake extrinsic', () => {
+    itSub('should "freeze" staking balance, add it to "staked" map, and increase "totalStaked" amount', async ({helper}) => {
+      const [staker, recepient] = await getAccounts(2);
+      const totalStakedBefore = await helper.staking.getTotalStaked();
+
+      // Minimum stake amount is 100:
+      await expect(helper.staking.stake(staker, 100n * nominal - 1n)).to.be.rejected;
+      await helper.staking.stake(staker, 100n * nominal);
+
+      // Staker balance is: frozen: 100, reserved: 0n...
+      // ...so he can not transfer 900
+      expect(await helper.balance.getSubstrateFull(staker.address)).to.contain({frozen: 100n * nominal, reserved: 0n});
+      expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 100n * nominal}]);
+      await expect(helper.balance.transferToSubstrate(staker, recepient.address, 900n * nominal)).to.be.rejectedWith(/^Token: Frozen$/);
+
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(100n * nominal);
+      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
+      // it is potentially flaky test. Promotion can credited some tokens. Maybe we need to use closeTo?
+      expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore + 100n * nominal); // total tokens amount staked in app-promotion increased
+
+      await helper.staking.stake(staker, 200n * nominal);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(300n * nominal);
+      const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(totalStakedPerBlock[0].amount).to.equal(100n * nominal);
+      expect(totalStakedPerBlock[1].amount).to.equal(200n * nominal);
+    });
+
+    [
+      {unstake: 'unstakeAll' as const},
+      {unstake: 'unstakePartial' as const},
+    ].map(testCase => {
+      itSub(`[${testCase.unstake}] should allow to create maximum 10 stakes for account`, async ({helper}) => {
+        const [staker] = await getAccounts(1, 2000n);
+        const ONE_STAKE = 100n * nominal;
+        for(let i = 0; i < 10; i++) {
+          await helper.staking.stake(staker, ONE_STAKE);
+        }
+
+        // can have 10 stakes
+        expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(1000n * nominal);
+        expect(await helper.staking.getTotalStakedPerBlock({Substrate: staker.address})).to.have.length(10);
+
+        await expect(helper.staking.stake(staker, ONE_STAKE)).to.be.rejectedWith('appPromotion.NoPermission');
+
+        // After unstake can stake again
+
+        // CASE 1: unstakeAll
+        if(testCase.unstake === 'unstakeAll') {
+          await helper.staking.unstakeAll(staker);
+          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
+          await helper.staking.stake(staker, 100n * nominal);
+          expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(100n * nominal);
+        }
+        // CASE 2: unstakePartial
+        else {
+          await helper.staking.unstakePartial(staker, ONE_STAKE);
+          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9);
+          await helper.staking.stake(staker, 100n * nominal);
+          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(10);
+          await expect(helper.staking.stake(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.NoPermission');
+          await helper.staking.unstakePartial(staker, 150n * nominal);
+          expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(9);
+          expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.equal(850n * nominal);
+        }
+      });
+    });
+    // TODO: Now AppPromo makes freezes. Probably this test should be changed\removed.
+    itSub.skip('should allow to stake() if balance is locked with different id', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+
+      // staker has tokens locked with vesting id:
+      await helper.balance.vestedTransfer(donor, staker.address, {start: 0n, period: 1n, periodCount: 1n, perPeriod: 200n * nominal});
+      expect(await helper.balance.getSubstrateFull(staker.address))
+        .to.deep.contain({free: 1200n * nominal, frozen: 200n * nominal, reserved: 0n});
+
+      // Locked balance can be staked. staker can stake 1200 tokens (minus fee):
+      await helper.staking.stake(staker, 1000n * nominal);
+      await helper.staking.stake(staker, 199n * nominal);
+      // check balances
+      expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]);
+      expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: 1199n * nominal}]);
+      expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 1199n * nominal});
+      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(1199n * nominal);
+
+      // staker can unstake
+      await helper.staking.unstakeAll(staker);
+      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1199n * nominal);
+      const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+      await helper.wait.forParachainBlockNumber(pendingUnstake.block);
+
+      // check balances
+      expect(await helper.balance.getLocked(staker.address)).to.deep.eq([{id: 'ormlvest', amount: 200n * nominal, reasons: 'All'}]);
+      expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 200n * nominal});
+      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(1199n);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n);
+
+      // staker can transfer balances now
+      await helper.balance.transferToSubstrate(staker, donor.address, 900n * nominal);
+    });
+
+    itSub('should not allow to stake(), if stake amount is more than total free balance minus locked by staking', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+
+      // Can't stake full balance because Alice needs to pay some fee
+      await expect(helper.staking.stake(staker, 1000n * nominal)).to.be.rejected; // With('Arithmetic')
+      await helper.staking.stake(staker, 500n * nominal);
+
+      // Can't stake 500 tkn because Alice has Less than 500 transferable;
+      await expect(helper.staking.stake(staker, 500n * nominal)).to.be.rejected; // With('Arithmetic');
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(500n * nominal);
+    });
+
+    itSub('for different accounts in one block is possible', async ({helper}) => {
+      const crowd = await getAccounts(4);
+
+      const crowdStartsToStake = crowd.map(user => helper.staking.stake(user, 100n * nominal));
+      await expect(Promise.all(crowdStartsToStake)).to.be.fulfilled;
+
+      const crowdStakes = await Promise.all(crowd.map(address => helper.staking.getTotalStaked({Substrate: address.address})));
+      expect(crowdStakes).to.deep.equal([100n * nominal, 100n * nominal, 100n * nominal, 100n * nominal]);
+    });
+  });
+
+  describe('Unstaking', () => {
+    [
+      {method: 'unstakeAll' as const},
+      {method: 'unstakePartial' as const},
+    ].map(testCase => {
+      itSub(`[${testCase.method}] should move tokens to "pendingUnstake" and subtract it from totalStaked`, async ({helper}) => {
+        const [staker, recepient] = await getAccounts(2);
+        const totalStakedBefore = await helper.staking.getTotalStaked();
+        const STAKE_AMOUNT = 900n * nominal;
+
+        await helper.staking.stake(staker, STAKE_AMOUNT);
+        testCase.method === 'unstakeAll'
+          ? await helper.staking.unstakeAll(staker)
+          : await helper.staking.unstakePartial(staker, STAKE_AMOUNT);
+
+        // Right after unstake tokens are still locked
+        expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
+        expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([{id: 'appstakeappstake', amount: STAKE_AMOUNT}]);
+        expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: STAKE_AMOUNT});
+        // Staker can not transfer
+        await expect(helper.balance.transferToSubstrate(staker, recepient.address, 100n * nominal)).to.be.rejectedWith(/^Token: Frozen$/);
+        expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(STAKE_AMOUNT);
+        expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
+        expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedBefore);
+      });
+    });
+
+    [
+      {method: 'unstakeAll' as const},
+      {method: 'unstakePartial' as const},
+    ].map(testCase => {
+      itSub(`[${testCase.method}] should unlock balance after unlocking period ends and remove it from "pendingUnstake"`, async ({helper}) => {
+        const [staker] = await getAccounts(1);
+        await helper.staking.stake(staker, 100n * nominal);
+        testCase.method === 'unstakeAll'
+          ? await helper.staking.unstakeAll(staker)
+          : await helper.staking.unstakePartial(staker, 100n * nominal);
+        const [pendingUnstake] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+
+        // Wait for unstaking period. Balance now free ~1000; reserved, frozen, miscFrozeb: 0n
+        await helper.wait.forParachainBlockNumber(pendingUnstake.block);
+        expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n});
+        expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
+
+        // staker can transfer:
+        await helper.balance.transferToSubstrate(staker, donor.address, 998n * nominal);
+        expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(1n);
+      });
+    });
+
+    [
+      {method: 'unstakeAll' as const},
+      {method: 'unstakePartial' as const},
+    ].map(testCase => {
+      itSub(`[${testCase.method}] should successfully unstake multiple stakes`, async ({helper}) => {
+        const [staker] = await getAccounts(1);
+        await helper.staking.stake(staker, 100n * nominal);
+        await helper.staking.stake(staker, 200n * nominal);
+        await helper.staking.stake(staker, 300n * nominal);
+
+        // staked: [100, 200, 300]; unstaked: 0
+        let totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
+        let pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+        let stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+        expect(totalPendingUnstake).to.be.deep.equal(0n);
+        expect(pendingUnstake).to.be.deep.equal([]);
+        expect(stakes[0].amount).to.equal(100n * nominal);
+        expect(stakes[1].amount).to.equal(200n * nominal);
+        expect(stakes[2].amount).to.equal(300n * nominal);
+
+        // Can unstake multiple stakes
+        testCase.method === 'unstakeAll'
+          ? await helper.staking.unstakeAll(staker)
+          : await helper.staking.unstakePartial(staker, 600n * nominal);
+
+        pendingUnstake = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+        totalPendingUnstake = await helper.staking.getPendingUnstake({Substrate: staker.address});
+        stakes = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+        expect(totalPendingUnstake).to.be.equal(600n * nominal);
+        expect(stakes).to.be.deep.equal([]);
+        expect(pendingUnstake[0].amount).to.equal(600n * nominal);
+
+        expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 600n * nominal});
+        expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
+        await helper.wait.forParachainBlockNumber(pendingUnstake[0].block);
+        expect (await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n});
+        expect (await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n);
+      });
+    });
+
+    [
+      {method: 'unstakeAll' as const},
+      {method: 'unstakePartial' as const},
+    ].map(testCase => {
+      itSub(`[${testCase.method}] should not have any effects if no active stakes`, async ({helper}) => {
+        const [staker] = await getAccounts(1);
+
+        // unstake has no effect if no stakes at all
+        testCase.method === 'unstakeAll'
+          ? await helper.staking.unstakeAll(staker)
+          : await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
+
+        expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(0n);
+        expect(await helper.balance.getSubstrate(staker.address) / nominal).to.be.equal(999n); // TODO bigint closeTo helper
+
+        // TODO stake() unstake() waitUnstaked() unstake();
+
+        // can't unstake if there are only pendingUnstakes
+        await helper.staking.stake(staker, 100n * nominal);
+
+        if(testCase.method === 'unstakeAll') {
+          await helper.staking.unstakeAll(staker);
+          await helper.staking.unstakeAll(staker);
+        } else {
+          await helper.staking.unstakePartial(staker, 100n * nominal);
+          await expect(helper.staking.unstakePartial(staker, 100n * nominal)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
+        }
+
+        expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
+        expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
+        expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
+      });
+    });
+
+    [
+      {method: 'unstakeAll' as const},
+      {method: 'unstakePartial' as const},
+    ].map(testCase => {
+      itSub(`[${testCase.method}] should create different pending-unlock for each unlocking stake`, async ({helper}) => {
+        const [staker] = await getAccounts(1);
+        await helper.staking.stake(staker, 100n * nominal);
+        testCase.method === 'unstakeAll'
+          ? await helper.staking.unstakeAll(staker)
+          : await helper.staking.unstakePartial(staker, 100n * nominal);
+        await helper.staking.stake(staker, 120n * nominal);
+        testCase.method === 'unstakeAll'
+          ? await helper.staking.unstakeAll(staker)
+          : await helper.staking.unstakePartial(staker, 120n * nominal);
+
+        const unstakingPerBlock = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+        expect(unstakingPerBlock).has.length(2);
+        expect(unstakingPerBlock[0].amount).to.equal(100n * nominal);
+        expect(unstakingPerBlock[1].amount).to.equal(120n * nominal);
+        expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.equal(0);
+      });
+    });
+
+    [
+      {method: 'unstakeAll' as const},
+      {method: 'unstakePartial' as const},
+    ].map(testCase => {
+      itSub(`[${testCase.method}] should be possible for 3 accounts in one block`, async ({helper}) => {
+        const stakers = await getAccounts(3);
+
+        await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
+        await Promise.all(stakers.map(staker => testCase.method === 'unstakeAll'
+          ? helper.staking.unstakeAll(staker)
+          : helper.staking.unstakePartial(staker, 100n * nominal)));
+
+        await Promise.all(stakers.map(async (staker) => {
+          expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.be.equal(100n * nominal);
+          expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.be.equal(0n);
+        }));
+      });
+    });
+
+    itSub('should not be possible for more than 3 accounts in one block', async ({helper}) => {
+      if(!await helper.arrange.isDevNode()) {
+        const stakers = await getAccounts(10);
+
+        await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
+        const unstakingResults = await Promise.allSettled(stakers.map((staker, i) => i % 2 === 0
+          ? helper.staking.unstakeAll(staker)
+          : helper.staking.unstakePartial(staker, 100n * nominal)));
+
+        const successfulUnstakes = unstakingResults.filter(result => result.status === 'fulfilled');
+        expect(successfulUnstakes).to.have.length(3);
+      }
+    });
+
+    itSub('Cannot partially unstake more than staked', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+      // Staker stakes 300:
+      await helper.staking.stake(staker, 100n * nominal);
+      await helper.staking.stake(staker, 200n * nominal);
+
+      // cannot usntake 300.00000...1
+      await expect(helper.staking.unstakePartial(staker, 300n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(2);
+
+      await helper.staking.unstakePartial(staker, 150n * nominal);
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1);
+      await expect(helper.staking.unstakePartial(staker, 150n * nominal + 1n)).to.be.rejectedWith('appPromotion.InsufficientStakedBalance');
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(1);
+
+      // nothing broken, can unstake full amount:
+      await helper.staking.unstakePartial(staker, 150n * nominal);
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).eq(0);
+    });
+
+    itSub('Can partially unstake arbitrary amount', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+      await helper.staking.stake(staker, 100n * nominal);
+      await helper.staking.stake(staker, 200n * nominal);
+
+      // 0. Staker cannot unstake negative amount
+      await expect(helper.staking.unstakePartial(staker, -1n)).to.be.rejected;
+
+      // 1. Staker can unstake 0 wei
+      await helper.staking.unstakePartial(staker, 0n);
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal);
+      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n);
+
+      // 2. Staker can unstake 1 wei
+      await helper.staking.unstakePartial(staker, 1n);
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(300n * nominal - 1n);
+      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(1n);
+      // 2.1 The oldest stake decreased:
+      let [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(stake1.amount).to.eq(100n * nominal - 1n);
+      expect(stake2.amount).to.eq(200n * nominal);
+
+      // 3. Staker can unstake all but 1 wei
+      await helper.staking.unstakePartial(staker, 100n * nominal - 2n);
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(2);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(200n * nominal + 1n);
+      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(100n * nominal - 1n);
+      [stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(stake1.amount).to.eq(1n);
+      expect(stake2.amount).to.eq(200n * nominal);
+    });
+
+    itSub('can mix different type of unstakes', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+      await helper.staking.stake(staker, 100n * nominal);
+      await helper.staking.stake(staker, 200n * nominal);
+
+      await helper.staking.unstakePartial(staker, 50n * nominal);
+      await helper.staking.unstakeAll(staker);
+      expect(await helper.staking.getStakesNumber({Substrate: staker.address})).to.eq(0);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n);
+      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(300n * nominal);
+
+      const [_unstake1, unstake2] = await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address});
+      await helper.wait.forParachainBlockNumber(unstake2.block);
+
+      expect(await helper.balance.getFrozen(staker.address)).to.deep.eq([]);
+      expect(await helper.balance.getSubstrateFull(staker.address)).to.deep.contain({reserved: 0n, frozen: 0n});
+      expect(await helper.balance.getSubstrate(staker.address) / nominal).to.eq(999n);
+      expect(await helper.staking.getTotalStaked({Substrate: staker.address})).to.eq(0n);
+      expect(await helper.staking.getPendingUnstake({Substrate: staker.address})).to.eq(0n);
+      expect(await helper.staking.getPendingUnstakePerBlock({Substrate: staker.address})).to.deep.eq([]);
+    });
+  });
+
+  describe('collection sponsoring', () => {
+    itSub('should actually sponsor transactions', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner, tokenSender, receiver] = await getAccounts(3);
+      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'Name', description: 'Description', tokenPrefix: 'Prefix', limits: {sponsorTransferTimeout: 0}});
+      const token = await collection.mintToken(collectionOwner, {Substrate: tokenSender.address});
+      await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId));
+      const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress);
+
+      await token.transfer(tokenSender, {Substrate: receiver.address});
+      expect (await token.getOwner()).to.be.deep.equal({Substrate: receiver.address});
+      const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress);
+
+      // senders balance the same, transaction has sponsored
+      expect (await helper.balance.getSubstrate(tokenSender.address)).to.be.equal(1000n * nominal);
+      expect (palletBalanceBefore > palletBalanceAfter).to.be.true;
+    });
+
+    itSub('can not be set by non admin', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner, nonAdmin] = await getAccounts(2);
+
+      const collection  = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
+
+      await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
+      expect((await collection.getData())?.raw.sponsorship).to.equal('Disabled');
+    });
+
+    itSub('should set pallet address as confirmed admin', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner, oldSponsor] = await getAccounts(2);
+
+      // Can set sponsoring for collection without sponsor
+      const collectionWithoutSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'No-sponsor', description: 'New Collection', tokenPrefix: 'Promotion'});
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithoutSponsor.collectionId))).to.be.fulfilled;
+      expect((await collectionWithoutSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
+
+      // Can set sponsoring for collection with unconfirmed sponsor
+      const collectionWithUnconfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Unconfirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}});
+      expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: oldSponsor.address});
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithUnconfirmedSponsor.collectionId))).to.be.fulfilled;
+      expect((await collectionWithUnconfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
+
+      // Can set sponsoring for collection with confirmed sponsor
+      const collectionWithConfirmedSponsor = await helper.nft.mintCollection(collectionOwner, {name: 'Confirmed', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: oldSponsor.address}});
+      await collectionWithConfirmedSponsor.confirmSponsorship(oldSponsor);
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithConfirmedSponsor.collectionId))).to.be.fulfilled;
+      expect((await collectionWithConfirmedSponsor.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
+    });
+
+    itSub('can be overwritten by collection owner', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner, newSponsor] = await getAccounts(2);
+      const collection  = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
+      const collectionId = collection.collectionId;
+
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionId))).to.be.fulfilled;
+
+      // Collection limits still can be changed by the owner
+      expect(await collection.setLimits(collectionOwner, {sponsorTransferTimeout: 0})).to.be.true;
+      expect((await collection.getData())?.raw.limits.sponsorTransferTimeout).to.be.equal(0);
+      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
+
+      // Collection sponsor can be changed too
+      expect((await collection.setSponsor(collectionOwner, newSponsor.address))).to.be.true;
+      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Unconfirmed: newSponsor.address});
+    });
+
+    itSub('should not overwrite collection limits set by the owner earlier', async ({helper}) => {
+      const [owner] = await getAccounts(1);
+      const api = helper.getApi();
+      const limits = {ownerCanDestroy: true, ownerCanTransfer: true, sponsorTransferTimeout: 0};
+      const collectionWithLimits = await helper.nft.mintCollection(owner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits});
+
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collectionWithLimits.collectionId))).to.be.fulfilled;
+      expect((await collectionWithLimits.getData())?.raw.limits).to.be.deep.contain(limits);
+    });
+
+    itSub('should reject transaction if collection doesn\'t exist', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner] = await getAccounts(1);
+
+      // collection has never existed
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(999999999))).to.be.rejected;
+      // collection has been burned
+      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
+      await collection.burn(collectionOwner);
+
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.rejected;
+    });
+  });
+
+  describe('stopSponsoringCollection', () => {
+    itSub('can not be called by non-admin', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner, nonAdmin] = await getAccounts(2);
+      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
+
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId))).to.be.fulfilled;
+
+      await expect(helper.signTransaction(nonAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected;
+      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: palletAddress});
+    });
+
+    itSub('should set sponsoring as disabled', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner, recepient] = await getAccounts(2);
+      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', limits: {sponsorTransferTimeout: 0}});
+      const token = await collection.mintToken(collectionOwner, {Substrate: collectionOwner.address});
+
+      await helper.signTransaction(palletAdmin, api.tx.appPromotion.sponsorCollection(collection.collectionId));
+      await helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId));
+
+      expect((await collection.getData())?.raw.sponsorship).to.be.equal('Disabled');
+
+      // Transactions are not sponsored anymore:
+      const ownerBalanceBefore = await helper.balance.getSubstrate(collectionOwner.address);
+      await token.transfer(collectionOwner, {Substrate: recepient.address});
+      const ownerBalanceAfter = await helper.balance.getSubstrate(collectionOwner.address);
+      expect(ownerBalanceAfter < ownerBalanceBefore).to.be.equal(true);
+    });
+
+    itSub('should not affect collection which is not sponsored by pallete', async ({helper}) => {
+      const api = helper.getApi();
+      const [collectionOwner] = await getAccounts(1);
+      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion', pendingSponsor: {Substrate: collectionOwner.address}});
+      await collection.confirmSponsorship(collectionOwner);
+
+      await expect(helper.signTransaction(palletAdmin, api.tx.appPromotion.stopSponsoringCollection(collection.collectionId))).to.be.rejected;
+
+      expect((await collection.getData())?.raw.sponsorship).to.be.deep.equal({Confirmed: collectionOwner.address});
+    });
+
+    itSub('should reject transaction if collection does not exist', async ({helper}) => {
+      const [collectionOwner] = await getAccounts(1);
+      const collection = await helper.nft.mintCollection(collectionOwner, {name: 'New', description: 'New Collection', tokenPrefix: 'Promotion'});
+
+      await collection.burn(collectionOwner);
+      await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [collection.collectionId], true)).to.be.rejectedWith('common.CollectionNotFound');
+      await expect(helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringCollection', [999_999_999], true)).to.be.rejectedWith('common.CollectionNotFound');
+    });
+  });
+
+  describe('contract sponsoring', () => {
+    itEth('should set palletes address as a sponsor', async ({helper}) => {
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
+      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
+
+      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]);
+
+      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
+      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
+        confirmed: {
+          substrate: palletAddress,
+        },
+      });
+    });
+
+    itEth('should overwrite sponsoring mode and existed sponsor', async ({helper}) => {
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
+      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
+
+      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled;
+
+      // Contract is self sponsored
+      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.be.deep.equal({
+        confirmed: {
+          ethereum: flipper.options.address.toLowerCase(),
+        },
+      });
+
+      // set promotion sponsoring
+      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
+
+      // new sponsor is pallet address
+      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
+      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
+        confirmed: {
+          substrate: palletAddress,
+        },
+      });
+    });
+
+    itEth('can be overwritten by contract owner', async ({helper}) => {
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
+      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
+
+      // contract sponsored by pallet
+      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
+
+      // owner sets self sponsoring
+      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.not.rejected;
+
+      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.true;
+      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
+      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
+        confirmed: {
+          ethereum: flipper.options.address.toLowerCase(),
+        },
+      });
+    });
+
+    itEth('can not be set by non admin', async ({helper}) => {
+      const [nonAdmin] = await getAccounts(1);
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
+      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
+
+      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled;
+
+      // nonAdmin calls sponsorContract
+      await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission');
+
+      // contract still self-sponsored
+      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
+        confirmed: {
+          ethereum: flipper.options.address.toLowerCase(),
+        },
+      });
+    });
+
+    itEth('should actually sponsor transactions', async ({helper}) => {
+      // Contract caller
+      const caller = await helper.eth.createAccountWithBalance(donor, 1000n);
+      const palletBalanceBefore = await helper.balance.getSubstrate(palletAddress);
+
+      // Deploy flipper
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner); // await deployFlipper(web3, contractOwner);
+      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
+
+      // Owner sets to sponsor every tx
+      await contractHelper.methods.setSponsoringRateLimit(flipper.options.address, 0).send({from: contractOwner});
+      await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner});
+      await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address, 1000n); // transferBalanceToEth(api, alice, flipper.options.address, 1000n);
+
+      // Set promotion to the Flipper
+      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
+
+      // Caller calls Flipper
+      await flipper.methods.flip().send({from: caller});
+      expect(await flipper.methods.getValue().call()).to.be.true;
+
+      // The contracts and caller balances have not changed
+      const callerBalance = await helper.balance.getEthereum(caller);
+      const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address);
+      expect(callerBalance).to.be.equal(1000n * nominal);
+      expect(1000n * nominal === contractBalanceAfter).to.be.true;
+
+      // The pallet balance has decreased
+      const palletBalanceAfter = await helper.balance.getSubstrate(palletAddress);
+      expect(palletBalanceAfter < palletBalanceBefore).to.be.true;
+    });
+  });
+
+  describe('stopSponsoringContract', () => {
+    itEth('should remove pallet address from contract sponsors', async ({helper}) => {
+      const caller = await helper.eth.createAccountWithBalance(donor, 1000n);
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner);
+      await helper.eth.transferBalanceFromSubstrate(donor, flipper.options.address);
+      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
+
+      await contractHelper.methods.setSponsoringMode(flipper.options.address, SponsoringMode.Generous).send({from: contractOwner});
+      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address], true);
+      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true);
+
+      expect(await contractHelper.methods.hasSponsor(flipper.options.address).call()).to.be.false;
+      expect((await helper.callRpc('api.query.evmContractHelpers.owner', [flipper.options.address])).toJSON()).to.be.equal(contractOwner);
+      expect((await helper.callRpc('api.query.evmContractHelpers.sponsoring', [flipper.options.address])).toJSON()).to.deep.equal({
+        disabled: null,
+      });
+
+      await flipper.methods.flip().send({from: caller});
+      expect(await flipper.methods.getValue().call()).to.be.true;
+
+      const callerBalance = await helper.balance.getEthereum(caller);
+      const contractBalanceAfter = await helper.balance.getEthereum(flipper.options.address);
+
+      // caller payed for call
+      expect(1000n * nominal > callerBalance).to.be.true;
+      expect(contractBalanceAfter).to.be.equal(100n * nominal);
+    });
+
+    itEth('can not be called by non-admin', async ({helper}) => {
+      const [nonAdmin] = await getAccounts(1);
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner);
+
+      await helper.executeExtrinsic(palletAdmin, 'api.tx.appPromotion.sponsorContract', [flipper.options.address]);
+      await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address]))
+        .to.be.rejectedWith(/appPromotion\.NoPermission/);
+    });
+
+    itEth('should not affect a contract which is not sponsored by pallete', async ({helper}) => {
+      const [nonAdmin] = await getAccounts(1);
+      const contractOwner = (await helper.eth.createAccountWithBalance(donor, 1000n)).toLowerCase();
+      const flipper = await helper.eth.deployFlipper(contractOwner);
+      const contractHelper = await helper.ethNativeContract.contractHelpers(contractOwner);
+      await expect(contractHelper.methods.selfSponsoredEnable(flipper.options.address).send()).to.be.fulfilled;
+
+      await expect(helper.executeExtrinsic(nonAdmin, 'api.tx.appPromotion.stopSponsoringContract', [flipper.options.address], true)).to.be.rejectedWith('appPromotion.NoPermission');
+    });
+  });
+
+  describe('payoutStakers', () => {
+    itSub('can not be called by non admin', async ({helper}) => {
+      const [nonAdmin] = await getAccounts(1);
+      await expect(helper.admin.payoutStakers(nonAdmin, 100)).to.be.rejectedWith('appPromotion.NoPermission');
+    });
+
+    itSub('should increase total staked', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+      const totalStakedBefore = await helper.staking.getTotalStaked();
+      await helper.staking.stake(staker, 100n * nominal);
+
+      // Wait for rewards and pay
+      const [stakedInBlock] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stakedInBlock.block));
+
+      const payout = await helper.admin.payoutStakers(palletAdmin, 100);
+      const totalPayout = payout.reduce((prev, payout) => prev + payout.payout, 0n);
+      const stakerReward = payout.find(p => p.staker === staker.address);
+
+      expect(stakerReward?.payout).to.eq(calculateIncome(100n * nominal) - (100n * nominal));
+
+      const totalStakedAfter = await helper.staking.getTotalStaked();
+      expect(totalStakedAfter).to.equal(totalStakedBefore + (100n * nominal) + totalPayout);
+      // staker can unstake
+      await helper.staking.unstakeAll(staker);
+      expect(await helper.staking.getTotalStaked()).to.be.equal(totalStakedAfter - calculateIncome(100n * nominal));
+    });
+
+    itSub('should credit 0.05% for staking period', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+
+      await waitPromotionPeriodDoesntEnd(helper);
+
+      await helper.staking.stake(staker, 100n * nominal);
+      await helper.staking.stake(staker, 200n * nominal);
+
+      // wait rewards are available:
+      const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));
+
+      const payoutToStaker = (await helper.admin.payoutStakers(palletAdmin, 100)).find((payout) => payout.staker === staker.address)!.payout;
+      expect(payoutToStaker + 300n * nominal).to.equal(calculateIncome(300n * nominal));
+
+      const totalStakedPerBlock = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      const income1 = calculateIncome(100n * nominal);
+      const income2 = calculateIncome(200n * nominal);
+      expect(totalStakedPerBlock[0].amount).to.equal(income1);
+      expect(totalStakedPerBlock[1].amount).to.equal(income2);
+
+      const stakerBalance = await helper.balance.getSubstrateFull(staker.address);
+      expect(stakerBalance).to.contain({frozen: income1 + income2, reserved: 0n});
+      expect(stakerBalance.free / nominal).to.eq(999n);
+    });
+
+    itSub('shoud be paid for more than one period if payments was missed', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+
+      await helper.staking.stake(staker, 100n * nominal);
+      // wait for two rewards are available:
+      let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
+
+      await helper.admin.payoutStakers(palletAdmin, 100);
+      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      const frozenBalanceShouldBe = calculateIncome(100n * nominal, 2);
+      expect(stake.amount).to.be.equal(frozenBalanceShouldBe);
+
+      const stakerFullBalance = await helper.balance.getSubstrateFull(staker.address);
+
+      expect(stakerFullBalance).to.contain({reserved: 0n, frozen: frozenBalanceShouldBe});
+    });
+
+    itSub('should not be credited for pending-unstaked tokens', async ({helper}) => {
+      // staker unstakes before rewards been payed
+      const [staker] = await getAccounts(1);
+      await helper.staking.stake(staker, 100n * nominal);
+      const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
+      await helper.staking.unstakeAll(staker);
+
+      // so he did not receive any rewards
+      const totalBalanceBefore = await helper.balance.getSubstrate(staker.address);
+      await helper.admin.payoutStakers(palletAdmin, 100);
+      const totalBalanceAfter = await helper.balance.getSubstrate(staker.address);
+
+      expect(totalBalanceBefore).to.be.equal(totalBalanceAfter);
+    });
+
+    itSub('should bring compound interest', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+
+      await helper.staking.stake(staker, 100n * nominal);
+
+      let [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block));
+
+      await helper.admin.payoutStakers(palletAdmin, 100);
+      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(stake.amount).to.equal(calculateIncome(100n * nominal));
+
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block) + LOCKING_PERIOD);
+      await helper.admin.payoutStakers(palletAdmin, 100);
+      [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      expect(stake.amount).to.equal(calculateIncome(100n * nominal, 2));
+    });
+
+    itSub('can calculate reward for tiny stake', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+      await helper.staking.stake(staker, 100n * nominal);
+      await helper.staking.stake(staker, 100n * nominal);
+      await helper.staking.unstakePartial(staker, 100n * nominal - 1n);
+
+      const [_stake1, stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));
+
+      const stakerPayout = await payUntilRewardFor(staker.address, helper);
+      expect(stakerPayout.stake).to.eq(100n * nominal + 1n);
+    });
+
+    itSub('can eventually pay all rewards', async ({helper}) => {
+      const stakers = await getAccounts(30);
+      // Create 30 stakes:
+      await Promise.all(stakers.map(staker => helper.staking.stake(staker, 100n * nominal)));
+
+      let unstakingTxs = [];
+      for(const staker of stakers) {
+        if(unstakingTxs.length == 3) {
+          await Promise.all(unstakingTxs);
+          unstakingTxs = [];
+        }
+        unstakingTxs.push(helper.staking.unstakePartial(staker, 100n * nominal - 1n));
+      }
+
+      const [staker] = await getAccounts(1);
+      await helper.staking.stake(staker, 100n * nominal);
+      const [stake] = await helper.staking.getTotalStakedPerBlock({Substrate: staker.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake.block));
+
+      let payouts;
+      do {
+        payouts = await helper.admin.payoutStakers(palletAdmin, 20);
+      } while(payouts.length !== 0);
+    });
+  });
+
+  describe('events', () => {
+    [
+      {method: 'unstakePartial' as const},
+      {method: 'unstakeAll' as const},
+    ].map(testCase => {
+      itSub(testCase.method, async ({helper}) => {
+        const unstakeParams: [] | [bigint] = testCase.method === 'unstakePartial'
+          ? [100n * nominal - 1n]
+          : [];
+        const [staker] = await getAccounts(1);
+        await helper.staking.stake(staker, 100n * nominal);
+        await helper.staking.stake(staker, 200n * nominal);
+        const {result} = await helper.executeExtrinsic(staker, `api.tx.appPromotion.${testCase.method}`, unstakeParams);
+
+        const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Unstake');
+        const unstakerEvents = event?.event.data[0].toString();
+        const unstakedEvents = BigInt(event?.event.data[1].toString());
+        expect(unstakerEvents).to.eq(staker.address);
+        expect(unstakedEvents).to.eq(testCase.method === 'unstakeAll' ? 300n * nominal : 100n * nominal - 1n);
+      });
+    });
+
+    itSub('stake', async ({helper}) => {
+      const [staker] = await getAccounts(1);
+      const {result} = await helper.executeExtrinsic(staker, 'api.tx.appPromotion.stake', [100n * nominal]);
+
+      const event = result.events.find(e => e.event.section === 'appPromotion' && e.event.method === 'Stake');
+      const stakerEvents = event?.event.data[0].toString();
+      const stakedEvents = BigInt(event?.event.data[1].toString());
+      expect(stakerEvents).to.eq(staker.address);
+      expect(stakedEvents).to.eq(100n * nominal);
+    });
+
+    // Flaky
+    itSub.skip('payoutStakers', async ({helper}) => {
+      const [staker1, staker2] = await getAccounts(2);
+      const STAKE1 = 100n * nominal;
+      const STAKE2 = 200n * nominal;
+      await helper.staking.stake(staker1, STAKE1);
+      await helper.staking.stake(staker2, STAKE2);
+
+      const [stake2] = await helper.staking.getTotalStakedPerBlock({Substrate: staker2.address});
+      await helper.wait.forRelayBlockNumber(rewardAvailableInBlock(stake2.block));
+
+      const results = await helper.admin.payoutStakers(palletAdmin, 100);
+      const stakersEvents = results.filter(ev => ev.staker === staker1.address || ev.staker === staker2.address);
+      expect(stakersEvents).has.length(2);
+      expect(stakersEvents).has.not.ordered.members([
+        {staker: staker1.address, stake: STAKE1, payout: calculateIncome(STAKE1) - STAKE1},
+        {staker: staker2.address, stake: STAKE2, payout: calculateIncome(STAKE2) - STAKE2},
+      ]);
+    });
+  });
+});
+
+
+// Sometimes is is required to make a cycle in order for the payment to be calculated for a specific account
+async function payUntilRewardFor(account: string, helper: DevUniqueHelper) {
+  for(let i = 0; i < 3; i++) {
+    const payouts = await helper.admin.payoutStakers(palletAdmin, 100);
+    const accountPayout = payouts.find(p => p.staker === account);
+    if(accountPayout) return accountPayout;
+  }
+  throw Error(`Cannot find payout for ${account}`);
+}
+
+function calculateIncome(base: bigint, iter = 0, calcPeriod: bigint = UNLOCKING_PERIOD): bigint {
+  const DAY = 7200n;
+  const ACCURACY = 1_000_000_000n;
+  // 5n / 10_000n = 0.05% p/day
+  const income = base + base * (ACCURACY * (calcPeriod * 5n) / (10_000n * DAY)) / ACCURACY ;
+
+  if(iter > 1) {
+    return calculateIncome(income, iter - 1, calcPeriod);
+  } else return income;
+}
+
+function rewardAvailableInBlock(stakedInBlock: bigint) {
+  if(stakedInBlock % LOCKING_PERIOD === 0n) return stakedInBlock + LOCKING_PERIOD;
+  return (stakedInBlock - stakedInBlock % LOCKING_PERIOD) + (LOCKING_PERIOD * 2n);
+}
+
+// Wait while promotion period less than specified block, to avoid boundary cases
+// 0 if this should be the beginning of the period.
+async function waitPromotionPeriodDoesntEnd(helper: DevUniqueHelper, waitBlockLessThan = LOCKING_PERIOD / 3n) {
+  const relayBlockNumber = (await helper.callRpc('api.query.parachainSystem.validationData', [])).value.relayParentNumber.toNumber(); // await helper.chain.getLatestBlockNumber();
+  const currentPeriodBlock = BigInt(relayBlockNumber) % LOCKING_PERIOD;
+
+  if(currentPeriodBlock > waitBlockLessThan) {
+    await helper.wait.forRelayBlockNumber(BigInt(relayBlockNumber) + LOCKING_PERIOD - currentPeriodBlock);
+  }
+}
addedjs-packages/tests/sub/nesting/admin.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/nesting/admin.test.ts
@@ -0,0 +1,87 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
+import {CrossAccountId} from '@unique/playgrounds/unique.js';
+
+describe('Nesting by collection admin', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 10n, 10n], donor);
+    });
+  });
+
+  [
+    {restricted: true},
+    {restricted: false},
+  ].map(testCase => {
+    itSub(`can nest tokens if "collectionAdmin" permission set ${testCase.restricted ? ', in restricted mode' : ''}`, async ({helper}) => {
+      const collectionA = await helper.nft.mintCollection(alice);
+      await collectionA.addAdmin(alice, {Substrate: bob.address});
+      const collectionB = await helper.nft.mintCollection(alice);
+      await collectionB.addAdmin(alice, {Substrate: bob.address});
+      // Collection has permission for collectionAdmin to nest:
+      await collectionA.setPermissions(alice, {nesting:
+        {collectionAdmin: true, restricted: testCase.restricted ? [collectionA.collectionId, collectionB.collectionId] : null},
+      });
+      // Token for nesting in from collectionA:
+      const targetTokenA = await collectionA.mintToken(alice, {Substrate: charlie.address});
+
+      // 1. Create an immediately nested tokens:
+      // 1.1 From own collection:
+      const nestedTokenA = await collectionA.mintToken(bob, targetTokenA.nestingAccount());
+      // 1.2 From different collection:
+      const nestedTokenB = await collectionB.mintToken(bob, targetTokenA.nestingAccount());
+      expect(await nestedTokenA.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+      expect(await nestedTokenA.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount()));
+      expect(await nestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+      expect(await nestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount()));
+
+      // 2. Create a token to be nested and nest:
+      const newNestedTokenA = await collectionA.mintToken(bob);
+      const newNestedTokenB = await collectionB.mintToken(bob);
+      // 2.1 From own collection:
+      await newNestedTokenA.nest(bob, targetTokenA);
+      // 2.2 From different collection:
+      await newNestedTokenB.nest(bob, targetTokenA);
+      expect(await newNestedTokenB.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+      expect(await newNestedTokenB.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenA.nestingAccount()));
+    });
+  });
+
+  itSub('can operate together with token owner if "collectionAdmin" and "tokenOwner" permissions set', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {collectionAdmin: true, tokenOwner: true}}});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    const targetToken = await collection.mintToken(alice, {Substrate: charlie.address});
+
+    // Admin can create an immediately nested token:
+    const nestedToken = await collection.mintToken(bob, targetToken.nestingAccount());
+    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+
+    // Owner can create and and nest:
+    const newToken = await collection.mintToken(alice, {Substrate: charlie.address});
+    await newToken.nest(charlie, targetToken);
+    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: charlie.address});
+    expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+  });
+});
addedjs-packages/tests/sub/nesting/common.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/nesting/common.test.ts
@@ -0,0 +1,164 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
+import {CrossAccountId, UniqueNFTCollection, UniqueRFTCollection} from '@unique/playgrounds/unique.js';
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+
+describe('Common nesting tests', () => {
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, restrictedMode: true, requiredPallets: []},
+    {mode: 'nft' as const, restrictedMode: false, requiredPallets: []},
+    {mode: 'rft' as const, restrictedMode: true, requiredPallets: [Pallets.ReFungible]},
+    {mode: 'rft' as const, restrictedMode: false, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase => {
+    itSub.ifWithPallets(`Token owner can nest ${testCase.mode.toUpperCase()} in NFT if "tokenOwner" permission set ${testCase.restrictedMode ? 'in restricted mode': ''}`, testCase.requiredPallets, async ({helper}) => {
+      // Only NFT can be target for nesting in
+      const targetNFTCollection = await helper.nft.mintCollection(alice);
+      const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
+
+      const collectionForNesting = await helper[testCase.mode].mintCollection(bob);
+      // permissions should be set:
+      await targetNFTCollection.setPermissions(alice, {
+        nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null},
+      });
+
+      // 1. Bob can immediately create nested token:
+      const nestedToken1 = testCase.mode === 'nft'
+        ? await (collectionForNesting as UniqueNFTCollection).mintToken(bob, targetTokenBob.nestingAccount())
+        : await (collectionForNesting as UniqueRFTCollection).mintToken(bob, 10n, targetTokenBob.nestingAccount());
+      expect(await nestedToken1.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+      expect(await nestedToken1.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount()));
+
+      // 2. Bob can mint and nest token:
+      const nestedToken2 = await collectionForNesting.mintToken(bob);
+      await nestedToken2.nest(bob, targetTokenBob);
+      expect(await nestedToken2.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+      expect(await nestedToken2.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetTokenBob.nestingAccount()));
+    });
+  });
+
+
+  [
+    {restrictedMode: true},
+    {restrictedMode: false},
+  ].map(testCase => {
+    itSub(`Token owner can nest FT in NFT if "tokenOwner" permission set  ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => {
+      // Only NFT allows nesting, permissions should be set:
+      const targetNFTCollection = await helper.nft.mintCollection(alice);
+      const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
+
+      const collectionForNesting = await helper.ft.mintCollection(bob);
+      // permissions should be set:
+      await targetNFTCollection.setPermissions(alice, {
+        nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null},
+      });
+
+      // 1. Alice can immediately create nested tokens:
+      await collectionForNesting.mint(bob, 100n, targetTokenBob.nestingAccount());
+      expect(await collectionForNesting.getTop10Owners()).deep.eq([CrossAccountId.toLowerCase(targetTokenBob.nestingAccount())]);
+      expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(100n);
+
+      // 2. Alice can mint and nest token:
+      await collectionForNesting.mint(bob, 100n);
+      await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n);
+      expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(150n);
+      expect(await targetTokenBob.getChildren()).to.be.deep.equal([{collectionId: collectionForNesting.collectionId, tokenId: 0}]);
+    });
+  });
+
+  [
+    {restrictedMode: true},
+    {restrictedMode: false},
+  ].map(testCase => {
+    itSub(`Token owner can nest Native FT in NFT if "tokenOwner" permission set  ${testCase.restrictedMode ? 'in restricted mode': ''}`, async ({helper}) => {
+      // Only NFT allows nesting, permissions should be set:
+      const targetNFTCollection = await helper.nft.mintCollection(alice);
+      const targetTokenBob = await targetNFTCollection.mintToken(alice, {Substrate: bob.address});
+      expect(await targetTokenBob.getChildren()).to.be.empty;
+
+      const collectionForNesting = helper.ft.getCollectionObject(0);
+      // permissions should be set:
+      await targetNFTCollection.setPermissions(alice, {
+        nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionForNesting.collectionId] : null},
+      });
+
+      // Bob can nest Native FT into their NFT:
+      await collectionForNesting.transfer(bob, targetTokenBob.nestingAccount(), 50n);
+      expect(await collectionForNesting.getBalance(targetTokenBob.nestingAccount())).eq(50n);
+      // Native FT should't be visible in NFT children:
+      expect(await targetTokenBob.getChildren()).to.be.deep.equal([]);
+    });
+  });
+
+
+  itSub.ifWithPallets('Owner can unnest tokens using transferFrom', [Pallets.ReFungible], async ({helper}) => {
+    const collectionToNest = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const tokenA = await collectionToNest.mintToken(alice);
+    const tokenB = await collectionToNest.mintToken(alice);
+
+    // Create a nested token
+    const nftCollectionToBeNested = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const rftCollectionToBeNested = await helper.rft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const ftCollectionToBeNested = await helper.ft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const nativeFtCollectionToBeNested = helper.ft.getCollectionObject(0);
+
+    const nestedNFT = await nftCollectionToBeNested.mintToken(alice, tokenA.nestingAccount());
+    const nestedRFT = await rftCollectionToBeNested.mintToken(alice, 100n, tokenA.nestingAccount());
+    await ftCollectionToBeNested.mint(alice, 100n, tokenA.nestingAccount());
+    await nativeFtCollectionToBeNested.transfer(alice, tokenA.nestingAccount(), 100n);
+    expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount()));
+    expect(await nestedRFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenA.nestingAccount()));
+    expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n);
+    expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(100n);
+
+    expect(await tokenA.getChildren()).to.be.length(3);
+    expect(await tokenB.getChildren()).to.be.length(0);
+
+    // Transfer the nested token to another token
+    await nestedNFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount());
+    await nestedRFT.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n);
+    await ftCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n);
+    await nativeFtCollectionToBeNested.transferFrom(alice, tokenA.nestingAccount(), tokenB.nestingAccount(), 25n);
+
+    expect(await nestedNFT.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+    expect(await nestedNFT.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(tokenB.nestingAccount()));
+
+    expect(await nestedRFT.getBalance(tokenB.nestingAccount())).to.equal(25n);
+    expect(await nestedRFT.getBalance(tokenA.nestingAccount())).to.equal(75n);
+
+    expect(await ftCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n);
+    expect(await ftCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n);
+
+    expect(await nativeFtCollectionToBeNested.getBalance(tokenB.nestingAccount())).to.equal(25n);
+    expect(await nativeFtCollectionToBeNested.getBalance(tokenA.nestingAccount())).to.equal(75n);
+
+    // RFT, FT, and without native FT
+    expect(await tokenA.getChildren()).to.be.length(2);
+    // NFT, RFT, FT, and without native FT
+    expect(await tokenB.getChildren()).to.be.length(3);
+  });
+});
addedjs-packages/tests/sub/nesting/e2e.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/nesting/e2e.test.ts
@@ -0,0 +1,145 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
+import {CrossAccountId} from '@unique/playgrounds/unique.js';
+
+describe('Composite nesting tests', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
+    });
+  });
+
+  itSub('Checks token children e2e', async ({helper}) => {
+    const collectionA = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const collectionB = await helper.ft.mintCollection(alice);
+    const collectionC = await helper.rft.mintCollection(alice);
+    const collectionNative = helper.ft.getCollectionObject(0);
+
+    const targetToken = await collectionA.mintToken(alice);
+    expect((await targetToken.getChildren()).length).to.be.equal(0, 'Children length check at creation');
+
+    // Create a nested NFT token
+    const tokenA = await collectionA.mintToken(alice, targetToken.nestingAccount());
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+    ]).and.has.length(1);
+
+    // Create then nest
+    const tokenB = await collectionA.mintToken(alice);
+    await tokenB.nest(alice, targetToken);
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+      {tokenId: tokenB.tokenId, collectionId: collectionA.collectionId},
+    ]).and.has.length(2);
+
+    // Move token B to a different user outside the nesting tree
+    await tokenB.unnest(alice, targetToken, {Substrate: bob.address});
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+    ]).and.has.length(1);
+
+    // Create a fungible token in another collection and then nest
+    await collectionB.mint(alice, 10n);
+    await collectionB.transfer(alice, targetToken.nestingAccount(), 2n);
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+      {tokenId: 0, collectionId: collectionB.collectionId},
+    ]).and.has.length(2);
+
+    // Create a refungible token in another collection and then nest
+    const tokenC = await collectionC.mintToken(alice, 10n);
+    await tokenC.transfer(alice, targetToken.nestingAccount(), 2n);
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+      {tokenId: 0, collectionId: collectionB.collectionId},
+      {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId},
+    ]).and.has.length(3);
+
+    // Nest native fungible token into another collection
+    await collectionNative.transfer(alice, targetToken.nestingAccount(), 2n);
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+      {tokenId: 0, collectionId: collectionB.collectionId},
+      {tokenId: tokenC.tokenId, collectionId: collectionC.collectionId},
+    ]).and.has.length(3);
+
+    // Burn all nested pieces
+    await tokenC.burnFrom(alice, targetToken.nestingAccount(), 2n);
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+      {tokenId: 0, collectionId: collectionB.collectionId},
+    ])
+      .and.has.length(2);
+
+    // Move part of the fungible token inside token A deeper in the nesting tree
+    await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n);
+    expect(await targetToken.getChildren()).to.be.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+      {tokenId: 0, collectionId: collectionB.collectionId},
+    ]).and.has.length(2);
+    // Nested token also has children now:
+    expect(await tokenA.getChildren()).to.have.deep.members([
+      {tokenId: 0, collectionId: collectionB.collectionId},
+    ]).and.has.length(1);
+
+    // Move the remaining part of the fungible token inside token A deeper in the nesting tree
+    await collectionB.transferFrom(alice, targetToken.nestingAccount(), tokenA.nestingAccount(), 1n);
+    expect(await targetToken.getChildren()).to.have.deep.members([
+      {tokenId: tokenA.tokenId, collectionId: collectionA.collectionId},
+    ]).and.has.length(1);
+    expect(await tokenA.getChildren()).to.have.deep.members([
+      {tokenId: 0, collectionId: collectionB.collectionId},
+    ]).and.has.length(1);
+  });
+
+  /// TODO review this test
+  itSub('Performs the full suite: bundles a token, transfers, and unnests', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    // Create an immediately nested token
+    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+
+    // Create a token to be nested
+    const newToken = await collection.mintToken(alice);
+
+    // Nest
+    await newToken.nest(alice, targetToken);
+    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+    expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+
+    // Move bundle to different user
+    await targetToken.transfer(alice, {Substrate: bob.address});
+    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+    expect(await newToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+
+    // Unnest
+    await newToken.unnest(bob, targetToken, {Substrate: bob.address});
+    expect(await newToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+    expect(await newToken.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+});
addedjs-packages/tests/sub/nesting/nesting.negative.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/nesting/nesting.negative.test.ts
@@ -0,0 +1,294 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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 {itEth} from '../../eth/util/index.js';
+
+let alice: IKeyringPair;
+let bob: IKeyringPair;
+let charlie: IKeyringPair;
+
+describe('Negative Test: Nesting', () => {
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase => {
+    itSub.ifWithPallets(`Owner cannot nest ${testCase.mode.toUpperCase()} if nesting is disabled`, testCase.requiredPallets, async ({helper}) => {
+      // Create default collection, permissions are not set:
+      const aliceNFTCollection = await helper.nft.mintCollection(alice);
+      const targetToken = await aliceNFTCollection.mintToken(alice);
+
+      const collectionForNesting = await helper[testCase.mode].mintCollection(alice);
+
+      // 1. Alice cannot create immediately nested tokens:
+      const nestingTx = testCase.mode === 'nft'
+        ? (collectionForNesting as UniqueNFTCollection).mintToken(alice, targetToken.nestingAccount())
+        : (collectionForNesting as UniqueRFTCollection).mintToken(alice, 10n, targetToken.nestingAccount());
+      await expect(nestingTx).to.be.rejectedWith('common.UserIsNotAllowedToNest');
+
+      // 2. Alice cannot mint and nest token:
+      const nestedToken2 = await collectionForNesting.mintToken(alice);
+      await expect(nestedToken2.nest(alice, targetToken)).to.be.rejectedWith('common.UserIsNotAllowedToNest');
+    });
+  });
+
+  [
+    {mode: 'ft'},
+    {mode: 'nativeFt'},
+  ].map(testCase => {
+    itSub(`Owner cannot nest [${testCase.mode}] if nesting is disabled (except for native fungible collection)`, async ({helper}) => {
+    // Create default collection, permissions are not set:
+      const aliceNFTCollection = await helper.nft.mintCollection(alice);
+      const targetToken = await aliceNFTCollection.mintToken(alice);
+
+      const collectionForNesting = testCase.mode === 'ft' ? await helper.ft.mintCollection(alice) : helper.ft.getCollectionObject(0);
+
+      // Alice cannot create immediately nested tokens:
+      if(testCase.mode === 'ft') {
+        await expect(collectionForNesting.mint(alice, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest');
+      } else {
+        await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 100n)).to.be.not.rejected;
+      }
+
+      // Alice can't mint and nest tokens:
+      if(testCase.mode === 'ft') {
+        await collectionForNesting.mint(alice, 100n);
+      }
+
+      if(testCase.mode === 'ft') {
+        await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.rejectedWith('common.UserIsNotAllowedToNest');
+      } else {
+        await expect(collectionForNesting.transfer(alice, targetToken.nestingAccount(), 50n)).to.be.not.rejected;
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const},
+    {mode: 'rft' as const},
+    {mode: 'ft' as const},
+    {mode: 'native ft' as const},
+  ].map(testCase => {
+    itSub(`Non-owner and non-admin cannot nest ${testCase.mode.toUpperCase()} in someone else's tokens (except for native fungible collection)`, async ({helper}) => {
+      const targetCollection = await helper.nft.mintCollection(alice, {permissions:
+        {nesting: {tokenOwner: true, collectionAdmin: true}},
+      });
+      const targetToken = await targetCollection.mintToken(alice);
+
+      const nestedCollectionBob = await (
+        testCase.mode === 'native ft'
+          ? helper.ft.getCollectionObject(0)
+          : helper[testCase.mode].mintCollection(bob)
+      );
+
+      let nestedTokenBob: UniqueNFToken | UniqueRFToken;
+      switch (testCase.mode) {
+        case 'nft': nestedTokenBob = await (nestedCollectionBob as UniqueNFTCollection).mintToken(bob); break;
+        case 'rft': nestedTokenBob = await (nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n); break;
+        case 'ft': await (nestedCollectionBob as UniqueFTCollection).mint(bob, 100n); break;
+        case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n)).to.be.rejectedWith('common.UnsupportedOperation'); break;
+      }
+
+      // Bob non-owner of targetToken and non admin of targetCollection, so
+      // 1. cannot mint nested token:
+      switch (testCase.mode) {
+        case 'nft': await expect((nestedCollectionBob as UniqueNFTCollection).mintToken(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
+        case 'rft': await expect((nestedCollectionBob as UniqueRFTCollection).mintToken(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
+        case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
+        case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).mint(bob, 100n, targetToken.nestingAccount())).to.be.rejectedWith('common.UnsupportedOperation'); break;
+      }
+
+      // 2. cannot nest existing token:
+      switch (testCase.mode) {
+        case 'nft':
+        case 'rft': await expect(nestedTokenBob!.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
+        case 'ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.rejectedWith('common.UserIsNotAllowedToNest'); break;
+        case 'native ft': await expect((nestedCollectionBob as UniqueFTCollection).transfer(bob, targetToken.nestingAccount(), 100n)).to.be.not.rejected; break;
+      }
+    });
+  });
+
+  [
+    {mode: 'nft' as const, nesting: {tokenOwner: true,  collectionAdmin: false}},
+    {mode: 'nft' as const, nesting: {tokenOwner: false, collectionAdmin: true}},
+  ].map(testCase => {
+    itSub(`${testCase.nesting.tokenOwner ? 'Admin' : 'Token owner'} cannot nest when only ${testCase.nesting.tokenOwner ? 'tokenOwner' : 'collectionAdmin'} is allowed`, async ({helper}) => {
+      // Create collection with tokenOwner or create collection with collectionAdmin permission:
+      const targetCollection = await helper.nft.mintCollection(alice, {permissions: {nesting: testCase.nesting}});
+      const targetTokenCharlie = await targetCollection.mintToken(alice, {Substrate: charlie.address});
+      await targetCollection.addAdmin(alice, {Substrate: bob.address});
+
+      const nestedCollectionCharlie = await helper[testCase.mode].mintCollection(charlie);
+      const nestedCollectionBob = await helper[testCase.mode].mintCollection(bob);
+      // if nesting permissions restricted for token owner – minter is bob (admin),
+      // if collectionAdmin – charlie (owner)
+      testCase.nesting.tokenOwner
+        ? await expect(nestedCollectionBob.mintToken(bob, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/)
+        : await expect(nestedCollectionCharlie.mintToken(charlie, targetTokenCharlie.nestingAccount())).to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);
+    });
+  });
+
+  itSub.ifWithPallets('Cannot nest in non existing token (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice);
+    // To avoid UserIsNotAllowedToNest error
+    await helper.collection.setPermissions(alice, collection.collectionId, {nesting: {collectionAdmin: true}});
+
+    // The list of non-existing tokens:
+    const tokenFromNonExistingCollection = helper.nft.getTokenObject(9999999, 1);
+    const tokenBurnt = await collection.mintToken(alice);
+    await tokenBurnt.burn(alice);
+    const tokenNotMintedYet = helper.nft.getTokenObject(collection.collectionId, 2);
+
+    // The list of collections to nest tokens from:
+    const nftCollectionForNesting = await helper.nft.mintCollection(alice);
+    const rftCollectionForNesting = await helper.rft.mintCollection(alice);
+    const ftCollectionForNesting = await helper.ft.mintCollection(alice);
+    const nativeFtCollectionForNesting = helper.ft.getCollectionObject(0);
+
+    const testCases = [
+      {token: tokenFromNonExistingCollection, error: 'CollectionNotFound'},
+      {token: tokenBurnt, error: 'TokenNotFound'},
+      {token: tokenNotMintedYet, error: 'TokenNotFound'},
+    ];
+
+    for(const testCase of testCases) {
+      // 1. Alice cannot create nested token to non-existing token
+      await expect(nftCollectionForNesting.mintToken(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
+      await expect(rftCollectionForNesting.mintToken(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
+      await expect(ftCollectionForNesting.mint(alice, 10n, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
+
+      // 2. Alice cannot mint and nest token:
+      const nft = await nftCollectionForNesting.mintToken(alice);
+      const rft = await rftCollectionForNesting.mintToken(alice, 100n);
+      await ftCollectionForNesting.mint(alice, 100n);
+      await expect(nft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
+      await expect(rft.transfer(alice, testCase.token.nestingAccount())).to.be.rejectedWith(testCase.error);
+      await expect(ftCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.rejectedWith(testCase.error);
+      await expect(nativeFtCollectionForNesting.transfer(alice, testCase.token.nestingAccount(), 50n)).to.be.not.rejected;
+    }
+  });
+
+  itEth.ifWithPallets('Cannot nest in collection address', [Pallets.ReFungible], async({helper}) => {
+    const existingCollection = await helper.nft.mintCollection(alice);
+    const existingCollectionAddress = helper.ethAddress.fromCollectionId(existingCollection.collectionId);
+    const futureCollectionAddress = helper.ethAddress.fromCollectionId(99999999);
+
+    const nftCollectionForNesting = await helper.nft.mintCollection(alice);
+
+    // 1. Alice cannot create nested token to collection address
+    await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
+    await expect(nftCollectionForNesting.mintToken(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
+
+    // 2. Alice cannot mint and nest token to collection address:
+    const nft = await nftCollectionForNesting.mintToken(alice);
+    await expect(nft.transfer(alice, {Ethereum: existingCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
+    await expect(nft.transfer(alice, {Ethereum: futureCollectionAddress})).to.be.rejectedWith('CantNestTokenUnderCollection');
+  });
+
+  itEth.ifWithPallets('Cannot nest in RFT or FT (except for native fungible collection)', [Pallets.ReFungible], async ({helper}) => {
+    // Create default collection, permissions are not set:
+    const rftCollection = await helper.rft.mintCollection(alice);
+    const ftCollection = await helper.ft.mintCollection(alice);
+    const nativeFtCollection = helper.ft.getCollectionObject(0);
+
+    const rftToken = await rftCollection.mintToken(alice);
+    await ftCollection.mint(alice, 100n);
+
+    const collectionForNesting = await helper.nft.mintCollection(alice);
+
+    // 1. Alice cannot create immediately nested tokens:
+    await expect(collectionForNesting.mintToken(alice, rftToken.nestingAccount())).to.be.rejectedWith('refungible.RefungibleDisallowsNesting');
+    await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting');
+    await expect(collectionForNesting.mintToken(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.rejectedWith('common.UnsupportedOperation');
+
+    // 2. Alice cannot mint and nest token:
+    const nestedToken2 = await collectionForNesting.mintToken(alice);
+    await expect(nestedToken2.nest(alice, rftToken)).to.be.rejectedWith('refungible.RefungibleDisallowsNesting');
+    await expect(ftCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(ftCollection.collectionId, 0)})).to.be.rejectedWith('fungible.FungibleDisallowsNesting');
+    await expect(nativeFtCollection.transfer(alice, {Ethereum: helper.ethAddress.fromTokenId(nativeFtCollection.collectionId, 0)})).to.be.not.rejected;
+  });
+
+  itSub('Cannot nest in restricted collection if collection is not in the list (except native fungible collection)', async ({helper}) => {
+    const {collectionId: allowedCollectionId} = await helper.nft.mintCollection(alice);
+    const notAllowedCollectionNFT = await helper.nft.mintCollection(alice);
+    const notAllowedCollectionRFT = await helper.rft.mintCollection(alice);
+    const notAllowedCollectionFT = await helper.ft.mintCollection(alice);
+    const allowedCollectionNativeFT = helper.ft.getCollectionObject(0);
+
+    // Collection restricted to allowedCollectionId
+    const restrictedCollectionA = await helper.nft.mintCollection(alice, {permissions:
+      {nesting: {tokenOwner: true, restricted: [allowedCollectionId]}},
+    });
+    // Create collection with restricted nesting -- even self is not allowed
+    const restrictedCollectionB = await helper.nft.mintCollection(alice, {permissions:
+      {nesting: {tokenOwner: true, restricted: []}},
+    });
+
+    const targetTokenA = await restrictedCollectionA.mintToken(alice);
+    const targetTokenB = await restrictedCollectionB.mintToken(alice);
+
+    // 1. Cannot mint in own collection after allowlisting the accounts:
+    await expect(restrictedCollectionA.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+    await expect(restrictedCollectionB.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+
+    // 2. Cannot mint from notAllowedCollection:
+    await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+    await expect(notAllowedCollectionNFT.mintToken(alice, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+    await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+    await expect(notAllowedCollectionRFT.mintToken(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+    await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenA.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+    await expect(notAllowedCollectionFT.mint(alice, 100n, targetTokenB.nestingAccount())).to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);
+    await expect(allowedCollectionNativeFT.transfer(alice, targetTokenA.nestingAccount(), 100n)).to.be.not.rejected;
+    await expect(allowedCollectionNativeFT.transfer(alice, targetTokenB.nestingAccount(), 100n)).to.be.not.rejected;
+  });
+
+  itSub('Cannot create nesting chains greater than 5', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    let token = await collection.mintToken(alice);
+
+    const maxNestingLevel = 5;
+
+    // Create a nested-token matryoshka
+    for(let i = 0; i < maxNestingLevel; i++) {
+      token = await collection.mintToken(alice, token.nestingAccount());
+    }
+
+    // The nesting depth is limited by `maxNestingLevel`
+    // 1. Cannot mint:
+    await expect(collection.mintToken(alice, token.nestingAccount()))
+      .to.be.rejectedWith(/structure\.DepthLimit/);
+    // 2. Cannot transfer:
+    const anotherToken = await collection.mintToken(alice);
+    await expect(anotherToken.transfer(alice, token.nestingAccount()))
+      .to.be.rejectedWith(/structure\.DepthLimit/);
+    // 3. Cannot nest FT pieces:
+    const ftCollection = await helper.ft.mintCollection(alice);
+    await expect(ftCollection.mint(alice, 100n, token.nestingAccount()))
+      .to.be.rejectedWith(/structure\.DepthLimit/);
+
+    expect(await token.getTopmostOwner()).to.be.deep.equal({Substrate: alice.address});
+    expect(await token.getChildren()).to.has.length(0);
+  });
+});
addedjs-packages/tests/sub/nesting/refungible.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/nesting/refungible.test.ts
@@ -0,0 +1,61 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, usingPlaygrounds} from '../../util/index.js';
+
+describe('ReFungible-specific nesting tests', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice] = await helper.arrange.createAccounts([200n], donor);
+    });
+  });
+
+  itSub.ifWithPallets('ReFungible: getTopmostOwner works correctly with Nesting', [Pallets.ReFungible], async({helper}) => {
+    const collectionNFT = await helper.nft.mintCollection(alice, {
+      permissions: {
+        nesting: {
+          tokenOwner: true,
+        },
+      },
+    });
+    const collectionRFT = await helper.rft.mintCollection(alice);
+
+    const nft = await collectionNFT.mintToken(alice, {Substrate: alice.address});
+    const rft = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address});
+
+    expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});
+
+    await rft.transfer(alice, nft.nestingAccount(), 40n);
+
+    expect(await rft.getTopmostOwner()).deep.equal(null);
+
+    await rft.transfer(alice, nft.nestingAccount(), 60n);
+
+    expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});
+
+    await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 30n);
+
+    expect(await rft.getTopmostOwner()).deep.equal(null);
+
+    await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 70n);
+
+    expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});
+  });
+});
addedjs-packages/tests/sub/nesting/unnesting.negative.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/nesting/unnesting.negative.test.ts
@@ -0,0 +1,88 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, usingPlaygrounds} from '../../util/index.js';
+import {CrossAccountId} from '@unique/playgrounds/unique.js';
+
+describe('Negative Test: Unnesting', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor);
+    });
+  });
+
+  // TODO: make this test a bit more generic
+  itSub('Admin (NFT): disallows an Admin to unnest someone else\'s token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {limits: {ownerCanTransfer: true}, permissions: {access: 'AllowList', mintMode: true, nesting: {collectionAdmin: true}}});
+    //await collection.addAdmin(alice, {Substrate: bob.address});
+    const targetToken = await collection.mintToken(alice, {Substrate: bob.address});
+    await collection.addToAllowList(alice, {Substrate: bob.address});
+    await collection.addToAllowList(alice, targetToken.nestingAccount());
+
+    // Try to nest somebody else's token
+    const newToken = await collection.mintToken(bob);
+    await expect(newToken.nest(alice, targetToken))
+      .to.be.rejectedWith(/common\.NoPermission/);
+
+    // Try to unnest a token belonging to someone else as collection admin
+    const nestedToken = await collection.mintToken(alice, targetToken.nestingAccount());
+    await expect(nestedToken.unnest(alice, targetToken, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.AddressNotInAllowlist/);
+
+    expect(await targetToken.getChildren()).to.be.length(1);
+    expect(await nestedToken.getTopmostOwner()).to.be.deep.equal({Substrate: bob.address});
+    expect(await nestedToken.getOwner()).to.be.deep.equal(CrossAccountId.toLowerCase(targetToken.nestingAccount()));
+  });
+
+  [
+    {mode: 'ft' as const},
+    {mode: 'native ft' as const},
+  ].map(md => [
+    {mode: md.mode, restrictedMode: true},
+    {mode: md.mode, restrictedMode: false},
+  ].map(testCase => {
+    itSub(`Fungible: disallows a non-Owner to unnest someone else's token [${testCase.mode}${testCase.restrictedMode ? ' (Restricted nesting)' : ''}]`, async ({helper}) => {
+      const collectionNFT = await helper.nft.mintCollection(alice);
+      const collectionFT = await (
+        testCase.mode === 'ft'
+          ? helper.ft.mintCollection(alice)
+          : helper.ft.getCollectionObject(0)
+      );
+      const targetToken = await collectionNFT.mintToken(alice, {Substrate: bob.address});
+
+      await collectionNFT.setPermissions(alice, {nesting: {
+        collectionAdmin: true, tokenOwner: true, restricted: testCase.restrictedMode ? [collectionFT.collectionId] : null,
+      }});
+
+      // Nest some tokens as Alice into Bob's token
+      await (
+        testCase.mode === 'ft'
+          ? collectionFT.mint(alice, 5n, targetToken.nestingAccount())
+          : collectionFT.transfer(alice, targetToken.nestingAccount(), 5n)
+      );
+
+      // Try to pull it out as Alice still
+      await expect(collectionFT.transferFrom(alice, targetToken.nestingAccount(), {Substrate: bob.address}, 1n))
+        .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+      expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
+    });
+  }));
+});
addedjs-packages/tests/sub/refungible/burn.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/refungible/burn.test.ts
@@ -0,0 +1,127 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
+
+describe('Refungible: burn', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
+    });
+  });
+
+  itSub('can burn some pieces', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
+    await token.burn(alice, 99n);
+    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n);
+  });
+
+  itSub('can burn all pieces', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+
+    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
+
+    await token.burn(alice, 100n);
+    expect(await collection.doesTokenExist(token.tokenId)).to.be.false;
+  });
+
+  itSub('burn pieces for multiple users', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+
+    await token.transfer(alice, {Substrate: bob.address}, 60n);
+
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n);
+    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n);
+
+    await token.burn(alice, 40n);
+
+    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n);
+
+    await token.burn(bob, 59n);
+
+    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n);
+    expect(await collection.doesTokenExist(token.tokenId)).to.be.true;
+
+    await token.burn(bob, 1n);
+
+    expect(await collection.doesTokenExist(token.tokenId)).to.be.false;
+  });
+
+  itSub('burn pieces by admin', async function({helper}) {
+    const collection = await helper.rft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+    const token = await collection.mintToken(alice, 100n);
+
+    await token.burnFrom(bob, {Substrate: alice.address}, 100n);
+    expect(await token.doesExist()).to.be.false;
+  });
+});
+
+describe('Refungible: burn negative tests', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
+    });
+  });
+
+  itSub('cannot burn non-owned token pieces', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice);
+    const aliceToken = await collection.mintToken(alice, 10n, {Substrate: alice.address});
+    const bobToken = await collection.mintToken(alice, 10n, {Substrate: bob.address});
+
+    // 1. Cannot burn non-owned token:
+    await expect(bobToken.burn(alice, 0n)).to.be.rejectedWith('common.TokenValueTooLow');
+    await expect(bobToken.burn(alice, 5n)).to.be.rejectedWith('common.TokenValueTooLow');
+    // 2. Cannot burn non-existing token:
+    await expect(helper.rft.burnToken(alice, 99999, 10)).to.be.rejectedWith('common.CollectionNotFound');
+    await expect(helper.rft.burnToken(alice, collection.collectionId, 99999)).to.be.rejectedWith('common.TokenValueTooLow');
+    // 3. Can burn zero amount of owned tokens (EIP-20)
+    await aliceToken.burn(alice, 0n);
+
+    // 4. Storage is not corrupted:
+    expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]);
+    expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]);
+
+    // 4.1 Tokens can be transfered:
+    await aliceToken.transfer(alice, {Substrate: bob.address}, 10n);
+    await bobToken.transfer(bob, {Substrate: alice.address}, 10n);
+    expect(await aliceToken.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]);
+    expect(await bobToken.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]);
+  });
+});
addedjs-packages/tests/sub/refungible/nesting.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/refungible/nesting.test.ts
@@ -0,0 +1,148 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {expect, itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../../util/index.js';
+
+describe('Refungible nesting', () => {
+  let alice: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      const donor = await privateKey({url: import.meta.url});
+      [alice, charlie] = await helper.arrange.createAccounts([50n, 10n], donor);
+    });
+  });
+
+  [
+    {restrictedMode: true},
+    {restrictedMode: false},
+  ].map(testCase => {
+    itSub(`Owner can nest their token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => {
+      const collectionNFT = await helper.nft.mintCollection(alice, {permissions: {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true}}});
+      const collectionRFT = await helper.rft.mintCollection(alice);
+      const targetToken = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
+
+      await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}});
+      await collectionNFT.addToAllowList(alice, {Substrate: charlie.address});
+      await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
+
+      await collectionRFT.setPermissions(alice, {access: 'AllowList', mintMode: true});
+      await collectionRFT.addToAllowList(alice, {Substrate: charlie.address});
+      await collectionRFT.addToAllowList(alice, targetToken.nestingAccount());
+
+      // Create an immediately nested token
+      const nestedToken = await collectionRFT.mintToken(charlie, 5n, targetToken.nestingAccount());
+      expect(await nestedToken.getBalance(targetToken.nestingAccount())).to.be.equal(5n);
+
+      // Create a token to be nested and nest
+      const newToken = await collectionRFT.mintToken(charlie, 5n);
+      await newToken.transfer(charlie, targetToken.nestingAccount(), 2n);
+      expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(2n);
+      expect(await newToken.getBalance({Substrate: charlie.address})).to.be.equal(3n);
+    });
+  });
+
+  itSub('Owner can unnest nested token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    // Owner mints nested RFT token:
+    const collectionRFT = await helper.rft.mintCollection(alice);
+    const token = await collectionRFT.mintToken(alice, 10n, targetToken.nestingAccount());
+
+    // 1.1 Owner can partially unnest token pieces with transferFrom:
+    await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 9n);
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(9n);
+    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
+
+    // 1.2 Owner can unnest all pieces:
+    await token.transferFrom(alice, targetToken.nestingAccount(), {Substrate: alice.address}, 1n);
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(10n);
+    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
+  });
+
+  itSub('Owner can burn nested token pieces', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {permissions: {nesting: {tokenOwner: true}}});
+    const targetToken = await collection.mintToken(alice);
+
+    // Owner mints nested RFT token:
+    const collectionRFT = await helper.rft.mintCollection(alice);
+    const token = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address});
+    await token.transfer(alice, targetToken.nestingAccount(), 30n);
+
+    // 1.1 Owner can partially burnFrom nested pieces:
+    await token.burnFrom(alice, targetToken.nestingAccount(), 10n);
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(70n);
+    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(20n);
+    expect(await targetToken.getChildren()).to.has.length(1);
+
+    // 1.1 Owner can burnFrom all nested pieces:
+    await token.burnFrom(alice, targetToken.nestingAccount(), 20n);
+    expect(await token.getBalance(targetToken.nestingAccount())).to.be.equal(0n);
+    expect(await targetToken.getChildren()).to.has.length(0);
+    expect(await token.doesExist()).to.be.true;
+
+    // 2. Target token does not contain any pieces and can be burnt:
+    await targetToken.burn(alice);
+    expect(await targetToken.doesExist()).to.be.false;
+  });
+});
+
+describe('Refungible nesting negative tests', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 50n], donor);
+    });
+  });
+
+  [
+    {restrictedMode: true},
+    {restrictedMode: false},
+  ].map(testCase => {
+    itSub(`non-Owner cannot nest someone else's token${testCase.restrictedMode ? ': Restricted mode' : ''}`, async ({helper}) => {
+      const collectionNFT = await helper.nft.mintCollection(alice);
+      const collectionRFT = await helper.rft.mintCollection(alice);
+      const targetToken = await collectionNFT.mintToken(alice);
+
+      await collectionNFT.setPermissions(alice, {access: 'AllowList', mintMode: true, nesting: {tokenOwner: true, restricted: testCase.restrictedMode ? [collectionRFT.collectionId] : null}});
+      await collectionNFT.addToAllowList(alice, {Substrate: bob.address});
+      await collectionNFT.addToAllowList(alice, targetToken.nestingAccount());
+
+      // Try to create a token to be nested and nest
+      const newToken = await collectionRFT.mintToken(alice);
+      await expect(newToken.transfer(bob, targetToken.nestingAccount())).to.be.rejectedWith(/common\.TokenValueTooLow/);
+
+      expect(await targetToken.getChildren()).to.be.length(0);
+      expect(await newToken.getBalance({Substrate: alice.address})).to.be.equal(1n);
+
+      // Nest some tokens as Alice into Bob's token
+      await newToken.transfer(alice, targetToken.nestingAccount());
+
+      // Try to pull it out
+      await expect(newToken.transferFrom(bob, targetToken.nestingAccount(), {Substrate: alice.address}, 1n))
+        .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+      expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(1n);
+    });
+  });
+});
addedjs-packages/tests/sub/refungible/repartition.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/refungible/repartition.test.ts
@@ -0,0 +1,96 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
+
+describe('integration test: Refungible functionality:', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([100n, 10n], donor);
+    });
+  });
+
+  itSub('Repartition', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+
+    expect(await token.repartition(alice, 200n)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(200n);
+    expect(await token.getTotalPieces()).to.be.equal(200n);
+
+    expect(await token.transfer(alice, {Substrate: bob.address}, 110n)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(90n);
+    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(110n);
+
+    await expect(token.repartition(alice, 80n))
+      .to.eventually.be.rejectedWith(/refungible\.RepartitionWhileNotOwningAllPieces/);
+
+    expect(await token.transfer(alice, {Substrate: bob.address}, 90n)).to.be.true;
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n);
+    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(200n);
+
+    expect(await token.repartition(bob, 150n)).to.be.true;
+    await expect(token.transfer(bob, {Substrate: alice.address}, 160n))
+      .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub('Repartition with increased amount', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+    await token.repartition(alice, 200n);
+    const chainEvents = helper.chainLog.slice(-1)[0].events;
+    const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemCreated');
+    expect(event).to.deep.include({
+      section: 'common',
+      method: 'ItemCreated',
+      index: [66, 2],
+      data: [
+        collection.collectionId,
+        token.tokenId,
+        {substrate: alice.address},
+        100n,
+      ],
+    });
+  });
+
+  itSub('Repartition with decreased amount', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+    await token.repartition(alice, 50n);
+    const chainEvents = helper.chainLog.slice(-1)[0].events;
+    const event = chainEvents.find((event: any) => event.section === 'common' && event.method === 'ItemDestroyed');
+    expect(event).to.deep.include({
+      section: 'common',
+      method: 'ItemDestroyed',
+      index: [66, 3],
+      data: [
+        collection.collectionId,
+        token.tokenId,
+        {substrate: alice.address},
+        50n,
+      ],
+    });
+  });
+});
+
addedjs-packages/tests/sub/refungible/transfer.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/sub/refungible/transfer.test.ts
@@ -0,0 +1,73 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, Pallets, requirePalletsOrSkip, usingPlaygrounds, expect} from '../../util/index.js';
+
+describe('Refungible transfer tests', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
+
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
+    });
+  });
+
+  itSub('Can transfer token pieces', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const token = await collection.mintToken(alice, 100n);
+
+    expect(await token.transfer(alice, {Substrate: bob.address}, 60n)).to.be.true;
+    // 1. Can transfer less or equal than have:
+    expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n);
+    expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n);
+  });
+
+  itSub('Cannot transfer incorrect amount of token pieces', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
+    const tokenAlice = await collection.mintToken(alice, 10n, {Substrate: alice.address});
+    const tokenBob = await collection.mintToken(alice, 10n, {Substrate: bob.address});
+
+    // 1. Alice cannot transfer Bob's token:
+    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow');
+    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow');
+    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 10n)).to.be.rejectedWith('common.TokenValueTooLow');
+    await expect(tokenBob.transfer(alice, {Substrate: charlie.address}, 100n)).to.be.rejectedWith('common.TokenValueTooLow');
+
+    // 2. Alice cannot transfer non-existing token:
+    await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 0n)).to.be.rejectedWith('common.TokenValueTooLow');
+    await expect(collection.transferToken(alice, 100, {Substrate: charlie.address}, 1n)).to.be.rejectedWith('common.TokenValueTooLow');
+
+    // 3. Cannot transfer more than have:
+    await expect(tokenAlice.transfer(alice, {Substrate: bob.address}, 11n))
+      .to.eventually.be.rejectedWith(/common\.TokenValueTooLow/);
+
+    // 4. Zero transfer allowed (EIP-20):
+    await tokenAlice.transfer(alice, {Substrate: charlie.address}, 0n);
+
+    expect(await tokenAlice.getTop10Owners()).to.deep.eq([{Substrate: alice.address}]);
+    expect(await tokenBob.getTop10Owners()).to.deep.eq([{Substrate: bob.address}]);
+    expect(await tokenAlice.getBalance({Substrate: alice.address})).to.eq(10n);
+    expect(await tokenBob.getBalance({Substrate: bob.address})).to.eq(10n);
+    expect(await tokenBob.getBalance({Substrate: charlie.address})).to.eq(0n);
+  });
+});
addedjs-packages/tests/transfer.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/transfer.test.ts
@@ -0,0 +1,345 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itEth, usingEthPlaygrounds} from './eth/util/index.js';
+import {itSub, Pallets, usingPlaygrounds, expect} from './util/index.js';
+import {NON_EXISTENT_COLLECTION_ID} from '@unique/playgrounds/types.js';
+
+describe('Integration Test Transfer(recipient, collection_id, item_id, value)', () => {
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
+    });
+  });
+
+  itSub('Balance transfers and check balance', async ({helper}) => {
+    const alicesBalanceBefore = await helper.balance.getSubstrate(alice.address);
+    const bobsBalanceBefore = await helper.balance.getSubstrate(bob.address);
+
+    expect(await helper.balance.transferToSubstrate(alice, bob.address, 1n)).to.be.true;
+
+    const alicesBalanceAfter = await helper.balance.getSubstrate(alice.address);
+    const bobsBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+    expect(alicesBalanceAfter < alicesBalanceBefore).to.be.true;
+    expect(bobsBalanceAfter > bobsBalanceBefore).to.be.true;
+  });
+
+  itSub('Inability to pay fees error message is correct', async ({helper}) => {
+    const [zero] = await helper.arrange.createAccounts([0n], donor);
+
+    // console.error = () => {};
+    // The following operation throws an error into the console and the logs. Pay it no heed as long as the test succeeds.
+    await expect(helper.balance.transferToSubstrate(zero, donor.address, 1n))
+      .to.be.rejectedWith('Inability to pay some fees , e.g. account balance too low');
+  });
+
+  itSub('[nft] User can transfer owned token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-1-NFT', description: '', tokenPrefix: 'T'});
+    const nft = await collection.mintToken(alice);
+
+    await nft.transfer(alice, {Substrate: bob.address});
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: bob.address});
+  });
+
+  itSub('[fungible] User can transfer owned token', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-1-FT', description: '', tokenPrefix: 'T'});
+    await collection.mint(alice, 10n);
+
+    await collection.transfer(alice, {Substrate: bob.address}, 9n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n);
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n);
+  });
+
+  itSub.ifWithPallets('[refungible] User can transfer owned token', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await rft.transfer(alice, {Substrate: bob.address}, 9n);
+    expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n);
+    expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n);
+  });
+
+  itSub('[nft] Collection admin can transfer owned token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-2-NFT', description: '', tokenPrefix: 'T'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    const nft = await collection.mintToken(bob, {Substrate: bob.address});
+    await nft.transfer(bob, {Substrate: alice.address});
+
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub('[fungible] Collection admin can transfer owned token', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-2-FT', description: '', tokenPrefix: 'T'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    await collection.mint(bob, 10n, {Substrate: bob.address});
+    await collection.transfer(bob, {Substrate: alice.address}, 1n);
+
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(9n);
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(1n);
+  });
+
+  itSub.ifWithPallets('[refungible] Collection admin can transfer owned token', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-2-RFT', description: '', tokenPrefix: 'T'});
+    await collection.addAdmin(alice, {Substrate: bob.address});
+
+    const rft = await collection.mintToken(bob, 10n, {Substrate: bob.address});
+    await rft.transfer(bob, {Substrate: alice.address}, 1n);
+
+    expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(9n);
+    expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(1n);
+  });
+});
+
+describe('Negative Integration Test Transfer(recipient, collection_id, item_id, value)', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob] = await helper.arrange.createAccounts([50n, 10n], donor);
+    });
+  });
+
+
+  itSub('[nft] Transfer with not existed collection_id', async ({helper}) => {
+    await expect(helper.nft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('[fungible] Transfer with not existed collection_id', async ({helper}) => {
+    await expect(helper.ft.transfer(alice, NON_EXISTENT_COLLECTION_ID, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub.ifWithPallets('[refungible] Transfer with not existed collection_id', [Pallets.ReFungible], async ({helper}) => {
+    await expect(helper.rft.transferToken(alice, NON_EXISTENT_COLLECTION_ID, 1, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('[nft] Transfer with deleted collection_id', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-1-NFT', description: '', tokenPrefix: 'T'});
+    const nft = await collection.mintToken(alice);
+
+    await nft.burn(alice);
+    await collection.burn(alice);
+
+    await expect(nft.transfer(alice, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('[fungible] Transfer with deleted collection_id', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-1-FT', description: '', tokenPrefix: 'T'});
+    await collection.mint(alice, 10n);
+
+    await collection.burnTokens(alice, 10n);
+    await collection.burn(alice);
+
+    await expect(collection.transfer(alice, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub.ifWithPallets('[refungible] Transfer with deleted collection_id', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-1-RFT', description: '', tokenPrefix: 'T'});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await rft.burn(alice, 10n);
+    await collection.burn(alice);
+
+    await expect(rft.transfer(alice, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  itSub('[nft] Transfer with not existed item_id', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-2-NFT', description: '', tokenPrefix: 'T'});
+    await expect(collection.transferToken(alice, 1, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.TokenNotFound/);
+  });
+
+  itSub('[fungible] Transfer with not existed item_id', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-2-FT', description: '', tokenPrefix: 'T'});
+    await expect(collection.transfer(alice, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub.ifWithPallets('[refungible] Transfer with not existed item_id', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-2-RFT', description: '', tokenPrefix: 'T'});
+    await expect(collection.transferToken(alice, 1, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub('Zero transfer NFT', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'});
+    const tokenAlice = await collection.mintToken(alice, {Substrate: alice.address});
+    const tokenBob = await collection.mintToken(alice, {Substrate: bob.address});
+    // 1. Zero transfer of own tokens allowed:
+    await helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: bob.address}, collection.collectionId, tokenAlice.tokenId, 0]);
+    // 2. Zero transfer of non-owned tokens not allowed:
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, tokenBob.tokenId, 0])).to.be.rejectedWith('common.NoPermission');
+    // 3. Zero transfer of non-existing tokens not allowed:
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transfer', [{Substrate: alice.address}, collection.collectionId, 10, 0])).to.be.rejectedWith('common.TokenNotFound');
+    expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: alice.address});
+    expect(await tokenBob.getOwner()).to.deep.eq({Substrate: bob.address});
+    // 4. Storage is not corrupted:
+    await tokenAlice.transfer(alice, {Substrate: bob.address});
+    await tokenBob.transfer(bob, {Substrate: alice.address});
+    expect(await tokenAlice.getOwner()).to.deep.eq({Substrate: bob.address});
+    expect(await tokenBob.getOwner()).to.deep.eq({Substrate: alice.address});
+  });
+
+  itSub('[nft] Transfer with deleted item_id', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-3-NFT', description: '', tokenPrefix: 'T'});
+    const nft = await collection.mintToken(alice);
+
+    await nft.burn(alice);
+
+    await expect(nft.transfer(alice, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.TokenNotFound/);
+  });
+
+  itSub('[fungible] Transfer with deleted item_id', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-3-FT', description: '', tokenPrefix: 'T'});
+    await collection.mint(alice, 10n);
+
+    await collection.burnTokens(alice, 10n);
+
+    await expect(collection.transfer(alice, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub.ifWithPallets('[refungible] Transfer with deleted item_id', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-Neg-3-RFT', description: '', tokenPrefix: 'T'});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await rft.burn(alice, 10n);
+
+    await expect(rft.transfer(alice, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub('[nft] Transfer with recipient that is not owner', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Transfer-Neg-4-NFT', description: '', tokenPrefix: 'T'});
+    const nft = await collection.mintToken(alice);
+
+    await expect(nft.transfer(bob, {Substrate: bob.address}))
+      .to.be.rejectedWith(/common\.NoPermission/);
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub('[fungible] Transfer with recipient that is not owner', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'Transfer-Neg-4-FT', description: '', tokenPrefix: 'T'});
+    await collection.mint(alice, 10n);
+
+    await expect(collection.transfer(bob, {Substrate: bob.address}, 9n))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(0n);
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(10n);
+  });
+
+  itSub.ifWithPallets('[refungible] Transfer with recipient that is not owner', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'Transfer-1-RFT', description: '', tokenPrefix: 'T'});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await expect(rft.transfer(bob, {Substrate: bob.address}, 9n))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+    expect(await rft.getBalance({Substrate: bob.address})).to.be.equal(0n);
+    expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(10n);
+  });
+});
+
+describe('Transfers to self (potentially over substrate-evm boundary)', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+    });
+  });
+
+  itEth('Transfers to self. In case of same frontend', async ({helper}) => {
+    const [owner] = await helper.arrange.createAccounts([10n], donor);
+    const collection = await helper.ft.mintCollection(owner, {});
+    await collection.mint(owner, 100n);
+
+    const ownerProxy = helper.address.substrateToEth(owner.address);
+
+    // transfer to own proxy
+    await collection.transfer(owner, {Ethereum: ownerProxy}, 10n);
+    expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n);
+    expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n);
+
+    // transfer-from own proxy to own proxy again
+    await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Ethereum: ownerProxy}, 5n);
+    expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n);
+    expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n);
+  });
+
+  itEth('Transfers to self. In case of substrate-evm boundary', async ({helper}) => {
+    const [owner] = await helper.arrange.createAccounts([10n], donor);
+    const collection = await helper.ft.mintCollection(owner, {});
+    await collection.mint(owner, 100n);
+
+    const ownerProxy = helper.address.substrateToEth(owner.address);
+
+    // transfer to own proxy
+    await collection.transfer(owner, {Ethereum: ownerProxy}, 10n);
+    expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(90n);
+    expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(10n);
+
+    // transfer-from own proxy to self
+    await collection.transferFrom(owner, {Ethereum: ownerProxy}, {Substrate: owner.address}, 5n);
+    expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(95n);
+    expect(await collection.getBalance({Ethereum: ownerProxy})).to.be.equal(5n);
+  });
+
+  itEth('Transfers to self. In case of inside substrate-evm', async ({helper}) => {
+    const [owner] = await helper.arrange.createAccounts([10n], donor);
+    const collection = await helper.ft.mintCollection(owner, {});
+    await collection.mint(owner, 100n);
+
+    // transfer to self again
+    await collection.transfer(owner, {Substrate: owner.address}, 10n);
+    expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n);
+
+    // transfer-from self to self again
+    await collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 5n);
+    expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(100n);
+  });
+
+  itEth('Transfers to self. In case of inside substrate-evm when not enought "Fungibles"', async ({helper}) => {
+    const [owner] = await helper.arrange.createAccounts([10n], donor);
+    const collection = await helper.ft.mintCollection(owner, {});
+    await collection.mint(owner, 10n);
+
+    // transfer to self again
+    await expect(collection.transfer(owner, {Substrate: owner.address}, 11n))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+
+    // transfer-from self to self again
+    await expect(collection.transferFrom(owner, {Substrate: owner.address}, {Substrate: owner.address}, 12n))
+      .to.be.rejectedWith(/common\.TokenValueTooLow/);
+    expect(await collection.getBalance({Substrate: owner.address})).to.be.equal(10n);
+  });
+});
addedjs-packages/tests/transferFrom.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/transferFrom.test.ts
@@ -0,0 +1,375 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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';
+
+describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([20n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('[nft] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-1', description: '', tokenPrefix: 'TF'});
+    const nft = await collection.mintToken(alice);
+    await nft.approve(alice, {Substrate: bob.address});
+    expect(await nft.isApproved({Substrate: bob.address})).to.be.true;
+
+    await nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address});
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address});
+  });
+
+  itSub('[fungible] Execute the extrinsic and check nftItemList - owner of token', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-2', description: '', tokenPrefix: 'TF'});
+    await collection.mint(alice, 10n);
+    await collection.approveTokens(alice, {Substrate: bob.address}, 7n);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n);
+
+    await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n);
+    expect(await collection.getBalance({Substrate: charlie.address})).to.be.equal(6n);
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(4n);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n);
+  });
+
+  itSub.ifWithPallets('[refungible] Execute the extrinsic and check nftItemList - owner of token', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-3', description: '', tokenPrefix: 'TF'});
+    const rft = await collection.mintToken(alice, 10n);
+    await rft.approve(alice, {Substrate: bob.address}, 7n);
+    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(7n);
+
+    await rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 6n);
+    expect(await rft.getBalance({Substrate: charlie.address})).to.be.equal(6n);
+    expect(await rft.getBalance({Substrate: alice.address})).to.be.equal(4n);
+    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(1n);
+  });
+
+  itSub('Should reduce allowance if value is big', async ({helper}) => {
+    // fungible
+    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-4', description: '', tokenPrefix: 'TF'});
+    await collection.mint(alice, 500000n);
+
+    await collection.approveTokens(alice, {Substrate: bob.address}, 500000n);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(500000n);
+    await collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 500000n);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.equal(0n);
+  });
+
+  itSub('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-5', description: '', tokenPrefix: 'TF'});
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+
+    const nft = await collection.mintToken(alice, {Substrate: bob.address});
+    await nft.transferFrom(alice, {Substrate: bob.address}, {Substrate: charlie.address});
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: charlie.address});
+  });
+});
+
+describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      const donor = await privateKey({url: import.meta.url});
+      [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor);
+    });
+  });
+
+  itSub('transferFrom for a collection that does not exist', async ({helper}) => {
+    await expect(helper.collection.approveToken(alice, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: bob.address}, 1n))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+    await expect(helper.collection.transferTokenFrom(bob, NON_EXISTENT_COLLECTION_ID, 0, {Substrate: alice.address}, {Substrate: bob.address}, 1n))
+      .to.be.rejectedWith(/common\.CollectionNotFound/);
+  });
+
+  /* itSub('transferFrom for a collection that was destroyed', async ({helper}) => {
+      this test copies approve negative test
+  }); */
+
+  /* itSub('transferFrom a token that does not exist', async ({helper}) => {
+    this test copies approve negative test
+  }); */
+
+  /* itSub('transferFrom a token that was deleted', async ({helper}) => {
+    this test copies approve negative test
+  }); */
+
+  itSub('[nft] transferFrom for not approved address', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'});
+    const nft = await collection.mintToken(alice);
+
+    await expect(nft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}))
+      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub('[fungible] transferFrom for not approved address', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-1', description: '', tokenPrefix: 'TF'});
+    await collection.mint(alice, 10n);
+
+    await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n))
+      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
+    expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
+  });
+
+  itSub.ifWithPallets('[refungible] transferFrom for not approved address', [Pallets.ReFungible], async({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-3', description: '', tokenPrefix: 'TF'});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}))
+      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
+    expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
+    expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
+  });
+
+  itSub('[nft] transferFrom incorrect token count', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-4', description: '', tokenPrefix: 'TF'});
+    const nft = await collection.mintToken(alice);
+
+    await nft.approve(alice, {Substrate: bob.address});
+    expect(await nft.isApproved({Substrate: bob.address})).to.be.true;
+
+    await expect(helper.collection.transferTokenFrom(
+      bob,
+      collection.collectionId,
+      nft.tokenId,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+      2n,
+    )).to.be.rejectedWith(/nonfungible\.NonfungibleItemsHaveNoAmount/);
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub('[fungible] transferFrom incorrect token count', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-5', description: '', tokenPrefix: 'TF'});
+    await collection.mint(alice, 10n);
+
+    await collection.approveTokens(alice, {Substrate: bob.address}, 2n);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(2n);
+
+    await expect(collection.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 5n))
+      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
+    expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
+  });
+
+  itSub.ifWithPallets('[refungible] transferFrom incorrect token count', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-6', description: '', tokenPrefix: 'TF'});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await rft.approve(alice, {Substrate: bob.address}, 5n);
+    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(5n);
+
+    await expect(rft.transferFrom(bob, {Substrate: alice.address}, {Substrate: charlie.address}, 7n))
+      .to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10n);
+    expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
+    expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
+  });
+
+  itSub('[nft] execute transferFrom from account that is not owner of collection', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-7', description: '', tokenPrefix: 'TF'});
+    const nft = await collection.mintToken(alice);
+
+    await expect(nft.approve(charlie, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
+    expect(await nft.isApproved({Substrate: bob.address})).to.be.false;
+
+    await expect(nft.transferFrom(
+      charlie,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await nft.getOwner()).to.be.deep.equal({Substrate: alice.address});
+  });
+
+  itSub('[fungible] execute transferFrom from account that is not owner of collection', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-8', description: '', tokenPrefix: 'TF'});
+    await collection.mint(alice, 10000n);
+
+    await expect(collection.approveTokens(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n);
+    expect(await collection.getApprovedTokens({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n);
+
+    await expect(collection.transferFrom(
+      charlie,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await collection.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n);
+    expect(await collection.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
+    expect(await collection.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
+  });
+
+  itSub.ifWithPallets('[refungible] execute transferFrom from account that is not owner of collection', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-9', description: '', tokenPrefix: 'TF'});
+    const rft = await collection.mintToken(alice, 10000n);
+
+    await expect(rft.approve(charlie, {Substrate: bob.address}, 1n)).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
+    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(0n);
+    expect(await rft.getApprovedPieces({Substrate: charlie.address}, {Substrate: bob.address})).to.be.eq(0n);
+
+    await expect(rft.transferFrom(
+      charlie,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+    expect(await rft.getBalance({Substrate: alice.address})).to.be.deep.equal(10000n);
+    expect(await rft.getBalance({Substrate: bob.address})).to.be.deep.equal(0n);
+    expect(await rft.getBalance({Substrate: charlie.address})).to.be.deep.equal(0n);
+  });
+
+  itSub('transferFrom burnt token before approve NFT', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-10', description: '', tokenPrefix: 'TF'});
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    const nft = await collection.mintToken(alice);
+
+    await nft.burn(alice);
+    await expect(nft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TokenNotFound/);
+
+    await expect(nft.transferFrom(
+      bob,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+  });
+
+  itSub('transferFrom burnt token before approve Fungible', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-11', description: '', tokenPrefix: 'TF'});
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    await collection.mint(alice, 10n);
+
+    await collection.burnTokens(alice, 10n);
+    await expect(collection.approveTokens(alice, {Substrate: bob.address})).to.be.not.rejected;
+
+    await expect(collection.transferFrom(
+      alice,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub.ifWithPallets('transferFrom burnt token before approve ReFungible', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-12', description: '', tokenPrefix: 'TF'});
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await rft.burn(alice, 10n);
+    await expect(rft.approve(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CantApproveMoreThanOwned/);
+
+    await expect(rft.transferFrom(
+      alice,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub('transferFrom burnt token after approve NFT', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-13', description: '', tokenPrefix: 'TF'});
+    const nft = await collection.mintToken(alice);
+
+    await nft.approve(alice, {Substrate: bob.address});
+    expect(await nft.isApproved({Substrate: bob.address})).to.be.true;
+
+    await nft.burn(alice);
+
+    await expect(nft.transferFrom(
+      bob,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+  });
+
+  itSub('transferFrom burnt token after approve Fungible', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {name: 'TransferFrom-Neg-14', description: '', tokenPrefix: 'TF'});
+    await collection.mint(alice, 10n);
+
+    await collection.approveTokens(alice, {Substrate: bob.address});
+    expect(await collection.getApprovedTokens({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(1n);
+
+    await collection.burnTokens(alice, 10n);
+
+    await expect(collection.transferFrom(
+      bob,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.TokenValueTooLow/);
+  });
+
+  itSub.ifWithPallets('transferFrom burnt token after approve ReFungible', [Pallets.ReFungible], async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {name: 'TransferFrom-Neg-15', description: '', tokenPrefix: 'TF'});
+    const rft = await collection.mintToken(alice, 10n);
+
+    await rft.approve(alice, {Substrate: bob.address}, 10n);
+    expect(await rft.getApprovedPieces({Substrate: alice.address}, {Substrate: bob.address})).to.be.eq(10n);
+
+    await rft.burn(alice, 10n);
+
+    await expect(rft.transferFrom(
+      bob,
+      {Substrate: alice.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+  });
+
+  itSub('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'TransferFrom-Neg-16', description: '', tokenPrefix: 'TF'});
+    const nft = await collection.mintToken(alice, {Substrate: bob.address});
+
+    await collection.setLimits(alice, {ownerCanTransfer: false});
+
+    await expect(nft.transferFrom(
+      alice,
+      {Substrate: bob.address},
+      {Substrate: charlie.address},
+    )).to.be.rejectedWith(/common\.ApprovedValueTooLow/);
+  });
+
+  itSub('zero transfer NFT', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(alice, {name: 'Zero', description: 'Zero transfer', tokenPrefix: 'TF'});
+    const notApprovedNft = await collection.mintToken(alice, {Substrate: bob.address});
+    const approvedNft = await collection.mintToken(alice, {Substrate: bob.address});
+    await approvedNft.approve(bob, {Substrate: alice.address});
+
+    // 1. Cannot zero transferFrom (non-existing token)
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, 9999, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
+    // 2. Cannot zero transferFrom (not approved token)
+    await expect(helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, notApprovedNft.tokenId, 0])).to.be.rejectedWith('common.ApprovedValueTooLow');
+    // 3. Can zero transferFrom (approved token):
+    await helper.executeExtrinsic(alice, 'api.tx.unique.transferFrom', [{Substrate: bob.address}, {Substrate: alice.address}, collection.collectionId, approvedNft.tokenId, 0]);
+
+    // 4.1 approvedNft still approved:
+    expect(await approvedNft.isApproved({Substrate: alice.address})).to.be.true;
+    // 4.2 bob is still the owner:
+    expect(await approvedNft.getOwner()).to.deep.eq({Substrate: bob.address});
+    expect(await notApprovedNft.getOwner()).to.deep.eq({Substrate: bob.address});
+    // 4.3 Alice can transfer approved nft:
+    await approvedNft.transferFrom(alice, {Substrate: bob.address}, {Substrate: alice.address});
+    expect(await approvedNft.getOwner()).to.deep.eq({Substrate: alice.address});
+  });
+});
deletedjs-packages/tests/tsconfig.jsondiffbeforeafterboth
--- a/js-packages/tests/tsconfig.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "extends": "../tsconfig.packages.json",
-  "compilerOptions": {
-    "rootDir": "./src",
-    "outDir": "dist",
-    "allowSyntheticDefaultImports": true,
-    "resolveJsonModule": true,
-  },
-  "include": ["./src/**/*.json", "./src/**/*.ts"],
-  "references": [
-    { "path": "../playgrounds/tsconfig.json" }
-  ]
-}
\ No newline at end of file
addedjs-packages/tests/tx-version-presence.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/tx-version-presence.test.ts
@@ -0,0 +1,32 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 {Metadata} from '@polkadot/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+
+let metadata: Metadata;
+
+describe('TxVersion is present', () => {
+  before(async () => {
+    await usingPlaygrounds(async helper => {
+      metadata = await helper.callRpc('api.rpc.state.getMetadata', []);
+    });
+  });
+
+  itSub('Signed extension CheckTxVersion is present', () => {
+    expect(metadata.asLatest.extrinsic.signedExtensions.map(se => se.identifier.toString())).to.include('CheckTxVersion');
+  });
+});
addedjs-packages/tests/util/authorizeEnactUpgrade.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/authorizeEnactUpgrade.ts
@@ -0,0 +1,19 @@
+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);
addedjs-packages/tests/util/createHrmp.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/createHrmp.ts
@@ -0,0 +1,36 @@
+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);
+      break;
+    default:
+      throw new Error(`unknown hrmp config profile: ${profile}`);
+  }
+}, config.relayUrl);
+// We miss disconnect/unref somewhere.
+process.exit(0);
addedjs-packages/tests/util/frankensteinMigrate.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/frankensteinMigrate.ts
@@ -0,0 +1,9 @@
+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,
+};
addedjs-packages/tests/util/globalSetup.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/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 './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.transfer',
+            [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);
+});
addedjs-packages/tests/util/identitySetter.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/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 './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));
addedjs-packages/tests/util/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/index.ts
@@ -0,0 +1,221 @@
+// 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} 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 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),
+  };
+}
addedjs-packages/tests/util/relayIdentitiesChecker.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/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 './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));
addedjs-packages/tests/util/setCode.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/util/setCode.ts
@@ -0,0 +1,15 @@
+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);
addedjs-packages/tests/vesting.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/vesting.test.ts
@@ -0,0 +1,151 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, usingPlaygrounds, expect} from './util/index.js';
+
+describe('Vesting', () => {
+  let donor: IKeyringPair;
+  let nominal: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      donor = await privateKey({url: import.meta.url});
+      nominal = helper.balance.getOneTokenNominal();
+    });
+  });
+
+  itSub('can perform vestedTransfer and claim tokens', async ({helper}) => {
+    // arrange
+    const [sender, recepient] = await helper.arrange.createAccounts([1000n, 1n], donor);
+    const currentRelayBlock = await helper.chain.getRelayBlockNumber();
+    const SCHEDULE_1_PERIOD = 6n; // 6 blocks one period
+    const SCHEDULE_1_START = currentRelayBlock + 6n; // Block when 1 schedule starts
+    const SCHEDULE_2_PERIOD = 12n; // 12 blocks one period
+    const SCHEDULE_2_START = currentRelayBlock + 12n; // Block when 2 schedule starts
+    const schedule1 = {start: SCHEDULE_1_START, period: SCHEDULE_1_PERIOD, periodCount: 2n, perPeriod: 50n * nominal};
+    const schedule2 = {start: SCHEDULE_2_START, period: SCHEDULE_2_PERIOD, periodCount: 2n, perPeriod: 100n * nominal};
+
+    // act
+    await helper.balance.vestedTransfer(sender, recepient.address, schedule1);
+    await helper.balance.vestedTransfer(sender, recepient.address, schedule2);
+    let schedule = await helper.balance.getVestingSchedules(recepient.address);
+
+    // check senders balance after vesting:
+    let balanceSender = await helper.balance.getSubstrateFull(sender.address);
+    expect(balanceSender.free / nominal).to.eq(699n);
+    expect(balanceSender.frozen).to.eq(0n);
+    expect(balanceSender.reserved).to.eq(0n);
+
+    // check recepient balance after vesting:
+    let balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free).to.eq(301n * nominal);
+    expect(balanceRecepient.frozen).to.eq(300n * nominal);
+    expect(balanceRecepient.reserved).to.eq(0n);
+
+    // Schedules list correct:
+    expect(schedule).to.has.length(2);
+    expect(schedule[0]).to.deep.eq(schedule1);
+    expect(schedule[1]).to.deep.eq(schedule2);
+
+    // Wait first part available:
+    await helper.wait.forRelayBlockNumber(SCHEDULE_1_START + SCHEDULE_1_PERIOD);
+    await helper.balance.claim(recepient);
+
+    // check recepient balance after claim (50 tokens claimed, 250 left):
+    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free / nominal).to.eq(300n);
+    expect(balanceRecepient.frozen).to.eq(250n * nominal);
+    expect(balanceRecepient.reserved).to.eq(0n);
+
+    // Wait first schedule ends and first part od second schedule:
+    await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD);
+    await helper.balance.claim(recepient);
+
+    // check recepient balance after second claim (150 tokens claimed, 100 left):
+    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free / nominal).to.eq(300n);
+    expect(balanceRecepient.frozen).to.eq(100n * nominal);
+    expect(balanceRecepient.reserved).to.eq(0n);
+
+    // Schedules list contain 1 vesting:
+    schedule = await helper.balance.getVestingSchedules(recepient.address);
+    expect(schedule).to.has.length(1);
+    expect(schedule[0]).to.deep.eq(schedule2);
+
+    // Wait 2 schedule ends:
+    await helper.wait.forRelayBlockNumber(SCHEDULE_2_START + SCHEDULE_2_PERIOD * 2n);
+    await helper.balance.claim(recepient);
+
+    // check recepient balance after second claim (100 tokens claimed, 0 left):
+    balanceRecepient = await helper.balance.getSubstrateFull(recepient.address);
+    expect(balanceRecepient.free / nominal).to.eq(300n);
+    expect(balanceRecepient.frozen).to.eq(0n);
+    expect(balanceRecepient.reserved).to.eq(0n);
+
+    // check sender balance does not changed:
+    balanceSender = await helper.balance.getSubstrateFull(sender.address);
+    expect(balanceSender.free / nominal).to.eq(699n);
+    expect(balanceSender.frozen).to.eq(0n);
+    expect(balanceSender.reserved).to.eq(0n);
+  });
+
+  itSub('cannot send more tokens than have', async ({helper}) => {
+    const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor);
+    const schedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 100n * nominal};
+    const manyPeriodsSchedule = {start: 0n, period: 1n, periodCount: 100n, perPeriod: 10n * nominal};
+    const oneBigSumSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 5000n * nominal};
+
+    // Sender cannot send vestedTransfer to self or other
+    await expect(helper.balance.vestedTransfer(sender, sender.address, manyPeriodsSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/);
+    await expect(helper.balance.vestedTransfer(sender, receiver.address, manyPeriodsSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/);
+    await expect(helper.balance.vestedTransfer(sender, sender.address, oneBigSumSchedule)).to.be.rejectedWith(/^vesting.InsufficientBalanceToLock$/);
+    await expect(helper.balance.vestedTransfer(sender, receiver.address, oneBigSumSchedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/);
+
+    const balanceSender = await helper.balance.getSubstrateFull(sender.address);
+    const balanceReceiver = await helper.balance.getSubstrateFull(receiver.address);
+
+    // Sender's balance has not changed
+    expect(balanceSender.free / nominal).to.eq(999n);
+    expect(balanceSender.frozen).to.eq(0n);
+    expect(balanceSender.reserved).to.eq(0n);
+
+    // Receiver's balance has not changed
+    expect(balanceReceiver.free).to.be.eq(1n * nominal);
+    expect(balanceReceiver.frozen).to.be.eq(0n);
+    expect(balanceReceiver.reserved).to.be.eq(0n);
+
+    // Receiver cannot send vestedTransfer back because of freeze
+    await expect(helper.balance.vestedTransfer(receiver, sender.address, schedule)).to.be.rejectedWith(/^Token: FundsUnavailable$/);
+  });
+
+  itSub('cannot send vestedTransfer with incorrect parameters', async ({helper}) => {
+    const [sender, receiver] = await helper.arrange.createAccounts([1000n, 1n], donor);
+    const incorrectperiodSchedule = {start: 0n, period: 0n, periodCount: 10n, perPeriod: 10n * nominal};
+    const incorrectPeriodCountSchedule = {start: 0n, period: 1n, periodCount: 0n, perPeriod: 10n * nominal};
+    const incorrectPerPeriodSchedule = {start: 0n, period: 1n, periodCount: 1n, perPeriod: 0n * nominal};
+
+    await expect(helper.balance.vestedTransfer(sender, sender.address, incorrectperiodSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/);
+    await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPeriodCountSchedule)).to.be.rejectedWith(/vesting.ZeroVestingPeriod/);
+    await expect(helper.balance.vestedTransfer(sender, receiver.address, incorrectPerPeriodSchedule)).to.be.rejectedWith(/vesting.AmountLow/);
+
+    const balanceSender = await helper.balance.getSubstrateFull(sender.address);
+    // Sender's balance has not changed
+    expect(balanceSender.free / nominal).to.eq(999n);
+    expect(balanceSender.frozen).to.eq(0n);
+    expect(balanceSender.reserved).to.eq(0n);
+  });
+});
addedjs-packages/tests/xcm/lowLevelXcmQuartz.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/xcm/lowLevelXcmQuartz.test.ts
@@ -0,0 +1,364 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import {itSub, describeXCM, usingPlaygrounds, usingKaruraPlaygrounds,  usingMoonriverPlaygrounds, usingShidenPlaygrounds, usingRelayPlaygrounds} from '../util/index.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';
+
+const testHelper = new XcmTestHelper('quartz');
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Karura', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: QUARTZ_CHAIN,
+            },
+          },
+        },
+      };
+
+      const metadata = {
+        name: 'Quartz',
+        symbol: 'QTZ',
+        decimals: 18,
+        minimalBalance: 1000000000000000000n,
+      };
+
+      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
+        hexToString(v.toJSON()['symbol'])) as string[];
+
+      if(!assets.includes('QTZ')) {
+        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+      } else {
+        console.log('QTZ token already registered on Karura assetRegistry pallet');
+      }
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+    });
+  });
+
+  itSub('Should connect and send QTZ to Karura', async () => {
+    await testHelper.sendUnqTo('karura', randomAccount);
+  });
+
+  itSub('Should connect to Karura and send QTZ back', async () => {
+    await testHelper.sendUnqBack('karura', alice, randomAccount);
+  });
+
+  itSub('Karura can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('karura', alice);
+  });
+});
+// These tests are relevant only when
+// the the corresponding foreign assets are not registered
+describeXCM('[XCMLL] Integration test: Quartz rejects non-native tokens', () => {
+  let alice: IKeyringPair;
+
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+
+
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+  });
+
+  itSub('Quartz rejects KAR tokens from Karura', async () => {
+    await testHelper.rejectNativeTokensFrom('karura', alice);
+  });
+
+  itSub('Quartz rejects MOVR tokens from Moonriver', async () => {
+    await testHelper.rejectNativeTokensFrom('moonriver', alice);
+  });
+
+  itSub('Quartz rejects SDN tokens from Shiden', async () => {
+    await testHelper.rejectNativeTokensFrom('shiden', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging QTZ with Moonriver', () => {
+  // Quartz constants
+  let alice: IKeyringPair;
+  let quartzAssetLocation;
+
+  let randomAccountQuartz: IKeyringPair;
+  let randomAccountMoonriver: IKeyringPair;
+
+  // Moonriver constants
+  let assetId: string;
+
+  const quartzAssetMetadata = {
+    name: 'xcQuartz',
+    symbol: 'xcQTZ',
+    decimals: 18,
+    isFrozen: false,
+    minimalBalance: 1n,
+  };
+
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
+
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const alithAccount = helper.account.alithAccount();
+      const baltatharAccount = helper.account.baltatharAccount();
+      const dorothyAccount = helper.account.dorothyAccount();
+
+      randomAccountMoonriver = helper.account.create();
+
+      // >>> Sponsoring Dorothy >>>
+      console.log('Sponsoring Dorothy.......');
+      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring Dorothy.......DONE');
+      // <<< Sponsoring Dorothy <<<
+
+      quartzAssetLocation = {
+        XCM: {
+          parents: 1,
+          interior: {X1: {Parachain: QUARTZ_CHAIN}},
+        },
+      };
+      const existentialDeposit = 1n;
+      const isSufficient = true;
+      const unitsPerSecond = 1n;
+      const numAssetsWeightHint = 0;
+      if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
+        console.log('Quartz asset already registered on Moonriver');
+      } else {
+        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+          location: quartzAssetLocation,
+          metadata: quartzAssetMetadata,
+          existentialDeposit,
+          isSufficient,
+          unitsPerSecond,
+          numAssetsWeightHint,
+        });
+
+        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+
+        await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
+      }
+      // >>> Acquire Quartz AssetId Info on Moonriver >>>
+      console.log('Acquire Quartz AssetId Info on Moonriver.......');
+
+      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();
+
+      console.log('QTZ asset ID is %s', assetId);
+      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');
+      // >>> Acquire Quartz AssetId Info on Moonriver >>>
+
+      // >>> Sponsoring random Account >>>
+      console.log('Sponsoring random Account.......');
+      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring random Account.......DONE');
+      // <<< Sponsoring random Account <<<
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);
+    });
+  });
+
+  itSub('Should connect and send QTZ to Moonriver', async () => {
+    await testHelper.sendUnqTo('moonriver', randomAccountQuartz, randomAccountMoonriver);
+  });
+
+  itSub('Should connect to Moonriver and send QTZ back', async () => {
+    await testHelper.sendUnqBack('moonriver', alice, randomAccountQuartz);
+  });
+
+  itSub('Moonriver can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('moonriver', alice);
+  });
+
+  itSub('Should not accept reserve transfer of QTZ from Moonriver', async () => {
+    await testHelper.rejectReserveTransferUNQfrom('moonriver', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Shiden', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden
+  const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden
+
+  // Quartz -> Shiden
+  const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden
+  const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      randomAccount = helper.arrange.createEmptyAccount();
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+      console.log('sender: ', randomAccount.address);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
+        console.log('1. Create foreign asset and metadata');
+        await helper.getSudo().assets.forceCreate(
+          alice,
+          QTZ_ASSET_ID_ON_SHIDEN,
+          alice.address,
+          QTZ_MINIMAL_BALANCE_ON_SHIDEN,
+        );
+
+        await helper.assets.setMetadata(
+          alice,
+          QTZ_ASSET_ID_ON_SHIDEN,
+          'Quartz',
+          'QTZ',
+          Number(QTZ_DECIMALS),
+        );
+
+        console.log('2. Register asset location on Shiden');
+        const assetLocation = {
+          V2: {
+            parents: 1,
+            interior: {
+              X1: {
+                Parachain: QUARTZ_CHAIN,
+              },
+            },
+          },
+        };
+
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
+
+        console.log('3. Set QTZ payment for XCM execution on Shiden');
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+      } else {
+        console.log('QTZ is already registered on Shiden');
+      }
+      console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, shidenInitialBalance);
+    });
+  });
+
+  itSub('Should connect and send QTZ to Shiden', async () => {
+    await testHelper.sendUnqTo('shiden', randomAccount);
+  });
+
+  itSub('Should connect to Shiden and send QTZ back', async () => {
+    await testHelper.sendUnqBack('shiden', alice, randomAccount);
+  });
+
+  itSub('Shiden can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('shiden', alice);
+  });
+
+  itSub('Should not accept reserve transfer of QTZ from Shiden', async () => {
+    await testHelper.rejectReserveTransferUNQfrom('shiden', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => {
+  let sudoer: IKeyringPair;
+
+  before(async function () {
+    await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => {
+      sudoer = await privateKey('//Alice');
+    });
+  });
+
+  // At the moment there is no reliable way
+  // to establish the correspondence between the `ExecutedDownward` event
+  // and the relay's sent message due to `SetTopic` instruction
+  // containing an unpredictable topic silently added by the relay's messages on the router level.
+  // This changes the message hash on arrival to our chain.
+  //
+  // See:
+  // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83
+  // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36
+  //
+  // Because of this, we insert time gaps between tests so
+  // different `ExecutedDownward` events won't interfere with each other.
+  afterEach(async () => {
+    await usingPlaygrounds(async (helper) => {
+      await helper.wait.newBlocks(3);
+    });
+  });
+
+  itSub('The relay can set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain');
+  });
+
+  itSub('The relay can batch set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch');
+  });
+
+  itSub('The relay can batchAll set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll');
+  });
+
+  itSub('The relay can forceBatch set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch');
+  });
+
+  itSub('[negative] The relay cannot set balance', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain');
+  });
+
+  itSub('[negative] The relay cannot set balance via batch', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch');
+  });
+
+  itSub('[negative] The relay cannot set balance via batchAll', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll');
+  });
+
+  itSub('[negative] The relay cannot set balance via forceBatch', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch');
+  });
+
+  itSub('[negative] The relay cannot set balance via dispatchAs', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs');
+  });
+});
addedjs-packages/tests/xcm/lowLevelXcmUnique.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/xcm/lowLevelXcmUnique.test.ts
@@ -0,0 +1,430 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import config from '../config.js';
+import {itSub, describeXCM, usingPlaygrounds, usingAcalaPlaygrounds, usingMoonbeamPlaygrounds, usingAstarPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds} from '../util/index.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,  moonbeamUrl, polkadexUrl, relayUrl, uniqueAssetId} from './xcm.types.js';
+
+const testHelper = new XcmTestHelper('unique');
+
+
+
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Acala', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      console.log(config.acalaUrl);
+      randomAccount = helper.arrange.createEmptyAccount();
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: UNIQUE_CHAIN,
+            },
+          },
+        },
+      };
+
+      const metadata = {
+        name: 'Unique Network',
+        symbol: 'UNQ',
+        decimals: 18,
+        minimalBalance: 1250_000_000_000_000_000n,
+      };
+      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>
+        hexToString(v.toJSON()['symbol'])) as string[];
+
+      if(!assets.includes('UNQ')) {
+        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+      } else {
+        console.log('UNQ token already registered on Acala assetRegistry pallet');
+      }
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Acala', async () => {
+    await testHelper.sendUnqTo('acala', randomAccount);
+  });
+
+  itSub('Should connect to Acala and send UNQ back', async () => {
+    await testHelper.sendUnqBack('acala', alice, randomAccount);
+  });
+
+  itSub('Acala can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('acala', alice);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Acala', async () => {
+    await testHelper.rejectReserveTransferUNQfrom('acala', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Polkadex', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      randomAccount = helper.arrange.createEmptyAccount();
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))
+        .toJSON() as [])
+        .map(nToBigInt).length != 0;
+      /*
+      Check whether the Unique token has been added
+      to the whitelist, since an error will occur
+      if it is added again. Needed for debugging
+      when this test is run multiple times.
+      */
+      if(isWhitelisted) {
+        console.log('UNQ token is already whitelisted on Polkadex');
+      } else {
+        await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);
+      }
+
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Polkadex', async () => {
+    await testHelper.sendUnqTo('polkadex', randomAccount);
+  });
+
+
+  itSub('Should connect to Polkadex and send UNQ back', async () => {
+    await testHelper.sendUnqBack('polkadex', alice, randomAccount);
+  });
+
+  itSub('Polkadex can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('polkadex', alice);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Polkadex', async () => {
+    await testHelper.rejectReserveTransferUNQfrom('polkadex', alice);
+  });
+});
+
+// These tests are relevant only when
+// the the corresponding foreign assets are not registered
+describeXCM('[XCMLL] Integration test: Unique rejects non-native tokens', () => {
+  let alice: IKeyringPair;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+  });
+
+  itSub('Unique rejects ACA tokens from Acala', async () => {
+    await testHelper.rejectNativeTokensFrom('acala', alice);
+  });
+
+  itSub('Unique rejects GLMR tokens from Moonbeam', async () => {
+    await testHelper.rejectNativeTokensFrom('moonbeam', alice);
+  });
+
+  itSub('Unique rejects ASTR tokens from Astar', async () => {
+    await testHelper.rejectNativeTokensFrom('astar', alice);
+  });
+
+  itSub('Unique rejects PDX tokens from Polkadex', async () => {
+    await testHelper.rejectNativeTokensFrom('polkadex', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging UNQ with Moonbeam', () => {
+  // Unique constants
+  let alice: IKeyringPair;
+  let uniqueAssetLocation;
+
+  let randomAccountUnique: IKeyringPair;
+  let randomAccountMoonbeam: IKeyringPair;
+
+  // Moonbeam constants
+  let assetId: string;
+
+  const uniqueAssetMetadata = {
+    name: 'xcUnique',
+    symbol: 'xcUNQ',
+    decimals: 18,
+    isFrozen: false,
+    minimalBalance: 1n,
+  };
+
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);
+
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const alithAccount = helper.account.alithAccount();
+      const baltatharAccount = helper.account.baltatharAccount();
+      const dorothyAccount = helper.account.dorothyAccount();
+
+      randomAccountMoonbeam = helper.account.create();
+
+      // >>> Sponsoring Dorothy >>>
+      console.log('Sponsoring Dorothy.......');
+      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring Dorothy.......DONE');
+      // <<< Sponsoring Dorothy <<<
+      uniqueAssetLocation = {
+        XCM: {
+          parents: 1,
+          interior: {X1: {Parachain: UNIQUE_CHAIN}},
+        },
+      };
+      const existentialDeposit = 1n;
+      const isSufficient = true;
+      const unitsPerSecond = 1n;
+      const numAssetsWeightHint = 0;
+
+      if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {
+        console.log('Unique asset already registered on Moonbeam');
+      } else {
+        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+          location: uniqueAssetLocation,
+          metadata: uniqueAssetMetadata,
+          existentialDeposit,
+          isSufficient,
+          unitsPerSecond,
+          numAssetsWeightHint,
+        });
+
+        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+
+        await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
+      }
+
+      // >>> Acquire Unique AssetId Info on Moonbeam >>>
+      console.log('Acquire Unique AssetId Info on Moonbeam.......');
+
+      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
+
+      console.log('UNQ asset ID is %s', assetId);
+      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');
+
+      // >>> Sponsoring random Account >>>
+      console.log('Sponsoring random Account.......');
+      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring random Account.......DONE');
+      // <<< Sponsoring random Account <<<
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, SENDER_BUDGET);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Moonbeam', async () => {
+    await testHelper.sendUnqTo('moonbeam', randomAccountUnique, randomAccountMoonbeam);
+  });
+
+  itSub('Should connect to Moonbeam and send UNQ back', async () => {
+    await testHelper.sendUnqBack('moonbeam', alice, randomAccountUnique);
+  });
+
+  itSub('Moonbeam can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('moonbeam', alice);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async () => {
+    await testHelper.rejectReserveTransferUNQfrom('moonbeam', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: Exchanging tokens with Astar', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar
+  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar
+
+  // Unique -> Astar
+  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
+  const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      randomAccount = helper.arrange.createEmptyAccount();
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, SENDER_BUDGET);
+      console.log('randomAccount', randomAccount.address);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {
+        console.log('1. Create foreign asset and metadata');
+        await helper.getSudo().assets.forceCreate(
+          alice,
+          UNQ_ASSET_ID_ON_ASTAR,
+          alice.address,
+          UNQ_MINIMAL_BALANCE_ON_ASTAR,
+        );
+
+        await helper.assets.setMetadata(
+          alice,
+          UNQ_ASSET_ID_ON_ASTAR,
+          'Unique Network',
+          'UNQ',
+          Number(UNQ_DECIMALS),
+        );
+
+        console.log('2. Register asset location on Astar');
+        const assetLocation = {
+          V2: {
+            parents: 1,
+            interior: {
+              X1: {
+                Parachain: UNIQUE_CHAIN,
+              },
+            },
+          },
+        };
+
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
+
+        console.log('3. Set UNQ payment for XCM execution on Astar');
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+      } else {
+        console.log('UNQ is already registered on Astar');
+      }
+      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Astar', async () => {
+    await testHelper.sendUnqTo('astar', randomAccount);
+  });
+
+  itSub('Should connect to Astar and send UNQ back', async () => {
+    await testHelper.sendUnqBack('astar', alice, randomAccount);
+  });
+
+  itSub('Astar can send only up to its balance', async () => {
+    await testHelper.sendOnlyOwnedBalance('astar', alice);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Astar', async () => {
+    await testHelper.rejectReserveTransferUNQfrom('astar', alice);
+  });
+});
+
+describeXCM('[XCMLL] Integration test: The relay can do some root ops', () => {
+  let sudoer: IKeyringPair;
+
+  before(async function () {
+    await usingRelayPlaygrounds(relayUrl, async (_, privateKey) => {
+      sudoer = await privateKey('//Alice');
+    });
+  });
+
+  // At the moment there is no reliable way
+  // to establish the correspondence between the `ExecutedDownward` event
+  // and the relay's sent message due to `SetTopic` instruction
+  // containing an unpredictable topic silently added by the relay's messages on the router level.
+  // This changes the message hash on arrival to our chain.
+  //
+  // See:
+  // * The relay's router: https://github.com/paritytech/polkadot-sdk/blob/f60318f68687e601c47de5ad5ca88e2c3f8139a7/polkadot/runtime/westend/src/xcm_config.rs#L83
+  // * The `WithUniqueTopic` helper: https://github.com/paritytech/polkadot-sdk/blob/945ebbbcf66646be13d5b1d1bc26c8b0d3296d9e/polkadot/xcm/xcm-builder/src/routing.rs#L36
+  //
+  // Because of this, we insert time gaps between tests so
+  // different `ExecutedDownward` events won't interfere with each other.
+  afterEach(async () => {
+    await usingPlaygrounds(async (helper) => {
+      await helper.wait.newBlocks(3);
+    });
+  });
+
+  itSub('The relay can set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'plain');
+  });
+
+  itSub('The relay can batch set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batch');
+  });
+
+  itSub('The relay can batchAll set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'batchAll');
+  });
+
+  itSub('The relay can forceBatch set storage', async () => {
+    await testHelper.relayIsPermittedToSetStorage(sudoer, 'forceBatch');
+  });
+
+  itSub('[negative] The relay cannot set balance', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'plain');
+  });
+
+  itSub('[negative] The relay cannot set balance via batch', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batch');
+  });
+
+  itSub('[negative] The relay cannot set balance via batchAll', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'batchAll');
+  });
+
+  itSub('[negative] The relay cannot set balance via forceBatch', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'forceBatch');
+  });
+
+  itSub('[negative] The relay cannot set balance via dispatchAs', async () => {
+    await testHelper.relayIsNotPermittedToSetBalance(sudoer, 'dispatchAs');
+  });
+});
addedjs-packages/tests/xcm/xcm.types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/xcm/xcm.types.ts
@@ -0,0 +1,621 @@
+import type {IKeyringPair} from '@polkadot/types/types';
+import {hexToString} from '@polkadot/util';
+import {expect, usingAcalaPlaygrounds, usingAstarPlaygrounds, usingKaruraPlaygrounds, usingMoonbeamPlaygrounds, usingMoonriverPlaygrounds, usingPlaygrounds, usingPolkadexPlaygrounds, usingRelayPlaygrounds, usingShidenPlaygrounds} from '../util/index.js';
+import {DevUniqueHelper, Event} from '@unique/playgrounds/unique.dev.js';
+import config from '../config.js';
+
+export const UNIQUE_CHAIN = +(process.env.RELAY_UNIQUE_ID || 2037);
+export const STATEMINT_CHAIN = +(process.env.RELAY_STATEMINT_ID || 1000);
+export const ACALA_CHAIN = +(process.env.RELAY_ACALA_ID || 2000);
+export const MOONBEAM_CHAIN = +(process.env.RELAY_MOONBEAM_ID || 2004);
+export const ASTAR_CHAIN = +(process.env.RELAY_ASTAR_ID || 2006);
+export const POLKADEX_CHAIN = +(process.env.RELAY_POLKADEX_ID || 2040);
+
+export const QUARTZ_CHAIN = +(process.env.RELAY_QUARTZ_ID || 2095);
+export const STATEMINE_CHAIN = +(process.env.RELAY_STATEMINE_ID || 1000);
+export const KARURA_CHAIN = +(process.env.RELAY_KARURA_ID || 2000);
+export const MOONRIVER_CHAIN = +(process.env.RELAY_MOONRIVER_ID || 2023);
+export const SHIDEN_CHAIN = +(process.env.RELAY_SHIDEN_ID || 2007);
+
+export const relayUrl = config.relayUrl;
+export const statemintUrl = config.statemintUrl;
+export const statemineUrl = config.statemineUrl;
+
+export const acalaUrl = config.acalaUrl;
+export const moonbeamUrl = config.moonbeamUrl;
+export const astarUrl = config.astarUrl;
+export const polkadexUrl = config.polkadexUrl;
+
+export const karuraUrl = config.karuraUrl;
+export const moonriverUrl = config.moonriverUrl;
+export const shidenUrl = config.shidenUrl;
+
+export const SAFE_XCM_VERSION = 3;
+
+
+export const RELAY_DECIMALS = 12;
+export const STATEMINE_DECIMALS = 12;
+export const KARURA_DECIMALS = 12;
+export const SHIDEN_DECIMALS = 18n;
+export const QTZ_DECIMALS = 18n;
+
+export const ASTAR_DECIMALS = 18n;
+export const UNQ_DECIMALS = 18n;
+
+export const maxWaitBlocks = 6;
+
+export const uniqueMultilocation = {
+  parents: 1,
+  interior: {
+    X1: {
+      Parachain: UNIQUE_CHAIN,
+    },
+  },
+};
+export const uniqueVersionedMultilocation = {
+  V3: uniqueMultilocation,
+};
+
+export const uniqueAssetId = {
+  Concrete: uniqueMultilocation,
+};
+
+export const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
+  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+};
+export const expectUntrustedReserveLocationFail = async (helper: DevUniqueHelper, messageSent: any) => {
+  await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
+         && event.outcome.isUntrustedReserveLocation);
+};
+
+export const expectDownwardXcmNoPermission = async (helper: DevUniqueHelper) => {
+  // The correct messageHash for downward messages can't be reliably obtained
+  await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.asIncomplete[1].isNoPermission);
+};
+
+export const expectDownwardXcmComplete = async (helper: DevUniqueHelper) => {
+  // The correct messageHash for downward messages can't be reliably obtained
+  await helper.wait.expectEvent(maxWaitBlocks, Event.DmpQueue.ExecutedDownward, event => event.outcome.isComplete);
+};
+
+export const NETWORKS = {
+  acala: usingAcalaPlaygrounds,
+  astar: usingAstarPlaygrounds,
+  polkadex: usingPolkadexPlaygrounds,
+  moonbeam: usingMoonbeamPlaygrounds,
+  moonriver: usingMoonriverPlaygrounds,
+  karura: usingKaruraPlaygrounds,
+  shiden: usingShidenPlaygrounds,
+} as const;
+type NetworkNames = keyof typeof NETWORKS;
+
+type NativeRuntime = 'opal' | 'quartz' | 'unique';
+
+export function mapToChainId(networkName: keyof typeof NETWORKS): number {
+  switch (networkName) {
+    case 'acala':
+      return ACALA_CHAIN;
+    case 'astar':
+      return ASTAR_CHAIN;
+    case 'moonbeam':
+      return MOONBEAM_CHAIN;
+    case 'polkadex':
+      return POLKADEX_CHAIN;
+    case 'moonriver':
+      return MOONRIVER_CHAIN;
+    case 'karura':
+      return KARURA_CHAIN;
+    case 'shiden':
+      return SHIDEN_CHAIN;
+  }
+}
+
+export function mapToChainUrl(networkName: NetworkNames): string {
+  switch (networkName) {
+    case 'acala':
+      return acalaUrl;
+    case 'astar':
+      return astarUrl;
+    case 'moonbeam':
+      return moonbeamUrl;
+    case 'polkadex':
+      return polkadexUrl;
+    case 'moonriver':
+      return moonriverUrl;
+    case 'karura':
+      return karuraUrl;
+    case 'shiden':
+      return shidenUrl;
+  }
+}
+
+export function getDevPlayground(name: NetworkNames) {
+  return NETWORKS[name];
+}
+
+export const TRANSFER_AMOUNT = 2000000_000_000_000_000_000_000n;
+export const SENDER_BUDGET = 2n * TRANSFER_AMOUNT;
+export const SENDBACK_AMOUNT = TRANSFER_AMOUNT / 2n;
+export const STAYED_ON_TARGET_CHAIN = TRANSFER_AMOUNT - SENDBACK_AMOUNT;
+export const TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT = 100_000_000_000n;
+
+export class XcmTestHelper {
+  private _balanceUniqueTokenInit: bigint = 0n;
+  private _balanceUniqueTokenMiddle: bigint = 0n;
+  private _balanceUniqueTokenFinal: bigint = 0n;
+  private _unqFees: bigint = 0n;
+  private _nativeRuntime: NativeRuntime;
+
+  constructor(runtime: NativeRuntime) {
+    this._nativeRuntime = runtime;
+  }
+
+  private _getNativeId() {
+    switch (this._nativeRuntime) {
+      case 'opal':
+        // To-Do
+        return 1001;
+      case 'quartz':
+        return QUARTZ_CHAIN;
+      case 'unique':
+        return UNIQUE_CHAIN;
+    }
+  }
+
+  private _isAddress20FormatFor(network: NetworkNames) {
+    switch (network) {
+      case 'moonbeam':
+      case 'moonriver':
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  private _runtimeVersionedMultilocation() {
+    return {
+      V3: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: this._getNativeId(),
+          },
+        },
+      },
+    };
+  }
+
+  private _uniqueChainMultilocationForRelay() {
+    return {
+      V3: {
+        parents: 0,
+        interior: {
+          X1: {Parachain: this._getNativeId()},
+        },
+      },
+    };
+  }
+
+  async sendUnqTo(
+    networkName: keyof typeof NETWORKS,
+    randomAccount: IKeyringPair,
+    randomAccountOnTargetChain = randomAccount,
+  ) {
+    const networkUrl = mapToChainUrl(networkName);
+    const targetPlayground = getDevPlayground(networkName);
+    await usingPlaygrounds(async (helper) => {
+      this._balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: mapToChainId(networkName),
+            },
+          },
+        },
+      };
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {
+            X1: (
+              this._isAddress20FormatFor(networkName) ?
+                {
+                  AccountKey20: {
+                    network: 'Any',
+                    key: randomAccountOnTargetChain.address,
+                  },
+                }
+                :
+                {
+                  AccountId32: {
+                    network: 'Any',
+                    id: randomAccountOnTargetChain.addressRaw,
+                  },
+                }
+            ),
+          },
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT,
+            },
+          },
+        ],
+      };
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+      const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+      this._balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
+
+      this._unqFees = this._balanceUniqueTokenInit - this._balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
+      console.log('[%s -> %s] transaction fees: %s', this._nativeRuntime, networkName, helper.util.bigIntToDecimals(this._unqFees));
+      expect(this._unqFees > 0n, 'Negative fees, looks like nothing was transferred').to.be.true;
+
+      await targetPlayground(networkUrl, async (helper) => {
+      /*
+        Since only the parachain part of the Polkadex
+        infrastructure is launched (without their
+        solochain validators), processing incoming
+        assets will lead to an error.
+        This error indicates that the Polkadex chain
+        received a message from the Unique network,
+        since the hash is being checked to ensure
+        it matches what was sent.
+      */
+        if(networkName == 'polkadex') {
+          await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);
+        } else {
+          await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == messageSent.messageHash);
+        }
+      });
+
+    });
+  }
+
+  async sendUnqBack(
+    networkName: keyof typeof NETWORKS,
+    sudoer: IKeyringPair,
+    randomAccountOnUnq: IKeyringPair,
+  ) {
+    const networkUrl = mapToChainUrl(networkName);
+
+    const targetPlayground = getDevPlayground(networkName);
+    await usingPlaygrounds(async (helper) => {
+
+      const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+        randomAccountOnUnq.addressRaw,
+        {
+          Concrete: {
+            parents: 1,
+            interior: {
+              X1: {Parachain: this._getNativeId()},
+            },
+          },
+        },
+        SENDBACK_AMOUNT,
+      );
+
+      let xcmProgramSent: any;
+
+
+      await targetPlayground(networkUrl, async (helper) => {
+        if('getSudo' in helper) {
+          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), xcmProgram);
+          xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        } else if('fastDemocracy' in helper) {
+          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), xcmProgram]);
+          // Needed to bypass the call filter.
+          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+          await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
+          xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+      });
+
+      await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);
+
+      this._balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountOnUnq.address);
+
+      expect(this._balanceUniqueTokenFinal).to.be.equal(this._balanceUniqueTokenInit - this._unqFees - STAYED_ON_TARGET_CHAIN);
+
+    });
+  }
+
+  async sendOnlyOwnedBalance(
+    networkName: keyof typeof NETWORKS,
+    sudoer: IKeyringPair,
+  ) {
+    const networkUrl = mapToChainUrl(networkName);
+    const targetPlayground = getDevPlayground(networkName);
+
+    const targetChainBalance = 10000n * (10n ** UNQ_DECIMALS);
+
+    await usingPlaygrounds(async (helper) => {
+      const targetChainSovereignAccount = helper.address.paraSiblingSovereignAccount(mapToChainId(networkName));
+      await helper.getSudo().balance.setBalanceSubstrate(sudoer, targetChainSovereignAccount, targetChainBalance);
+      const moreThanTargetChainHas = 2n * targetChainBalance;
+
+      const targetAccount = helper.arrange.createEmptyAccount();
+
+      const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+        targetAccount.addressRaw,
+        {
+          Concrete: {
+            parents: 0,
+            interior: 'Here',
+          },
+        },
+        moreThanTargetChainHas,
+      );
+
+      let maliciousXcmProgramSent: any;
+
+
+      await targetPlayground(networkUrl, async (helper) => {
+        if('getSudo' in helper) {
+          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgram);
+          maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        } else if('fastDemocracy' in helper) {
+          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgram]);
+          // Needed to bypass the call filter.
+          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+          await helper.fastDemocracy.executeProposal(`sending ${networkName} -> Unique via XCM program`, batchCall);
+          maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+      });
+
+      await expectFailedToTransact(helper, maliciousXcmProgramSent);
+
+      const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+      expect(targetAccountBalance).to.be.equal(0n);
+    });
+  }
+
+  async rejectReserveTransferUNQfrom(networkName: keyof typeof NETWORKS, sudoer: IKeyringPair) {
+    const networkUrl = mapToChainUrl(networkName);
+    const targetPlayground = getDevPlayground(networkName);
+
+    await usingPlaygrounds(async (helper) => {
+      const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
+      const targetAccount = helper.arrange.createEmptyAccount();
+
+      const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+        targetAccount.addressRaw,
+        {
+          Concrete: {
+            parents: 1,
+            interior: {
+              X1: {
+                Parachain: this._getNativeId(),
+              },
+            },
+          },
+        },
+        testAmount,
+      );
+
+      const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+        targetAccount.addressRaw,
+        {
+          Concrete: {
+            parents: 0,
+            interior: 'Here',
+          },
+        },
+        testAmount,
+      );
+
+      let maliciousXcmProgramFullIdSent: any;
+      let maliciousXcmProgramHereIdSent: any;
+      const maxWaitBlocks = 3;
+
+      // Try to trick Unique using full UNQ identification
+      await targetPlayground(networkUrl, async (helper) => {
+        if('getSudo' in helper) {
+          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);
+          maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+        // Moonbeam case
+        else if('fastDemocracy' in helper) {
+          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
+          // Needed to bypass the call filter.
+          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+          await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using path asset identification`,batchCall);
+
+          maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+      });
+
+
+      await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);
+
+      let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+      expect(accountBalance).to.be.equal(0n);
+
+      // Try to trick Unique using shortened UNQ identification
+      await targetPlayground(networkUrl, async (helper) => {
+        if('getSudo' in helper) {
+          await helper.getSudo().xcm.send(sudoer, this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId);
+          maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+        else if('fastDemocracy' in helper) {
+          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramHereId]);
+          // Needed to bypass the call filter.
+          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+          await helper.fastDemocracy.executeProposal(`${networkName} try to act like a reserve location for UNQ using "here" asset identification`, batchCall);
+
+          maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+      });
+
+      await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);
+
+      accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+      expect(accountBalance).to.be.equal(0n);
+    });
+  }
+
+  async rejectNativeTokensFrom(networkName: keyof typeof NETWORKS, sudoerOnTargetChain: IKeyringPair) {
+    const networkUrl = mapToChainUrl(networkName);
+    const targetPlayground = getDevPlayground(networkName);
+    let messageSent: any;
+
+    await usingPlaygrounds(async (helper) => {
+      const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+        helper.arrange.createEmptyAccount().addressRaw,
+        {
+          Concrete: {
+            parents: 1,
+            interior: {
+              X1: {
+                Parachain: mapToChainId(networkName),
+              },
+            },
+          },
+        },
+        TARGET_CHAIN_TOKEN_TRANSFER_AMOUNT,
+      );
+      await targetPlayground(networkUrl, async (helper) => {
+        if('getSudo' in helper) {
+          await helper.getSudo().xcm.send(sudoerOnTargetChain, this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId);
+          messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        } else if('fastDemocracy' in helper) {
+          const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [this._runtimeVersionedMultilocation(), maliciousXcmProgramFullId]);
+          // Needed to bypass the call filter.
+          const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+          await helper.fastDemocracy.executeProposal(`${networkName} sending native tokens to the Unique via fast democracy`, batchCall);
+
+          messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+        }
+      });
+      await expectFailedToTransact(helper, messageSent);
+    });
+  }
+
+  private async _relayXcmTransactSetStorage(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') {
+    // eslint-disable-next-line require-await
+    return await usingPlaygrounds(async (helper) => {
+      const relayForceKV = () => {
+        const random = Math.random();
+        const key = `relay-forced-key (instance: ${random})`;
+        const val = `relay-forced-value (instance: ${random})`;
+        const call = helper.constructApiCall('api.tx.system.setStorage', [[[key, val]]]).method.toHex();
+
+        return {
+          call,
+          key,
+          val,
+        };
+      };
+
+      if(variant == 'plain') {
+        const kv = relayForceKV();
+        return {
+          program: helper.arrange.makeUnpaidSudoTransactProgram({
+            weightMultiplier: 1,
+            call: kv.call,
+          }),
+          kvs: [kv],
+        };
+      } else {
+        const kv0 = relayForceKV();
+        const kv1 = relayForceKV();
+
+        const batchCall = helper.constructApiCall(`api.tx.utility.${variant}`, [[kv0.call, kv1.call]]).method.toHex();
+        return {
+          program: helper.arrange.makeUnpaidSudoTransactProgram({
+            weightMultiplier: 2,
+            call: batchCall,
+          }),
+          kvs: [kv0, kv1],
+        };
+      }
+    });
+  }
+
+  async relayIsPermittedToSetStorage(relaySudoer: IKeyringPair, variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch') {
+    const {program, kvs} = await this._relayXcmTransactSetStorage(variant);
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [
+        this._uniqueChainMultilocationForRelay(),
+        program,
+      ]);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await expectDownwardXcmComplete(helper);
+
+      for(const kv of kvs) {
+        const forcedValue = await helper.callRpc('api.rpc.state.getStorage', [kv.key]);
+        expect(hexToString(forcedValue.toHex())).to.be.equal(kv.val);
+      }
+    });
+  }
+
+  private async _relayXcmTransactSetBalance(variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs') {
+    // eslint-disable-next-line require-await
+    return await usingPlaygrounds(async (helper) => {
+      const emptyAccount = helper.arrange.createEmptyAccount().address;
+
+      const forceSetBalanceCall = helper.constructApiCall('api.tx.balances.forceSetBalance', [emptyAccount, 10_000n]).method.toHex();
+
+      let call;
+
+      if(variant == 'plain') {
+        call = forceSetBalanceCall;
+
+      } else if(variant == 'dispatchAs') {
+        call = helper.constructApiCall('api.tx.utility.dispatchAs', [
+          {
+            system: 'Root',
+          },
+          forceSetBalanceCall,
+        ]).method.toHex();
+      } else {
+        call = helper.constructApiCall(`api.tx.utility.${variant}`, [[forceSetBalanceCall]]).method.toHex();
+      }
+
+      return {
+        program: helper.arrange.makeUnpaidSudoTransactProgram({
+          weightMultiplier: 1,
+          call,
+        }),
+        emptyAccount,
+      };
+    });
+  }
+
+  async relayIsNotPermittedToSetBalance(
+    relaySudoer: IKeyringPair,
+    variant: 'plain' | 'batch' | 'batchAll' | 'forceBatch' | 'dispatchAs',
+  ) {
+    const {program, emptyAccount} = await this._relayXcmTransactSetBalance(variant);
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.getSudo().executeExtrinsic(relaySudoer, 'api.tx.xcmPallet.send', [
+        this._uniqueChainMultilocationForRelay(),
+        program,
+      ]);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await expectDownwardXcmNoPermission(helper);
+      expect(await helper.balance.getSubstrate(emptyAccount)).to.be.equal(0n);
+    });
+  }
+}
addedjs-packages/tests/xcm/xcmOpal.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/xcm/xcmOpal.test.ts
@@ -0,0 +1,409 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 type {IKeyringPair} from '@polkadot/types/types';
+import config from '../config.js';
+import {itSub, expect, describeXCM, usingPlaygrounds, usingWestmintPlaygrounds, usingRelayPlaygrounds} from '../util/index.js';
+
+const STATEMINE_CHAIN = +(process.env.RELAY_WESTMINT_ID || 1000);
+const UNIQUE_CHAIN = +(process.env.RELAY_OPAL_ID || 2095);
+
+const relayUrl = config.relayUrl;
+const westmintUrl = config.westmintUrl;
+
+const STATEMINE_PALLET_INSTANCE = 50;
+const ASSET_ID = 100;
+const ASSET_METADATA_DECIMALS = 18;
+const ASSET_METADATA_NAME = 'USDT';
+const ASSET_METADATA_DESCRIPTION = 'USDT';
+const ASSET_METADATA_MINIMAL_BALANCE = 1n;
+
+const RELAY_DECIMALS = 12;
+const WESTMINT_DECIMALS = 12;
+
+const TRANSFER_AMOUNT = 1_000_000_000_000_000_000n;
+
+// 10,000.00 (ten thousands) USDT
+const ASSET_AMOUNT = 1_000_000_000_000_000_000_000n;
+
+describeXCM('[XCM] Integration test: Exchanging USDT with Westmint', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  let balanceStmnBefore: bigint;
+  let balanceStmnAfter: bigint;
+
+  let balanceOpalBefore: bigint;
+  let balanceOpalAfter: bigint;
+  let balanceOpalFinal: bigint;
+
+  let balanceBobBefore: bigint;
+  let balanceBobAfter: bigint;
+  let balanceBobFinal: bigint;
+
+  let balanceBobRelayTokenBefore: bigint;
+  let balanceBobRelayTokenAfter: bigint;
+
+
+  before(async () => {
+    await usingPlaygrounds(async (_helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      bob = await privateKey('//Bob'); // funds donor
+    });
+
+    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
+      // 350.00 (three hundred fifty) DOT
+      const fundingAmount = 3_500_000_000_000n;
+
+      await helper.assets.create(alice, ASSET_ID, alice.address, ASSET_METADATA_MINIMAL_BALANCE);
+      await helper.assets.setMetadata(alice, ASSET_ID, ASSET_METADATA_NAME, ASSET_METADATA_DESCRIPTION, ASSET_METADATA_DECIMALS);
+      await helper.assets.mint(alice, ASSET_ID, alice.address, ASSET_AMOUNT);
+
+      // funding parachain sovereing account (Parachain: 2095)
+      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);
+      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, fundingAmount);
+    });
+
+
+    await usingPlaygrounds(async (helper) => {
+      const location = {
+        V2: {
+          parents: 1,
+          interior: {X3: [
+            {
+              Parachain: STATEMINE_CHAIN,
+            },
+            {
+              PalletInstance: STATEMINE_PALLET_INSTANCE,
+            },
+            {
+              GeneralIndex: ASSET_ID,
+            },
+          ]},
+        },
+      };
+
+      const metadata =
+      {
+        name: ASSET_ID,
+        symbol: ASSET_METADATA_NAME,
+        decimals: ASSET_METADATA_DECIMALS,
+        minimalBalance: ASSET_METADATA_MINIMAL_BALANCE,
+      };
+      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+      balanceOpalBefore = await helper.balance.getSubstrate(alice.address);
+    });
+
+
+    // Providing the relay currency to the unique sender account
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: 50_000_000_000_000_000n,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+
+  });
+
+  itSub('Should connect and send USDT from Westmint to Opal', async ({helper}) => {
+    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
+      const dest = {
+        V2: {
+          parents: 1,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: {
+                  X2: [
+                    {
+                      PalletInstance: STATEMINE_PALLET_INSTANCE,
+                    },
+                    {
+                      GeneralIndex: ASSET_ID,
+                    },
+                  ]},
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
+      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
+
+      // common good parachain take commission in it native token
+      console.log(
+        '[Westmint -> Opal] transaction fees on Westmint: %s WND',
+        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, WESTMINT_DECIMALS),
+      );
+      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
+
+    });
+
+
+    // ensure that asset has been delivered
+    await helper.wait.newBlocks(3);
+
+    // expext collection id will be with id 1
+    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+
+    balanceOpalAfter = await helper.balance.getSubstrate(alice.address);
+
+    console.log(
+      '[Westmint -> Opal] transaction fees on Opal: %s USDT',
+      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, ASSET_METADATA_DECIMALS),
+    );
+    console.log(
+      '[Westmint -> Opal] transaction fees on Opal: %s OPL',
+      helper.util.bigIntToDecimals(balanceOpalAfter - balanceOpalBefore),
+    );
+
+    // commission has not paid in USDT token
+    expect(free == TRANSFER_AMOUNT).to.be.true;
+    // ... and parachain native token
+    expect(balanceOpalAfter == balanceOpalBefore).to.be.true;
+  });
+
+  itSub('Should connect and send USDT from Unique to Statemine back', async ({helper}) => {
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {X2: [
+          {
+            Parachain: STATEMINE_CHAIN,
+          },
+          {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          },
+        ]},
+      },
+    };
+
+    const currencies: [any, bigint][] = [
+      [
+        {
+          ForeignAssetId: 0,
+        },
+        //10_000_000_000_000_000n,
+        TRANSFER_AMOUNT,
+      ],
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        400_000_000_000_000n,
+      ],
+    ];
+
+    const feeItem = 1;
+
+    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
+
+    // the commission has been paid in parachain native token
+    balanceOpalFinal = await helper.balance.getSubstrate(alice.address);
+    expect(balanceOpalAfter > balanceOpalFinal).to.be.true;
+
+    await usingWestmintPlaygrounds(westmintUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+
+      // The USDT token never paid fees. Its amount not changed from begin value.
+      // Also check that xcm transfer has been succeeded
+      expect((await helper.assets.account(ASSET_ID, alice.address))! == ASSET_AMOUNT).to.be.true;
+    });
+  });
+
+  itSub('Should connect and send Relay token to Unique', async ({helper}) => {
+    const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
+
+    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    // Providing the relay currency to the unique sender account
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+
+    await helper.wait.newBlocks(3);
+
+    balanceBobAfter = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    const wndFee = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
+    console.log(
+      'Relay (Westend) to Opal transaction fees: %s OPL',
+      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+    );
+    console.log(
+      'Relay (Westend) to Opal transaction fees: %s WND',
+      helper.util.bigIntToDecimals(wndFee, WESTMINT_DECIMALS),
+    );
+    expect(balanceBobBefore == balanceBobAfter).to.be.true;
+    expect(balanceBobRelayTokenBefore < balanceBobRelayTokenAfter).to.be.true;
+  });
+
+  itSub('Should connect and send Relay token back', async ({helper}) => {
+    let relayTokenBalanceBefore: bigint;
+    let relayTokenBalanceAfter: bigint;
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    });
+
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1:{
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const currencies: any = [
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        50_000_000_000_000_000n,
+      ],
+    ];
+
+    const feeItem = 0;
+
+    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
+
+    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
+    console.log('[Opal -> Relay (Westend)] transaction fees: %s OPL',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.wait.newBlocks(10);
+      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+      console.log('[Opal -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+    });
+  });
+});
addedjs-packages/tests/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/xcm/xcmQuartz.test.ts
@@ -0,0 +1,1636 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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 {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';
+
+
+
+const STATEMINE_PALLET_INSTANCE = 50;
+
+const TRANSFER_AMOUNT = 2000000000000000000000000n;
+
+const FUNDING_AMOUNT = 3_500_000_0000_000_000n;
+
+const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
+
+const USDT_ASSET_ID = 100;
+const USDT_ASSET_METADATA_DECIMALS = 18;
+const USDT_ASSET_METADATA_NAME = 'USDT';
+const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
+const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
+
+const SAFE_XCM_VERSION = 2;
+
+describeXCM('[XCM] Integration test: Exchanging USDT with Statemine', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  let balanceStmnBefore: bigint;
+  let balanceStmnAfter: bigint;
+
+  let balanceQuartzBefore: bigint;
+  let balanceQuartzAfter: bigint;
+  let balanceQuartzFinal: bigint;
+
+  let balanceBobBefore: bigint;
+  let balanceBobAfter: bigint;
+  let balanceBobFinal: bigint;
+
+  let balanceBobRelayTokenBefore: bigint;
+  let balanceBobRelayTokenAfter: bigint;
+
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      bob = await privateKey('//Bob'); // sovereign account on Statemine(t) funds donor
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      // Fund accounts on Statemine(t)
+      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
+      await helper.xcm.teleportNativeAsset(alice, STATEMINE_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
+    });
+
+    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+      const sovereignFundingAmount = 3_500_000_000n;
+
+      await helper.assets.create(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      );
+      await helper.assets.setMetadata(
+        alice,
+        USDT_ASSET_ID,
+        USDT_ASSET_METADATA_NAME,
+        USDT_ASSET_METADATA_DESCRIPTION,
+        USDT_ASSET_METADATA_DECIMALS,
+      );
+      await helper.assets.mint(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_AMOUNT,
+      );
+
+      // funding parachain sovereing account on Statemine(t).
+      // The sovereign account should be created before any action
+      // (the assets pallet on Statemine(t) check if the sovereign account exists)
+      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(QUARTZ_CHAIN);
+      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
+    });
+
+
+    await usingPlaygrounds(async (helper) => {
+      const location = {
+        V2: {
+          parents: 1,
+          interior: {X3: [
+            {
+              Parachain: STATEMINE_CHAIN,
+            },
+            {
+              PalletInstance: STATEMINE_PALLET_INSTANCE,
+            },
+            {
+              GeneralIndex: USDT_ASSET_ID,
+            },
+          ]},
+        },
+      };
+
+      const metadata =
+      {
+        name: USDT_ASSET_ID,
+        symbol: USDT_ASSET_METADATA_NAME,
+        decimals: USDT_ASSET_METADATA_DECIMALS,
+        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      };
+      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+      balanceQuartzBefore = await helper.balance.getSubstrate(alice.address);
+    });
+
+
+    // Providing the relay currency to the quartz sender account
+    // (fee for USDT XCM are paid in relay tokens)
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+
+  });
+
+  itSub('Should connect and send USDT from Statemine to Quartz', async ({helper}) => {
+    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+      const dest = {
+        V2: {
+          parents: 1,
+          interior: {X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: {
+                  X2: [
+                    {
+                      PalletInstance: STATEMINE_PALLET_INSTANCE,
+                    },
+                    {
+                      GeneralIndex: USDT_ASSET_ID,
+                    },
+                  ]},
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
+      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
+
+      // common good parachain take commission in it native token
+      console.log(
+        '[Statemine -> Quartz] transaction fees on Statemine: %s WND',
+        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINE_DECIMALS),
+      );
+      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
+
+    });
+
+
+    // ensure that asset has been delivered
+    await helper.wait.newBlocks(3);
+
+    // expext collection id will be with id 1
+    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+
+    balanceQuartzAfter = await helper.balance.getSubstrate(alice.address);
+
+    console.log(
+      '[Statemine -> Quartz] transaction fees on Quartz: %s USDT',
+      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
+    );
+    console.log(
+      '[Statemine -> Quartz] transaction fees on Quartz: %s QTZ',
+      helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzBefore),
+    );
+    // commission has not paid in USDT token
+    expect(free).to.be.equal(TRANSFER_AMOUNT);
+    // ... and parachain native token
+    expect(balanceQuartzAfter == balanceQuartzBefore).to.be.true;
+  });
+
+  itSub('Should connect and send USDT from Quartz to Statemine back', async ({helper}) => {
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {X2: [
+          {
+            Parachain: STATEMINE_CHAIN,
+          },
+          {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          },
+        ]},
+      },
+    };
+
+    const relayFee = 400_000_000_000_000n;
+    const currencies: [any, bigint][] = [
+      [
+        {
+          ForeignAssetId: 0,
+        },
+        TRANSFER_AMOUNT,
+      ],
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        relayFee,
+      ],
+    ];
+
+    const feeItem = 1;
+
+    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
+
+    // the commission has been paid in parachain native token
+    balanceQuartzFinal = await helper.balance.getSubstrate(alice.address);
+    console.log('[Quartz -> Statemine] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(balanceQuartzAfter - balanceQuartzFinal));
+    expect(balanceQuartzAfter > balanceQuartzFinal).to.be.true;
+
+    await usingStateminePlaygrounds(statemineUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+
+      // The USDT token never paid fees. Its amount not changed from begin value.
+      // Also check that xcm transfer has been succeeded
+      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
+    });
+  });
+
+  itSub('Should connect and send Relay token to Quartz', async ({helper}) => {
+    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+
+    await helper.wait.newBlocks(3);
+
+    balanceBobAfter = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    const wndFeeOnQuartz = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
+    const wndDiffOnQuartz = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
+    console.log(
+      '[Relay (Westend) -> Quartz] transaction fees: %s QTZ',
+      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+    );
+    console.log(
+      '[Relay (Westend) -> Quartz] transaction fees: %s WND',
+      helper.util.bigIntToDecimals(wndFeeOnQuartz, STATEMINE_DECIMALS),
+    );
+    console.log('[Relay (Westend) -> Quartz] actually delivered: %s WND', wndDiffOnQuartz);
+    expect(wndFeeOnQuartz == 0n, 'No incoming WND fees should be taken').to.be.true;
+    expect(balanceBobBefore == balanceBobAfter, 'No incoming QTZ fees should be taken').to.be.true;
+  });
+
+  itSub('Should connect and send Relay token back', async ({helper}) => {
+    let relayTokenBalanceBefore: bigint;
+    let relayTokenBalanceAfter: bigint;
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    });
+
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1:{
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const currencies: any = [
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        TRANSFER_AMOUNT_RELAY,
+      ],
+    ];
+
+    const feeItem = 0;
+
+    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
+
+    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
+    console.log('[Quartz -> Relay (Westend)] transaction fees: %s QTZ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.wait.newBlocks(10);
+      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+      console.log('[Quartz -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+    });
+  });
+});
+
+describeXCM('[XCM] Integration test: Exchanging tokens with Karura', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  let balanceQuartzTokenInit: bigint;
+  let balanceQuartzTokenMiddle: bigint;
+  let balanceQuartzTokenFinal: bigint;
+  let balanceKaruraTokenInit: bigint;
+  let balanceKaruraTokenMiddle: bigint;
+  let balanceKaruraTokenFinal: bigint;
+  let balanceQuartzForeignTokenInit: bigint;
+  let balanceQuartzForeignTokenMiddle: bigint;
+  let balanceQuartzForeignTokenFinal: bigint;
+
+  // computed by a test transfer from prod Quartz to prod Karura.
+  // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e9
+  // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block)
+  const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n;
+  const karuraEps = 8n * 10n ** 16n;
+
+  let karuraBackwardTransferAmount: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: QUARTZ_CHAIN,
+            },
+          },
+        },
+      };
+
+      const metadata = {
+        name: 'Quartz',
+        symbol: 'QTZ',
+        decimals: 18,
+        minimalBalance: 1000000000000000000n,
+      };
+
+      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v]: [any, any]) =>
+        hexToString(v.toJSON()['symbol'])) as string[];
+
+      if(!assets.includes('QTZ')) {
+        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+      } else {
+        console.log('QTZ token already registered on Karura assetRegistry pallet');
+      }
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+      balanceKaruraTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+      balanceQuartzForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
+      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+    });
+  });
+
+  itSub('Should connect and send QTZ to Karura', async ({helper}) => {
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: KARURA_CHAIN,
+          },
+        },
+      },
+    };
+
+    const beneficiary = {
+      V2: {
+        parents: 0,
+        interior: {
+          X1: {
+            AccountId32: {
+              network: 'Any',
+              id: randomAccount.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const assets = {
+      V2: [
+        {
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: TRANSFER_AMOUNT,
+          },
+        },
+      ],
+    };
+
+    const feeAssetItem = 0;
+
+    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
+
+    const qtzFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
+    expect(qtzFees > 0n, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
+    console.log('[Quartz -> Karura] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
+
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+
+      balanceQuartzForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
+      balanceKaruraTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
+
+      const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;
+      const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;
+      karuraBackwardTransferAmount = qtzIncomeTransfer;
+
+      const karUnqFees = TRANSFER_AMOUNT - qtzIncomeTransfer;
+
+      console.log(
+        '[Quartz -> Karura] transaction fees on Karura: %s KAR',
+        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),
+      );
+      console.log(
+        '[Quartz -> Karura] transaction fees on Karura: %s QTZ',
+        helper.util.bigIntToDecimals(karUnqFees),
+      );
+      console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));
+      expect(karFees == 0n).to.be.true;
+
+      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;
+
+      expect(
+        bigintAbs(karUnqFees - expectedKaruraIncomeFee) < karuraEps,
+        'Karura took different income fee, check the Karura foreign asset config',
+      ).to.be.true;
+    });
+  });
+
+  itSub('Should connect to Karura and send QTZ back', async ({helper}) => {
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X2: [
+              {Parachain: QUARTZ_CHAIN},
+              {
+                AccountId32: {
+                  network: 'Any',
+                  id: randomAccount.addressRaw,
+                },
+              },
+            ],
+          },
+        },
+      };
+
+      const id = {
+        ForeignAsset: 0,
+      };
+
+      await helper.xTokens.transfer(randomAccount, id, karuraBackwardTransferAmount, destination, 'Unlimited');
+      balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
+      balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
+
+      const karFees = balanceKaruraTokenMiddle - balanceKaruraTokenFinal;
+      const qtzOutcomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenFinal;
+
+      console.log(
+        '[Karura -> Quartz] transaction fees on Karura: %s KAR',
+        helper.util.bigIntToDecimals(karFees, KARURA_DECIMALS),
+      );
+      console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));
+
+      expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;
+      expect(qtzOutcomeTransfer == karuraBackwardTransferAmount).to.be.true;
+    });
+
+    await helper.wait.newBlocks(3);
+
+    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
+    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;
+    expect(actuallyDelivered > 0).to.be.true;
+
+    console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));
+
+    const qtzFees = karuraBackwardTransferAmount - actuallyDelivered;
+    console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
+    expect(qtzFees == 0n).to.be.true;
+  });
+
+  itSub('Karura can send only up to its balance', async ({helper}) => {
+    // set Karura's sovereign account's balance
+    const karuraBalance = 10000n * (10n ** QTZ_DECIMALS);
+    const karuraSovereignAccount = helper.address.paraSiblingSovereignAccount(KARURA_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, karuraSovereignAccount, karuraBalance);
+
+    const moreThanKaruraHas = karuraBalance * 2n;
+
+    let targetAccountBalance = 0n;
+    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+    const quartzMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {Parachain: QUARTZ_CHAIN},
+        },
+      },
+    };
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanKaruraHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+    const maxWaitBlocks = 5;
+
+    // Try to trick Quartz
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+
+    // But Karura still can send the correct amount
+    const validTransferAmount = karuraBalance / 2n;
+    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      validTransferAmount,
+    );
+
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
+    });
+
+    await helper.wait.newBlocks(maxWaitBlocks);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(validTransferAmount);
+  });
+
+  itSub('Should not accept reserve transfer of QTZ from Karura', async ({helper}) => {
+    const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
+    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+    const quartzMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+        },
+      },
+    };
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: QUARTZ_CHAIN,
+            },
+          },
+        },
+      },
+      testAmount,
+    );
+
+    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      testAmount,
+    );
+
+    let maliciousXcmProgramFullIdSent: any;
+    let maliciousXcmProgramHereIdSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Quartz using full QTZ identification
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);
+
+      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+
+    // Try to trick Quartz using shortened QTZ identification
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);
+
+      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+  });
+});
+
+// These tests are relevant only when
+// the the corresponding foreign assets are not registered
+describeXCM('[XCM] Integration test: Quartz rejects non-native tokens', () => {
+  let alice: IKeyringPair;
+  let alith: IKeyringPair;
+
+  const testAmount = 100_000_000_000n;
+  let quartzParachainJunction;
+  let quartzAccountJunction;
+
+  let quartzParachainMultilocation: any;
+  let quartzAccountMultilocation: any;
+  let quartzCombinedMultilocation: any;
+
+  let messageSent: any;
+
+  const maxWaitBlocks = 3;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+
+      quartzParachainJunction = {Parachain: QUARTZ_CHAIN};
+      quartzAccountJunction = {
+        AccountId32: {
+          network: 'Any',
+          id: alice.addressRaw,
+        },
+      };
+
+      quartzParachainMultilocation = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: quartzParachainJunction,
+          },
+        },
+      };
+
+      quartzAccountMultilocation = {
+        V2: {
+          parents: 0,
+          interior: {
+            X1: quartzAccountJunction,
+          },
+        },
+      };
+
+      quartzCombinedMultilocation = {
+        V2: {
+          parents: 1,
+          interior: {
+            X2: [quartzParachainJunction, quartzAccountJunction],
+          },
+        },
+      };
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    // eslint-disable-next-line require-await
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      alith = helper.account.alithAccount();
+    });
+  });
+
+  const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+  };
+
+  itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
+    await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
+      const id = {
+        Token: 'KAR',
+      };
+      const destination = quartzCombinedMultilocation;
+      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');
+
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
+
+  itSub('Quartz rejects MOVR tokens from Moonriver', async ({helper}) => {
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const id = 'SelfReserve';
+      const destination = quartzCombinedMultilocation;
+      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');
+
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
+
+  itSub('Quartz rejects SDN tokens from Shiden', async ({helper}) => {
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      const destinationParachain = quartzParachainMultilocation;
+      const beneficiary = quartzAccountMultilocation;
+      const assets = {
+        V2: [{
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: testAmount,
+          },
+        }],
+      };
+      const feeAssetItem = 0;
+
+      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [
+        destinationParachain,
+        beneficiary,
+        assets,
+        feeAssetItem,
+      ]);
+
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
+});
+
+describeXCM('[XCM] Integration test: Exchanging QTZ with Moonriver', () => {
+  // Quartz constants
+  let alice: IKeyringPair;
+  let quartzAssetLocation;
+
+  let randomAccountQuartz: IKeyringPair;
+  let randomAccountMoonriver: IKeyringPair;
+
+  // Moonriver constants
+  let assetId: string;
+
+  const quartzAssetMetadata = {
+    name: 'xcQuartz',
+    symbol: 'xcQTZ',
+    decimals: 18,
+    isFrozen: false,
+    minimalBalance: 1n,
+  };
+
+  let balanceQuartzTokenInit: bigint;
+  let balanceQuartzTokenMiddle: bigint;
+  let balanceQuartzTokenFinal: bigint;
+  let balanceForeignQtzTokenInit: bigint;
+  let balanceForeignQtzTokenMiddle: bigint;
+  let balanceForeignQtzTokenFinal: bigint;
+  let balanceMovrTokenInit: bigint;
+  let balanceMovrTokenMiddle: bigint;
+  let balanceMovrTokenFinal: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccountQuartz] = await helper.arrange.createAccounts([0n], alice);
+
+      balanceForeignQtzTokenInit = 0n;
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const alithAccount = helper.account.alithAccount();
+      const baltatharAccount = helper.account.baltatharAccount();
+      const dorothyAccount = helper.account.dorothyAccount();
+
+      randomAccountMoonriver = helper.account.create();
+
+      // >>> Sponsoring Dorothy >>>
+      console.log('Sponsoring Dorothy.......');
+      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring Dorothy.......DONE');
+      // <<< Sponsoring Dorothy <<<
+
+      quartzAssetLocation = {
+        XCM: {
+          parents: 1,
+          interior: {X1: {Parachain: QUARTZ_CHAIN}},
+        },
+      };
+      const existentialDeposit = 1n;
+      const isSufficient = true;
+      const unitsPerSecond = 1n;
+      const numAssetsWeightHint = 0;
+
+      if((await helper.assetManager.assetTypeId(quartzAssetLocation)).toJSON()) {
+        console.log('Quartz asset already registered on Moonriver');
+      } else {
+        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+          location: quartzAssetLocation,
+          metadata: quartzAssetMetadata,
+          existentialDeposit,
+          isSufficient,
+          unitsPerSecond,
+          numAssetsWeightHint,
+        });
+
+        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+
+        await helper.fastDemocracy.executeProposal('register QTZ foreign asset', encodedProposal);
+      }
+      // >>> Acquire Quartz AssetId Info on Moonriver >>>
+      console.log('Acquire Quartz AssetId Info on Moonriver.......');
+
+      assetId = (await helper.assetManager.assetTypeId(quartzAssetLocation)).toString();
+
+      console.log('QTZ asset ID is %s', assetId);
+      console.log('Acquire Quartz AssetId Info on Moonriver.......DONE');
+      // >>> Acquire Quartz AssetId Info on Moonriver >>>
+
+      // >>> Sponsoring random Account >>>
+      console.log('Sponsoring random Account.......');
+      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonriver.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring random Account.......DONE');
+      // <<< Sponsoring random Account <<<
+
+      balanceMovrTokenInit = await helper.balance.getEthereum(randomAccountMoonriver.address);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccountQuartz.address, 10n * TRANSFER_AMOUNT);
+      balanceQuartzTokenInit = await helper.balance.getSubstrate(randomAccountQuartz.address);
+    });
+  });
+
+  itSub('Should connect and send QTZ to Moonriver', async ({helper}) => {
+    const currencyId = {
+      NativeAssetId: 'Here',
+    };
+    const dest = {
+      V2: {
+        parents: 1,
+        interior: {
+          X2: [
+            {Parachain: MOONRIVER_CHAIN},
+            {AccountKey20: {network: 'Any', key: randomAccountMoonriver.address}},
+          ],
+        },
+      },
+    };
+    const amount = TRANSFER_AMOUNT;
+
+    await helper.xTokens.transfer(randomAccountQuartz, currencyId, amount, dest, 'Unlimited');
+
+    balanceQuartzTokenMiddle = await helper.balance.getSubstrate(randomAccountQuartz.address);
+    expect(balanceQuartzTokenMiddle < balanceQuartzTokenInit).to.be.true;
+
+    const transactionFees = balanceQuartzTokenInit - balanceQuartzTokenMiddle - TRANSFER_AMOUNT;
+    console.log('[Quartz -> Moonriver] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(transactionFees));
+    expect(transactionFees > 0, 'Negative fees QTZ, looks like nothing was transferred').to.be.true;
+
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+
+      balanceMovrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonriver.address);
+
+      const movrFees = balanceMovrTokenInit - balanceMovrTokenMiddle;
+      console.log('[Quartz -> Moonriver] transaction fees on Moonriver: %s MOVR',helper.util.bigIntToDecimals(movrFees));
+      expect(movrFees == 0n).to.be.true;
+
+      balanceForeignQtzTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonriver.address))!; // BigInt(qtzRandomAccountAsset['balance']);
+      const qtzIncomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenInit;
+      console.log('[Quartz -> Moonriver] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));
+      expect(qtzIncomeTransfer == TRANSFER_AMOUNT).to.be.true;
+    });
+  });
+
+  itSub('Should connect to Moonriver and send QTZ back', async ({helper}) => {
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const asset = {
+        V2: {
+          id: {
+            Concrete: {
+              parents: 1,
+              interior: {
+                X1: {Parachain: QUARTZ_CHAIN},
+              },
+            },
+          },
+          fun: {
+            Fungible: TRANSFER_AMOUNT,
+          },
+        },
+      };
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X2: [
+              {Parachain: QUARTZ_CHAIN},
+              {AccountId32: {network: 'Any', id: randomAccountQuartz.addressRaw}},
+            ],
+          },
+        },
+      };
+
+      await helper.xTokens.transferMultiasset(randomAccountMoonriver, asset, destination, 'Unlimited');
+
+      balanceMovrTokenFinal = await helper.balance.getEthereum(randomAccountMoonriver.address);
+
+      const movrFees = balanceMovrTokenMiddle - balanceMovrTokenFinal;
+      console.log('[Moonriver -> Quartz] transaction fees on Moonriver: %s MOVR', helper.util.bigIntToDecimals(movrFees));
+      expect(movrFees > 0, 'Negative fees MOVR, looks like nothing was transferred').to.be.true;
+
+      const qtzRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonriver.address);
+
+      expect(qtzRandomAccountAsset).to.be.null;
+
+      balanceForeignQtzTokenFinal = 0n;
+
+      const qtzOutcomeTransfer = balanceForeignQtzTokenMiddle - balanceForeignQtzTokenFinal;
+      console.log('[Quartz -> Moonriver] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));
+      expect(qtzOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
+    });
+
+    await helper.wait.newBlocks(3);
+
+    balanceQuartzTokenFinal = await helper.balance.getSubstrate(randomAccountQuartz.address);
+    const actuallyDelivered = balanceQuartzTokenFinal - balanceQuartzTokenMiddle;
+    expect(actuallyDelivered > 0).to.be.true;
+
+    console.log('[Moonriver -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));
+
+    const qtzFees = TRANSFER_AMOUNT - actuallyDelivered;
+    console.log('[Moonriver -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
+    expect(qtzFees == 0n).to.be.true;
+  });
+
+  itSub('Moonriver can send only up to its balance', async ({helper}) => {
+    // set Moonriver's sovereign account's balance
+    const moonriverBalance = 10000n * (10n ** QTZ_DECIMALS);
+    const moonriverSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONRIVER_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, moonriverSovereignAccount, moonriverBalance);
+
+    const moreThanMoonriverHas = moonriverBalance * 2n;
+
+    let targetAccountBalance = 0n;
+    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+    const quartzMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {Parachain: QUARTZ_CHAIN},
+        },
+      },
+    };
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanMoonriverHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Quartz
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgram]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('try to spend more QTZ than Moonriver has', batchCall);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+
+    // But Moonriver still can send the correct amount
+    const validTransferAmount = moonriverBalance / 2n;
+    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      validTransferAmount,
+    );
+
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, validXcmProgram]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('Spend the correct amount of QTZ', batchCall);
+    });
+
+    await helper.wait.newBlocks(maxWaitBlocks);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(validTransferAmount);
+  });
+
+  itSub('Should not accept reserve transfer of QTZ from Moonriver', async ({helper}) => {
+    const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
+    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+    const quartzMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+        },
+      },
+    };
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: {
+            X1: {
+              Parachain: QUARTZ_CHAIN,
+            },
+          },
+        },
+      },
+      testAmount,
+    );
+
+    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      testAmount,
+    );
+
+    let maliciousXcmProgramFullIdSent: any;
+    let maliciousXcmProgramHereIdSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Quartz using full QTZ identification
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramFullId]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using path asset identification', batchCall);
+
+      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+
+    // Try to trick Quartz using shortened QTZ identification
+    await usingMoonriverPlaygrounds(moonriverUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [quartzMultilocation, maliciousXcmProgramHereId]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('try to act like a reserve location for QTZ using "here" asset identification', batchCall);
+
+      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+  });
+});
+
+describeXCM('[XCM] Integration test: Exchanging tokens with Shiden', () => {
+  let alice: IKeyringPair;
+  let sender: IKeyringPair;
+
+  const QTZ_ASSET_ID_ON_SHIDEN = 18_446_744_073_709_551_633n; // The value is taken from the live Shiden
+  const QTZ_MINIMAL_BALANCE_ON_SHIDEN = 1n; // The value is taken from the live Shiden
+
+  // Quartz -> Shiden
+  const shidenInitialBalance = 1n * (10n ** SHIDEN_DECIMALS); // 1 SHD, existential deposit required to actually create the account on Shiden
+  const unitsPerSecond = 500_451_000_000_000_000_000n; // The value is taken from the live Shiden
+  const qtzToShidenTransferred = 10n * (10n ** QTZ_DECIMALS); // 10 QTZ
+  const qtzToShidenArrived = 7_998_196_000_000_000_000n; // 7.99 ... QTZ, Shiden takes a commision in foreign tokens
+
+  // Shiden -> Quartz
+  const qtzFromShidenTransfered = 5n * (10n ** QTZ_DECIMALS); // 5 QTZ
+  const qtzOnShidenLeft = qtzToShidenArrived - qtzFromShidenTransfered; // 2.99 ... QTZ
+
+  let balanceAfterQuartzToShidenXCM: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [sender] = await helper.arrange.createAccounts([100n], alice);
+      console.log('sender', sender.address);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      if(!(await helper.callRpc('api.query.assets.asset', [QTZ_ASSET_ID_ON_SHIDEN])).toJSON()) {
+        console.log('1. Create foreign asset and metadata');
+        await helper.getSudo().assets.forceCreate(
+          alice,
+          QTZ_ASSET_ID_ON_SHIDEN,
+          alice.address,
+          QTZ_MINIMAL_BALANCE_ON_SHIDEN,
+        );
+
+        await helper.assets.setMetadata(
+          alice,
+          QTZ_ASSET_ID_ON_SHIDEN,
+          'Quartz',
+          'QTZ',
+          Number(QTZ_DECIMALS),
+        );
+
+        console.log('2. Register asset location on Shiden');
+        const assetLocation = {
+          V2: {
+            parents: 1,
+            interior: {
+              X1: {
+                Parachain: QUARTZ_CHAIN,
+              },
+            },
+          },
+        };
+
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, QTZ_ASSET_ID_ON_SHIDEN]);
+
+        console.log('3. Set QTZ payment for XCM execution on Shiden');
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+      } else {
+        console.log('QTZ is already registered on Shiden');
+      }
+      console.log('4. Transfer 1 SDN to recipient to create the account (needed due to existential balance)');
+      await helper.balance.transferToSubstrate(alice, sender.address, shidenInitialBalance);
+    });
+  });
+
+  itSub('Should connect and send QTZ to Shiden', async ({helper}) => {
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: SHIDEN_CHAIN,
+          },
+        },
+      },
+    };
+
+    const beneficiary = {
+      V2: {
+        parents: 0,
+        interior: {
+          X1: {
+            AccountId32: {
+              network: 'Any',
+              id: sender.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const assets = {
+      V2: [
+        {
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: qtzToShidenTransferred,
+          },
+        },
+      ],
+    };
+
+    // Initial balance is 100 QTZ
+    const balanceBefore = await helper.balance.getSubstrate(sender.address);
+    console.log(`Initial balance is: ${balanceBefore}`);
+
+    const feeAssetItem = 0;
+    await helper.xcm.limitedReserveTransferAssets(sender, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+    // Balance after reserve transfer is less than 90
+    balanceAfterQuartzToShidenXCM = await helper.balance.getSubstrate(sender.address);
+    console.log(`QTZ Balance on Quartz after XCM is: ${balanceAfterQuartzToShidenXCM}`);
+    console.log(`Quartz's QTZ commission is: ${balanceBefore - balanceAfterQuartzToShidenXCM}`);
+    expect(balanceBefore - balanceAfterQuartzToShidenXCM > 0).to.be.true;
+
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+      const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address);
+      const shidenBalance = await helper.balance.getSubstrate(sender.address);
+
+      console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`);
+      console.log(`Shiden's QTZ commission is: ${qtzToShidenTransferred - xcQTZbalance!}`);
+
+      expect(xcQTZbalance).to.eq(qtzToShidenArrived);
+      // SHD balance does not changed:
+      expect(shidenBalance).to.eq(shidenInitialBalance);
+    });
+  });
+
+  itSub('Should connect to Shiden and send QTZ back', async ({helper}) => {
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: QUARTZ_CHAIN,
+            },
+          },
+        },
+      };
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {
+            X1: {
+              AccountId32: {
+                network: 'Any',
+                id: sender.addressRaw,
+              },
+            },
+          },
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 1,
+                interior: {
+                  X1: {
+                    Parachain: QUARTZ_CHAIN,
+                  },
+                },
+              },
+            },
+            fun: {
+              Fungible: qtzFromShidenTransfered,
+            },
+          },
+        ],
+      };
+
+      // Initial balance is 1 SDN
+      const balanceSDNbefore = await helper.balance.getSubstrate(sender.address);
+      console.log(`SDN balance is: ${balanceSDNbefore}, it does not changed`);
+      expect(balanceSDNbefore).to.eq(shidenInitialBalance);
+
+      const feeAssetItem = 0;
+      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw
+      await helper.executeExtrinsic(sender, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);
+
+      // Balance after reserve transfer is less than 1 SDN
+      const xcQTZbalance = await helper.assets.account(QTZ_ASSET_ID_ON_SHIDEN, sender.address);
+      const balanceSDN = await helper.balance.getSubstrate(sender.address);
+      console.log(`xcQTZ balance on Shiden after XCM is: ${xcQTZbalance}`);
+
+      // Assert: xcQTZ balance correctly decreased
+      expect(xcQTZbalance).to.eq(qtzOnShidenLeft);
+      // Assert: SDN balance is 0.996...
+      expect(balanceSDN / (10n ** (SHIDEN_DECIMALS - 3n))).to.eq(996n);
+    });
+
+    await helper.wait.newBlocks(3);
+    const balanceQTZ = await helper.balance.getSubstrate(sender.address);
+    console.log(`QTZ Balance on Quartz after XCM is: ${balanceQTZ}`);
+    expect(balanceQTZ).to.eq(balanceAfterQuartzToShidenXCM + qtzFromShidenTransfered);
+  });
+
+  itSub('Shiden can send only up to its balance', async ({helper}) => {
+    // set Shiden's sovereign account's balance
+    const shidenBalance = 10000n * (10n ** QTZ_DECIMALS);
+    const shidenSovereignAccount = helper.address.paraSiblingSovereignAccount(SHIDEN_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, shidenSovereignAccount, shidenBalance);
+
+    const moreThanShidenHas = shidenBalance * 2n;
+
+    let targetAccountBalance = 0n;
+    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+    const quartzMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {Parachain: QUARTZ_CHAIN},
+        },
+      },
+    };
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanShidenHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Quartz
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgram);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+
+    // But Shiden still can send the correct amount
+    const validTransferAmount = shidenBalance / 2n;
+    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      validTransferAmount,
+    );
+
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, validXcmProgram);
+    });
+
+    await helper.wait.newBlocks(maxWaitBlocks);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(validTransferAmount);
+  });
+
+  itSub('Should not accept reserve transfer of QTZ from Shiden', async ({helper}) => {
+    const testAmount = 10_000n * (10n ** QTZ_DECIMALS);
+    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+    const quartzMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: QUARTZ_CHAIN,
+          },
+        },
+      },
+    };
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: QUARTZ_CHAIN,
+            },
+          },
+        },
+      },
+      testAmount,
+    );
+
+    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      testAmount,
+    );
+
+    let maliciousXcmProgramFullIdSent: any;
+    let maliciousXcmProgramHereIdSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Quartz using full QTZ identification
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramFullId);
+
+      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+
+    // Try to trick Quartz using shortened QTZ identification
+    await usingShidenPlaygrounds(shidenUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, quartzMultilocation, maliciousXcmProgramHereId);
+
+      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+  });
+});
addedjs-packages/tests/xcm/xcmUnique.test.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/tests/xcm/xcmUnique.test.ts
@@ -0,0 +1,1834 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// 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 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 {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';
+
+
+const STATEMINT_PALLET_INSTANCE = 50;
+
+const relayUrl = config.relayUrl;
+const statemintUrl = config.statemintUrl;
+const acalaUrl = config.acalaUrl;
+const moonbeamUrl = config.moonbeamUrl;
+const astarUrl = config.astarUrl;
+const polkadexUrl = config.polkadexUrl;
+
+const RELAY_DECIMALS = 12;
+const STATEMINT_DECIMALS = 12;
+const ACALA_DECIMALS = 12;
+const ASTAR_DECIMALS = 18n;
+const UNQ_DECIMALS = 18n;
+
+const TRANSFER_AMOUNT = 2000000000000000000000000n;
+
+const FUNDING_AMOUNT = 3_500_000_0000_000_000n;
+
+const TRANSFER_AMOUNT_RELAY = 50_000_000_000_000_000n;
+
+const USDT_ASSET_ID = 100;
+const USDT_ASSET_METADATA_DECIMALS = 18;
+const USDT_ASSET_METADATA_NAME = 'USDT';
+const USDT_ASSET_METADATA_DESCRIPTION = 'USDT';
+const USDT_ASSET_METADATA_MINIMAL_BALANCE = 1n;
+const USDT_ASSET_AMOUNT = 10_000_000_000_000_000_000_000_000n;
+
+describeXCM('[XCM] Integration test: Exchanging USDT with Statemint', () => {
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+
+  let balanceStmnBefore: bigint;
+  let balanceStmnAfter: bigint;
+
+  let balanceUniqueBefore: bigint;
+  let balanceUniqueAfter: bigint;
+  let balanceUniqueFinal: bigint;
+
+  let balanceBobBefore: bigint;
+  let balanceBobAfter: bigint;
+  let balanceBobFinal: bigint;
+
+  let balanceBobRelayTokenBefore: bigint;
+  let balanceBobRelayTokenAfter: bigint;
+
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      bob = await privateKey('//Bob'); // sovereign account on Statemint funds donor
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      // Fund accounts on Statemint
+      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
+      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
+    });
+
+    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+      const sovereignFundingAmount = 3_500_000_000n;
+
+      await helper.assets.create(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      );
+      await helper.assets.setMetadata(
+        alice,
+        USDT_ASSET_ID,
+        USDT_ASSET_METADATA_NAME,
+        USDT_ASSET_METADATA_DESCRIPTION,
+        USDT_ASSET_METADATA_DECIMALS,
+      );
+      await helper.assets.mint(
+        alice,
+        USDT_ASSET_ID,
+        alice.address,
+        USDT_ASSET_AMOUNT,
+      );
+
+      // funding parachain sovereing account on Statemint.
+      // The sovereign account should be created before any action
+      // (the assets pallet on Statemint check if the sovereign account exists)
+      const parachainSovereingAccount = helper.address.paraSiblingSovereignAccount(UNIQUE_CHAIN);
+      await helper.balance.transferToSubstrate(bob, parachainSovereingAccount, sovereignFundingAmount);
+    });
+
+
+    await usingPlaygrounds(async (helper) => {
+      const location = {
+        V2: {
+          parents: 1,
+          interior: {X3: [
+            {
+              Parachain: STATEMINT_CHAIN,
+            },
+            {
+              PalletInstance: STATEMINT_PALLET_INSTANCE,
+            },
+            {
+              GeneralIndex: USDT_ASSET_ID,
+            },
+          ]},
+        },
+      };
+
+      const metadata =
+      {
+        name: USDT_ASSET_ID,
+        symbol: USDT_ASSET_METADATA_NAME,
+        decimals: USDT_ASSET_METADATA_DECIMALS,
+        minimalBalance: USDT_ASSET_METADATA_MINIMAL_BALANCE,
+      };
+      await helper.getSudo().foreignAssets.register(alice, alice.address, location, metadata);
+      balanceUniqueBefore = await helper.balance.getSubstrate(alice.address);
+    });
+
+
+    // Providing the relay currency to the unique sender account
+    // (fee for USDT XCM are paid in relay tokens)
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(alice, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+
+  });
+
+  itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {
+    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+      const dest = {
+        V2: {
+          parents: 1,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: {
+                  X2: [
+                    {
+                      PalletInstance: STATEMINT_PALLET_INSTANCE,
+                    },
+                    {
+                      GeneralIndex: USDT_ASSET_ID,
+                    },
+                  ]},
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      balanceStmnBefore = await helper.balance.getSubstrate(alice.address);
+      await helper.xcm.limitedReserveTransferAssets(alice, dest, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+      balanceStmnAfter = await helper.balance.getSubstrate(alice.address);
+
+      // common good parachain take commission in it native token
+      console.log(
+        '[Statemint -> Unique] transaction fees on Statemint: %s WND',
+        helper.util.bigIntToDecimals(balanceStmnBefore - balanceStmnAfter, STATEMINT_DECIMALS),
+      );
+      expect(balanceStmnBefore > balanceStmnAfter).to.be.true;
+
+    });
+
+
+    // ensure that asset has been delivered
+    await helper.wait.newBlocks(3);
+
+    // expext collection id will be with id 1
+    const free = await helper.ft.getBalance(1, {Substrate: alice.address});
+
+    balanceUniqueAfter = await helper.balance.getSubstrate(alice.address);
+
+    console.log(
+      '[Statemint -> Unique] transaction fees on Unique: %s USDT',
+      helper.util.bigIntToDecimals(TRANSFER_AMOUNT - free, USDT_ASSET_METADATA_DECIMALS),
+    );
+    console.log(
+      '[Statemint -> Unique] transaction fees on Unique: %s UNQ',
+      helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueBefore),
+    );
+    // commission has not paid in USDT token
+    expect(free).to.be.equal(TRANSFER_AMOUNT);
+    // ... and parachain native token
+    expect(balanceUniqueAfter == balanceUniqueBefore).to.be.true;
+  });
+
+  itSub('Should connect and send USDT from Unique to Statemint back', async ({helper}) => {
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {X2: [
+          {
+            Parachain: STATEMINT_CHAIN,
+          },
+          {
+            AccountId32: {
+              network: 'Any',
+              id: alice.addressRaw,
+            },
+          },
+        ]},
+      },
+    };
+
+    const relayFee = 400_000_000_000_000n;
+    const currencies: [any, bigint][] = [
+      [
+        {
+          ForeignAssetId: 0,
+        },
+        TRANSFER_AMOUNT,
+      ],
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        relayFee,
+      ],
+    ];
+
+    const feeItem = 1;
+
+    await helper.xTokens.transferMulticurrencies(alice, currencies, feeItem, destination, 'Unlimited');
+
+    // the commission has been paid in parachain native token
+    balanceUniqueFinal = await helper.balance.getSubstrate(alice.address);
+    console.log('[Unique -> Statemint] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(balanceUniqueAfter - balanceUniqueFinal));
+    expect(balanceUniqueAfter > balanceUniqueFinal).to.be.true;
+
+    await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+
+      // The USDT token never paid fees. Its amount not changed from begin value.
+      // Also check that xcm transfer has been succeeded
+      expect((await helper.assets.account(USDT_ASSET_ID, alice.address))! == USDT_ASSET_AMOUNT).to.be.true;
+    });
+  });
+
+  itSub('Should connect and send Relay token to Unique', async ({helper}) => {
+    balanceBobBefore = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenBefore = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+          },
+        }};
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {X1: {
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          }},
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 0,
+                interior: 'Here',
+              },
+            },
+            fun: {
+              Fungible: TRANSFER_AMOUNT_RELAY,
+            },
+          },
+        ],
+      };
+
+      const feeAssetItem = 0;
+
+      await helper.xcm.limitedReserveTransferAssets(bob, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    });
+
+    await helper.wait.newBlocks(3);
+
+    balanceBobAfter = await helper.balance.getSubstrate(bob.address);
+    balanceBobRelayTokenAfter = await helper.tokens.accounts(bob.address, {NativeAssetId: 'Parent'});
+
+    const wndFeeOnUnique = balanceBobRelayTokenAfter - TRANSFER_AMOUNT_RELAY - balanceBobRelayTokenBefore;
+    const wndDiffOnUnique = balanceBobRelayTokenAfter - balanceBobRelayTokenBefore;
+    console.log(
+      '[Relay (Westend) -> Unique] transaction fees: %s UNQ',
+      helper.util.bigIntToDecimals(balanceBobAfter - balanceBobBefore),
+    );
+    console.log(
+      '[Relay (Westend) -> Unique] transaction fees: %s WND',
+      helper.util.bigIntToDecimals(wndFeeOnUnique, STATEMINT_DECIMALS),
+    );
+    console.log('[Relay (Westend) -> Unique] actually delivered: %s WND', wndDiffOnUnique);
+    expect(wndFeeOnUnique == 0n, 'No incoming WND fees should be taken').to.be.true;
+    expect(balanceBobBefore == balanceBobAfter, 'No incoming UNQ fees should be taken').to.be.true;
+  });
+
+  itSub('Should connect and send Relay token back', async ({helper}) => {
+    let relayTokenBalanceBefore: bigint;
+    let relayTokenBalanceAfter: bigint;
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      relayTokenBalanceBefore = await helper.balance.getSubstrate(bob.address);
+    });
+
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1:{
+            AccountId32: {
+              network: 'Any',
+              id: bob.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const currencies: any = [
+      [
+        {
+          NativeAssetId: 'Parent',
+        },
+        TRANSFER_AMOUNT_RELAY,
+      ],
+    ];
+
+    const feeItem = 0;
+
+    await helper.xTokens.transferMulticurrencies(bob, currencies, feeItem, destination, 'Unlimited');
+
+    balanceBobFinal = await helper.balance.getSubstrate(bob.address);
+    console.log('[Unique -> Relay (Westend)] transaction fees: %s UNQ',  helper.util.bigIntToDecimals(balanceBobAfter - balanceBobFinal));
+
+    await usingRelayPlaygrounds(relayUrl, async (helper) => {
+      await helper.wait.newBlocks(10);
+      relayTokenBalanceAfter = await helper.balance.getSubstrate(bob.address);
+
+      const diff = relayTokenBalanceAfter - relayTokenBalanceBefore;
+      console.log('[Unique -> Relay (Westend)] actually delivered: %s WND', helper.util.bigIntToDecimals(diff, RELAY_DECIMALS));
+      expect(diff > 0, 'Relay tokens was not delivered back').to.be.true;
+    });
+  });
+});
+
+describeXCM('[XCM] Integration test: Exchanging tokens with Acala', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  let balanceUniqueTokenInit: bigint;
+  let balanceUniqueTokenMiddle: bigint;
+  let balanceUniqueTokenFinal: bigint;
+  let balanceAcalaTokenInit: bigint;
+  let balanceAcalaTokenMiddle: bigint;
+  let balanceAcalaTokenFinal: bigint;
+  let balanceUniqueForeignTokenInit: bigint;
+  let balanceUniqueForeignTokenMiddle: bigint;
+  let balanceUniqueForeignTokenFinal: bigint;
+
+  // computed by a test transfer from prod Unique to prod Acala.
+  // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48
+  // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)
+  const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;
+  const acalaEps = 8n * 10n ** 16n;
+
+  let acalaBackwardTransferAmount: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: UNIQUE_CHAIN,
+            },
+          },
+        },
+      };
+
+      const metadata = {
+        name: 'Unique Network',
+        symbol: 'UNQ',
+        decimals: 18,
+        minimalBalance: 1250000000000000000n,
+      };
+      const assets = (await (helper.callRpc('api.query.assetRegistry.assetMetadatas.entries'))).map(([_k, v] : [any, any]) =>
+        hexToString(v.toJSON()['symbol'])) as string[];
+
+      if(!assets.includes('UNQ')) {
+        await helper.getSudo().assetRegistry.registerForeignAsset(alice, destination, metadata);
+      } else {
+        console.log('UNQ token already registered on Acala assetRegistry pallet');
+      }
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+      balanceAcalaTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+      balanceUniqueForeignTokenInit = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
+      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Acala', async ({helper}) => {
+
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: ACALA_CHAIN,
+          },
+        },
+      },
+    };
+
+    const beneficiary = {
+      V2: {
+        parents: 0,
+        interior: {
+          X1: {
+            AccountId32: {
+              network: 'Any',
+              id: randomAccount.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const assets = {
+      V2: [
+        {
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: TRANSFER_AMOUNT,
+          },
+        },
+      ],
+    };
+
+    const feeAssetItem = 0;
+
+    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
+
+    const unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
+    console.log('[Unique -> Acala] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
+    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
+
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+
+      balanceUniqueForeignTokenMiddle = await helper.tokens.accounts(randomAccount.address, {ForeignAsset: 0});
+      balanceAcalaTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
+
+      const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;
+      const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;
+      acalaBackwardTransferAmount = unqIncomeTransfer;
+
+      const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;
+
+      console.log(
+        '[Unique -> Acala] transaction fees on Acala: %s ACA',
+        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),
+      );
+      console.log(
+        '[Unique -> Acala] transaction fees on Acala: %s UNQ',
+        helper.util.bigIntToDecimals(acaUnqFees),
+      );
+      console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));
+      expect(acaFees == 0n).to.be.true;
+
+      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;
+
+      expect(
+        bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,
+        'Acala took different income fee, check the Acala foreign asset config',
+      ).to.be.true;
+    });
+  });
+
+  itSub('Should connect to Acala and send UNQ back', async ({helper}) => {
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X2: [
+              {Parachain: UNIQUE_CHAIN},
+              {
+                AccountId32: {
+                  network: 'Any',
+                  id: randomAccount.addressRaw,
+                },
+              },
+            ],
+          },
+        },
+      };
+
+      const id = {
+        ForeignAsset: 0,
+      };
+
+      await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');
+      balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
+      balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
+
+      const acaFees = balanceAcalaTokenMiddle - balanceAcalaTokenFinal;
+      const unqOutcomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenFinal;
+
+      console.log(
+        '[Acala -> Unique] transaction fees on Acala: %s ACA',
+        helper.util.bigIntToDecimals(acaFees, ACALA_DECIMALS),
+      );
+      console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));
+
+      expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;
+      expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;
+    });
+
+    await helper.wait.newBlocks(3);
+
+    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
+    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
+    expect(actuallyDelivered > 0).to.be.true;
+
+    console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));
+
+    const unqFees = acalaBackwardTransferAmount - actuallyDelivered;
+    console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
+    expect(unqFees == 0n).to.be.true;
+  });
+
+  itSub('Acala can send only up to its balance', async ({helper}) => {
+    // set Acala's sovereign account's balance
+    const acalaBalance = 10000n * (10n ** UNQ_DECIMALS);
+    const acalaSovereignAccount = helper.address.paraSiblingSovereignAccount(ACALA_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, acalaSovereignAccount, acalaBalance);
+
+    const moreThanAcalaHas = acalaBalance * 2n;
+
+    let targetAccountBalance = 0n;
+    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanAcalaHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Unique
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+
+    // But Acala still can send the correct amount
+    const validTransferAmount = acalaBalance / 2n;
+    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      validTransferAmount,
+    );
+
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram);
+    });
+
+    await helper.wait.newBlocks(maxWaitBlocks);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(validTransferAmount);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Acala', async ({helper}) => {
+    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
+    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+    const uniqueMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+        },
+      },
+    };
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      uniqueAssetId,
+      testAmount,
+    );
+
+    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      testAmount,
+    );
+
+    let maliciousXcmProgramFullIdSent: any;
+    let maliciousXcmProgramHereIdSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Unique using full UNQ identification
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);
+
+      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+
+    // Try to trick Unique using shortened UNQ identification
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);
+
+      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+  });
+});
+
+describeXCM('[XCM] Integration test: Exchanging tokens with Polkadex', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+  let unqFees: bigint;
+  let balanceUniqueTokenInit: bigint;
+  let balanceUniqueTokenMiddle: bigint;
+  let balanceUniqueTokenFinal: bigint;
+  const maxWaitBlocks = 6;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccount] = await helper.arrange.createAccounts([0n], alice);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      const isWhitelisted = ((await helper.callRpc('api.query.xcmHelper.whitelistedTokens', []))
+        .toJSON() as [])
+        .map(nToBigInt).length != 0;
+      /*
+      Check whether the Unique token has been added
+      to the whitelist, since an error will occur
+      if it is added again. Needed for debugging
+      when this test is run multiple times.
+      */
+      if(isWhitelisted) {
+        console.log('UNQ token is already whitelisted on Polkadex');
+      } else {
+        await helper.getSudo().xcmHelper.whitelistToken(alice, uniqueAssetId);
+      }
+
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10000000000000n);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, 10n * TRANSFER_AMOUNT);
+      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccount.address);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Polkadex', async ({helper}) => {
+
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: POLKADEX_CHAIN,
+          },
+        },
+      },
+    };
+
+    const beneficiary = {
+      V2: {
+        parents: 0,
+        interior: {
+          X1: {
+            AccountId32: {
+              network: 'Any',
+              id: randomAccount.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const assets = {
+      V2: [
+        {
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: TRANSFER_AMOUNT,
+          },
+        },
+      ],
+    };
+
+    const feeAssetItem = 0;
+
+    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+    const messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccount.address);
+
+    unqFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
+    console.log('[Unique -> Polkadex] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
+    expect(unqFees > 0n, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      /*
+        Since only the parachain part of the Polkadex
+        infrastructure is launched (without their
+        solochain validators), processing incoming
+        assets will lead to an error.
+        This error indicates that the Polkadex chain
+        received a message from the Unique network,
+        since the hash is being checked to ensure
+        it matches what was sent.
+      */
+      await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash);
+    });
+  });
+
+
+  itSub('Should connect to Polkadex and send UNQ back', async ({helper}) => {
+
+    const xcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      randomAccount.addressRaw,
+      uniqueAssetId,
+      TRANSFER_AMOUNT,
+    );
+
+    let xcmProgramSent: any;
+
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, xcmProgram);
+
+      xcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Success, event => event.messageHash == xcmProgramSent.messageHash);
+
+    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
+
+    expect(balanceUniqueTokenFinal).to.be.equal(balanceUniqueTokenInit - unqFees);
+  });
+
+  itSub('Polkadex can send only up to its balance', async ({helper}) => {
+    const polkadexBalance = 10000n * (10n ** UNQ_DECIMALS);
+    const polkadexSovereignAccount = helper.address.paraSiblingSovereignAccount(POLKADEX_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, polkadexSovereignAccount, polkadexBalance);
+    const moreThanPolkadexHas = 2n * polkadexBalance;
+
+    const targetAccount = helper.arrange.createEmptyAccount();
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanPolkadexHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, maliciousXcmProgramSent);
+
+    const targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Polkadex', async ({helper}) => {
+    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
+    const targetAccount = helper.arrange.createEmptyAccount();
+
+    const uniqueMultilocation = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: UNIQUE_CHAIN,
+          },
+        },
+      },
+    };
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      uniqueAssetId,
+      testAmount,
+    );
+
+    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      testAmount,
+    );
+
+    let maliciousXcmProgramFullIdSent: any;
+    let maliciousXcmProgramHereIdSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Unique using full UNQ identification
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramFullId);
+
+      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramFullIdSent);
+
+    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+
+    // Try to trick Unique using shortened UNQ identification
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueMultilocation, maliciousXcmProgramHereId);
+
+      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectUntrustedReserveLocationFail(helper, maliciousXcmProgramHereIdSent);
+
+    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+  });
+});
+
+// These tests are relevant only when
+// the the corresponding foreign assets are not registered
+describeXCM('[XCM] Integration test: Unique rejects non-native tokens', () => {
+  let alice: IKeyringPair;
+  let alith: IKeyringPair;
+
+  const testAmount = 100_000_000_000n;
+  let uniqueParachainJunction;
+  let uniqueAccountJunction;
+
+  let uniqueParachainMultilocation: any;
+  let uniqueAccountMultilocation: any;
+  let uniqueCombinedMultilocation: any;
+  let uniqueCombinedMultilocationAcala: any; // TODO remove when Acala goes V2
+
+  let messageSent: any;
+
+  const maxWaitBlocks = 3;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+
+      uniqueParachainJunction = {Parachain: UNIQUE_CHAIN};
+      uniqueAccountJunction = {
+        AccountId32: {
+          network: 'Any',
+          id: alice.addressRaw,
+        },
+      };
+
+      uniqueParachainMultilocation = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: uniqueParachainJunction,
+          },
+        },
+      };
+
+      uniqueAccountMultilocation = {
+        V2: {
+          parents: 0,
+          interior: {
+            X1: uniqueAccountJunction,
+          },
+        },
+      };
+
+      uniqueCombinedMultilocation = {
+        V2: {
+          parents: 1,
+          interior: {
+            X2: [uniqueParachainJunction, uniqueAccountJunction],
+          },
+        },
+      };
+
+      uniqueCombinedMultilocationAcala = {
+        V2: {
+          parents: 1,
+          interior: {
+            X2: [uniqueParachainJunction, uniqueAccountJunction],
+          },
+        },
+      };
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    // eslint-disable-next-line require-await
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      alith = helper.account.alithAccount();
+    });
+  });
+
+
+
+  itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {
+    await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
+      const id = {
+        Token: 'ACA',
+      };
+      const destination = uniqueCombinedMultilocationAcala;
+      await helper.xTokens.transfer(alice, id, testAmount, destination, 'Unlimited');
+
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
+
+  itSub('Unique rejects GLMR tokens from Moonbeam', async ({helper}) => {
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const id = 'SelfReserve';
+      const destination = uniqueCombinedMultilocation;
+      await helper.xTokens.transfer(alith, id, testAmount, destination, 'Unlimited');
+
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
+
+  itSub('Unique rejects ASTR tokens from Astar', async ({helper}) => {
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      const destinationParachain = uniqueParachainMultilocation;
+      const beneficiary = uniqueAccountMultilocation;
+      const assets = {
+        V2: [{
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: testAmount,
+          },
+        }],
+      };
+      const feeAssetItem = 0;
+
+      await helper.executeExtrinsic(alice, 'api.tx.polkadotXcm.reserveWithdrawAssets', [
+        destinationParachain,
+        beneficiary,
+        assets,
+        feeAssetItem,
+      ]);
+
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
+
+  itSub('Unique rejects PDX tokens from Polkadex', async ({helper}) => {
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      helper.arrange.createEmptyAccount().addressRaw,
+      {
+        Concrete: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: POLKADEX_CHAIN,
+            },
+          },
+        },
+      },
+      testAmount,
+    );
+
+    await usingPolkadexPlaygrounds(polkadexUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueParachainMultilocation, maliciousXcmProgramFullId);
+      messageSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await expectFailedToTransact(helper, messageSent);
+  });
+});
+
+describeXCM('[XCM] Integration test: Exchanging UNQ with Moonbeam', () => {
+  // Unique constants
+  let alice: IKeyringPair;
+  let uniqueAssetLocation;
+
+  let randomAccountUnique: IKeyringPair;
+  let randomAccountMoonbeam: IKeyringPair;
+
+  // Moonbeam constants
+  let assetId: string;
+
+  const uniqueAssetMetadata = {
+    name: 'xcUnique',
+    symbol: 'xcUNQ',
+    decimals: 18,
+    isFrozen: false,
+    minimalBalance: 1n,
+  };
+
+  let balanceUniqueTokenInit: bigint;
+  let balanceUniqueTokenMiddle: bigint;
+  let balanceUniqueTokenFinal: bigint;
+  let balanceForeignUnqTokenInit: bigint;
+  let balanceForeignUnqTokenMiddle: bigint;
+  let balanceForeignUnqTokenFinal: bigint;
+  let balanceGlmrTokenInit: bigint;
+  let balanceGlmrTokenMiddle: bigint;
+  let balanceGlmrTokenFinal: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccountUnique] = await helper.arrange.createAccounts([0n], alice);
+
+      balanceForeignUnqTokenInit = 0n;
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const alithAccount = helper.account.alithAccount();
+      const baltatharAccount = helper.account.baltatharAccount();
+      const dorothyAccount = helper.account.dorothyAccount();
+
+      randomAccountMoonbeam = helper.account.create();
+
+      // >>> Sponsoring Dorothy >>>
+      console.log('Sponsoring Dorothy.......');
+      await helper.balance.transferToEthereum(alithAccount, dorothyAccount.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring Dorothy.......DONE');
+      // <<< Sponsoring Dorothy <<<
+
+      uniqueAssetLocation = {
+        XCM: {
+          parents: 1,
+          interior: {X1: {Parachain: UNIQUE_CHAIN}},
+        },
+      };
+      const existentialDeposit = 1n;
+      const isSufficient = true;
+      const unitsPerSecond = 1n;
+      const numAssetsWeightHint = 0;
+
+      if((await helper.assetManager.assetTypeId(uniqueAssetLocation)).toJSON()) {
+        console.log('Unique asset is already registered on MoonBeam');
+      } else {
+        const encodedProposal = helper.assetManager.makeRegisterForeignAssetProposal({
+          location: uniqueAssetLocation,
+          metadata: uniqueAssetMetadata,
+          existentialDeposit,
+          isSufficient,
+          unitsPerSecond,
+          numAssetsWeightHint,
+        });
+
+        console.log('Encoded proposal for registerForeignAsset & setAssetUnitsPerSecond is %s', encodedProposal);
+
+        await helper.fastDemocracy.executeProposal('register UNQ foreign asset', encodedProposal);
+      }
+
+      // >>> Acquire Unique AssetId Info on Moonbeam >>>
+      console.log('Acquire Unique AssetId Info on Moonbeam.......');
+
+      assetId = (await helper.assetManager.assetTypeId(uniqueAssetLocation)).toString();
+      console.log('UNQ asset ID is %s', assetId);
+      console.log('Acquire Unique AssetId Info on Moonbeam.......DONE');
+      // >>> Acquire Unique AssetId Info on Moonbeam >>>
+
+      // >>> Sponsoring random Account >>>
+      console.log('Sponsoring random Account.......');
+      await helper.balance.transferToEthereum(baltatharAccount, randomAccountMoonbeam.address, 11_000_000_000_000_000_000n);
+      console.log('Sponsoring random Account.......DONE');
+      // <<< Sponsoring random Account <<<
+
+      balanceGlmrTokenInit = await helper.balance.getEthereum(randomAccountMoonbeam.address);
+    });
+
+    await usingPlaygrounds(async (helper) => {
+      await helper.balance.transferToSubstrate(alice, randomAccountUnique.address, 10n * TRANSFER_AMOUNT);
+      balanceUniqueTokenInit = await helper.balance.getSubstrate(randomAccountUnique.address);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Moonbeam', async ({helper}) => {
+    const currencyId = {
+      NativeAssetId: 'Here',
+    };
+    const dest = {
+      V2: {
+        parents: 1,
+        interior: {
+          X2: [
+            {Parachain: MOONBEAM_CHAIN},
+            {AccountKey20: {network: 'Any', key: randomAccountMoonbeam.address}},
+          ],
+        },
+      },
+    };
+    const amount = TRANSFER_AMOUNT;
+
+    await helper.xTokens.transfer(randomAccountUnique, currencyId, amount, dest, 'Unlimited');
+
+    balanceUniqueTokenMiddle = await helper.balance.getSubstrate(randomAccountUnique.address);
+    expect(balanceUniqueTokenMiddle < balanceUniqueTokenInit).to.be.true;
+
+    const transactionFees = balanceUniqueTokenInit - balanceUniqueTokenMiddle - TRANSFER_AMOUNT;
+    console.log('[Unique -> Moonbeam] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(transactionFees));
+    expect(transactionFees > 0, 'Negative fees UNQ, looks like nothing was transferred').to.be.true;
+
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+      balanceGlmrTokenMiddle = await helper.balance.getEthereum(randomAccountMoonbeam.address);
+
+      const glmrFees = balanceGlmrTokenInit - balanceGlmrTokenMiddle;
+      console.log('[Unique -> Moonbeam] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));
+      expect(glmrFees == 0n).to.be.true;
+
+      balanceForeignUnqTokenMiddle = (await helper.assets.account(assetId, randomAccountMoonbeam.address))!;
+
+      const unqIncomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenInit;
+      console.log('[Unique -> Moonbeam] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));
+      expect(unqIncomeTransfer == TRANSFER_AMOUNT).to.be.true;
+    });
+  });
+
+  itSub('Should connect to Moonbeam and send UNQ back', async ({helper}) => {
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const asset = {
+        V2: {
+          id: {
+            Concrete: {
+              parents: 1,
+              interior: {
+                X1: {Parachain: UNIQUE_CHAIN},
+              },
+            },
+          },
+          fun: {
+            Fungible: TRANSFER_AMOUNT,
+          },
+        },
+      };
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X2: [
+              {Parachain: UNIQUE_CHAIN},
+              {AccountId32: {network: 'Any', id: randomAccountUnique.addressRaw}},
+            ],
+          },
+        },
+      };
+
+      await helper.xTokens.transferMultiasset(randomAccountMoonbeam, asset, destination, 'Unlimited');
+
+      balanceGlmrTokenFinal = await helper.balance.getEthereum(randomAccountMoonbeam.address);
+
+      const glmrFees = balanceGlmrTokenMiddle - balanceGlmrTokenFinal;
+      console.log('[Moonbeam -> Unique] transaction fees on Moonbeam: %s GLMR', helper.util.bigIntToDecimals(glmrFees));
+      expect(glmrFees > 0, 'Negative fees GLMR, looks like nothing was transferred').to.be.true;
+
+      const unqRandomAccountAsset = await helper.assets.account(assetId, randomAccountMoonbeam.address);
+
+      expect(unqRandomAccountAsset).to.be.null;
+
+      balanceForeignUnqTokenFinal = 0n;
+
+      const unqOutcomeTransfer = balanceForeignUnqTokenMiddle - balanceForeignUnqTokenFinal;
+      console.log('[Unique -> Moonbeam] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));
+      expect(unqOutcomeTransfer == TRANSFER_AMOUNT).to.be.true;
+    });
+
+    await helper.wait.newBlocks(3);
+
+    balanceUniqueTokenFinal = await helper.balance.getSubstrate(randomAccountUnique.address);
+    const actuallyDelivered = balanceUniqueTokenFinal - balanceUniqueTokenMiddle;
+    expect(actuallyDelivered > 0).to.be.true;
+
+    console.log('[Moonbeam -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));
+
+    const unqFees = TRANSFER_AMOUNT - actuallyDelivered;
+    console.log('[Moonbeam -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
+    expect(unqFees == 0n).to.be.true;
+  });
+
+  itSub('Moonbeam can send only up to its balance', async ({helper}) => {
+    // set Moonbeam's sovereign account's balance
+    const moonbeamBalance = 10000n * (10n ** UNQ_DECIMALS);
+    const moonbeamSovereignAccount = helper.address.paraSiblingSovereignAccount(MOONBEAM_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, moonbeamSovereignAccount, moonbeamBalance);
+
+    const moreThanMoonbeamHas = moonbeamBalance * 2n;
+
+    let targetAccountBalance = 0n;
+    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanMoonbeamHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Unique
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgram]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('try to spend more UNQ than Moonbeam has', batchCall);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+
+    // But Moonbeam still can send the correct amount
+    const validTransferAmount = moonbeamBalance / 2n;
+    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      validTransferAmount,
+    );
+
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, validXcmProgram]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('Spend the correct amount of UNQ', batchCall);
+    });
+
+    await helper.wait.newBlocks(maxWaitBlocks);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(validTransferAmount);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Moonbeam', async ({helper}) => {
+    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
+    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      uniqueAssetId,
+      testAmount,
+    );
+
+    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      testAmount,
+    );
+
+    let maliciousXcmProgramFullIdSent: any;
+    let maliciousXcmProgramHereIdSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Unique using full UNQ identification
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramFullId]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using path asset identification', batchCall);
+
+      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+
+    // Try to trick Unique using shortened UNQ identification
+    await usingMoonbeamPlaygrounds(moonbeamUrl, async (helper) => {
+      const xcmSend = helper.constructApiCall('api.tx.polkadotXcm.send', [uniqueVersionedMultilocation, maliciousXcmProgramHereId]);
+
+      // Needed to bypass the call filter.
+      const batchCall = helper.encodeApiCall('api.tx.utility.batch', [[xcmSend]]);
+      await helper.fastDemocracy.executeProposal('try to act like a reserve location for UNQ using "here" asset identification', batchCall);
+
+      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+  });
+});
+
+describeXCM('[XCM] Integration test: Exchanging tokens with Astar', () => {
+  let alice: IKeyringPair;
+  let randomAccount: IKeyringPair;
+
+  const UNQ_ASSET_ID_ON_ASTAR = 18_446_744_073_709_551_631n; // The value is taken from the live Astar
+  const UNQ_MINIMAL_BALANCE_ON_ASTAR = 1n; // The value is taken from the live Astar
+
+  // Unique -> Astar
+  const astarInitialBalance = 1n * (10n ** ASTAR_DECIMALS); // 1 ASTR, existential deposit required to actually create the account on Astar.
+  const unitsPerSecond = 9_451_000_000_000_000_000n; // The value is taken from the live Astar
+  const unqToAstarTransferred = 10n * (10n ** UNQ_DECIMALS); // 10 UNQ
+  const unqToAstarArrived = 9_962_196_000_000_000_000n; // 9.962 ... UNQ, Astar takes a commision in foreign tokens
+
+  // Astar -> Unique
+  const unqFromAstarTransfered = 5n * (10n ** UNQ_DECIMALS); // 5 UNQ
+  const unqOnAstarLeft = unqToAstarArrived - unqFromAstarTransfered; // 4.962_219_600_000_000_000n UNQ
+
+  let balanceAfterUniqueToAstarXCM: bigint;
+
+  before(async () => {
+    await usingPlaygrounds(async (helper, privateKey) => {
+      alice = await privateKey('//Alice');
+      [randomAccount] = await helper.arrange.createAccounts([100n], alice);
+      console.log('randomAccount', randomAccount.address);
+
+      // Set the default version to wrap the first message to other chains.
+      await helper.getSudo().xcm.setSafeXcmVersion(alice, SAFE_XCM_VERSION);
+    });
+
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      if(!(await helper.callRpc('api.query.assets.asset', [UNQ_ASSET_ID_ON_ASTAR])).toJSON()) {
+        console.log('1. Create foreign asset and metadata');
+        await helper.getSudo().assets.forceCreate(
+          alice,
+          UNQ_ASSET_ID_ON_ASTAR,
+          alice.address,
+          UNQ_MINIMAL_BALANCE_ON_ASTAR,
+        );
+
+        await helper.assets.setMetadata(
+          alice,
+          UNQ_ASSET_ID_ON_ASTAR,
+          'Unique Network',
+          'UNQ',
+          Number(UNQ_DECIMALS),
+        );
+
+        console.log('2. Register asset location on Astar');
+        const assetLocation = {
+          V2: {
+            parents: 1,
+            interior: {
+              X1: {
+                Parachain: UNIQUE_CHAIN,
+              },
+            },
+          },
+        };
+
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.registerAssetLocation', [assetLocation, UNQ_ASSET_ID_ON_ASTAR]);
+
+        console.log('3. Set UNQ payment for XCM execution on Astar');
+        await helper.getSudo().executeExtrinsic(alice, 'api.tx.xcAssetConfig.setAssetUnitsPerSecond', [assetLocation, unitsPerSecond]);
+      } else {
+        console.log('UNQ is already registered on Astar');
+      }
+      console.log('4. Transfer 1 ASTR to recipient to create the account (needed due to existential balance)');
+      await helper.balance.transferToSubstrate(alice, randomAccount.address, astarInitialBalance);
+    });
+  });
+
+  itSub('Should connect and send UNQ to Astar', async ({helper}) => {
+    const destination = {
+      V2: {
+        parents: 1,
+        interior: {
+          X1: {
+            Parachain: ASTAR_CHAIN,
+          },
+        },
+      },
+    };
+
+    const beneficiary = {
+      V2: {
+        parents: 0,
+        interior: {
+          X1: {
+            AccountId32: {
+              network: 'Any',
+              id: randomAccount.addressRaw,
+            },
+          },
+        },
+      },
+    };
+
+    const assets = {
+      V2: [
+        {
+          id: {
+            Concrete: {
+              parents: 0,
+              interior: 'Here',
+            },
+          },
+          fun: {
+            Fungible: unqToAstarTransferred,
+          },
+        },
+      ],
+    };
+
+    // Initial balance is 100 UNQ
+    const balanceBefore = await helper.balance.getSubstrate(randomAccount.address);
+    console.log(`Initial balance is: ${balanceBefore}`);
+
+    const feeAssetItem = 0;
+    await helper.xcm.limitedReserveTransferAssets(randomAccount, destination, beneficiary, assets, feeAssetItem, 'Unlimited');
+
+    // Balance after reserve transfer is less than 90
+    balanceAfterUniqueToAstarXCM = await helper.balance.getSubstrate(randomAccount.address);
+    console.log(`UNQ Balance on Unique after XCM is: ${balanceAfterUniqueToAstarXCM}`);
+    console.log(`Unique's UNQ commission is: ${balanceBefore - balanceAfterUniqueToAstarXCM}`);
+    expect(balanceBefore - balanceAfterUniqueToAstarXCM > 0).to.be.true;
+
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      await helper.wait.newBlocks(3);
+      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);
+      const astarBalance = await helper.balance.getSubstrate(randomAccount.address);
+
+      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);
+      console.log(`Astar's UNQ commission is: ${unqToAstarTransferred - xcUNQbalance!}`);
+
+      expect(xcUNQbalance).to.eq(unqToAstarArrived);
+      // Astar balance does not changed
+      expect(astarBalance).to.eq(astarInitialBalance);
+    });
+  });
+
+  itSub('Should connect to Astar and send UNQ back', async ({helper}) => {
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      const destination = {
+        V2: {
+          parents: 1,
+          interior: {
+            X1: {
+              Parachain: UNIQUE_CHAIN,
+            },
+          },
+        },
+      };
+
+      const beneficiary = {
+        V2: {
+          parents: 0,
+          interior: {
+            X1: {
+              AccountId32: {
+                network: 'Any',
+                id: randomAccount.addressRaw,
+              },
+            },
+          },
+        },
+      };
+
+      const assets = {
+        V2: [
+          {
+            id: {
+              Concrete: {
+                parents: 1,
+                interior: {
+                  X1: {
+                    Parachain: UNIQUE_CHAIN,
+                  },
+                },
+              },
+            },
+            fun: {
+              Fungible: unqFromAstarTransfered,
+            },
+          },
+        ],
+      };
+
+      // Initial balance is 1 ASTR
+      const balanceASTRbefore = await helper.balance.getSubstrate(randomAccount.address);
+      console.log(`ASTR balance is: ${balanceASTRbefore}, it does not changed`);
+      expect(balanceASTRbefore).to.eq(astarInitialBalance);
+
+      const feeAssetItem = 0;
+      // this is non-standard polkadotXcm extension for Astar only. It calls InitiateReserveWithdraw
+      await helper.executeExtrinsic(randomAccount, 'api.tx.polkadotXcm.reserveWithdrawAssets', [destination, beneficiary, assets, feeAssetItem]);
+
+      const xcUNQbalance = await helper.assets.account(UNQ_ASSET_ID_ON_ASTAR, randomAccount.address);
+      const balanceAstar = await helper.balance.getSubstrate(randomAccount.address);
+      console.log(`xcUNQ balance on Astar after XCM is: ${xcUNQbalance}`);
+
+      // Assert: xcUNQ balance correctly decreased
+      expect(xcUNQbalance).to.eq(unqOnAstarLeft);
+      // Assert: ASTR balance is 0.996...
+      expect(balanceAstar / (10n ** (ASTAR_DECIMALS - 3n))).to.eq(996n);
+    });
+
+    await helper.wait.newBlocks(3);
+    const balanceUNQ = await helper.balance.getSubstrate(randomAccount.address);
+    console.log(`UNQ Balance on Unique after XCM is: ${balanceUNQ}`);
+    expect(balanceUNQ).to.eq(balanceAfterUniqueToAstarXCM + unqFromAstarTransfered);
+  });
+
+  itSub('Astar can send only up to its balance', async ({helper}) => {
+    // set Astar's sovereign account's balance
+    const astarBalance = 10000n * (10n ** UNQ_DECIMALS);
+    const astarSovereignAccount = helper.address.paraSiblingSovereignAccount(ASTAR_CHAIN);
+    await helper.getSudo().balance.setBalanceSubstrate(alice, astarSovereignAccount, astarBalance);
+
+    const moreThanAstarHas = astarBalance * 2n;
+
+    let targetAccountBalance = 0n;
+    const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
+
+    const maliciousXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      moreThanAstarHas,
+    );
+
+    let maliciousXcmProgramSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Unique
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgram);
+
+      maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
+        && event.outcome.isFailedToTransactAsset);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(0n);
+
+    // But Astar still can send the correct amount
+    const validTransferAmount = astarBalance / 2n;
+    const validXcmProgram = helper.arrange.makeXcmProgramWithdrawDeposit(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      validTransferAmount,
+    );
+
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, validXcmProgram);
+    });
+
+    await helper.wait.newBlocks(maxWaitBlocks);
+
+    targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(targetAccountBalance).to.be.equal(validTransferAmount);
+  });
+
+  itSub('Should not accept reserve transfer of UNQ from Astar', async ({helper}) => {
+    const testAmount = 10_000n * (10n ** UNQ_DECIMALS);
+    const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
+
+    const maliciousXcmProgramFullId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      uniqueAssetId,
+      testAmount,
+    );
+
+    const maliciousXcmProgramHereId = helper.arrange.makeXcmProgramReserveAssetDeposited(
+      targetAccount.addressRaw,
+      {
+        Concrete: {
+          parents: 0,
+          interior: 'Here',
+        },
+      },
+      testAmount,
+    );
+
+    let maliciousXcmProgramFullIdSent: any;
+    let maliciousXcmProgramHereIdSent: any;
+    const maxWaitBlocks = 3;
+
+    // Try to trick Unique using full UNQ identification
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramFullId);
+
+      maliciousXcmProgramFullIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+
+    // Try to trick Unique using shortened UNQ identification
+    await usingAstarPlaygrounds(astarUrl, async (helper) => {
+      await helper.getSudo().xcm.send(alice, uniqueVersionedMultilocation, maliciousXcmProgramHereId);
+
+      maliciousXcmProgramHereIdSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
+    });
+
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
+        && event.outcome.isUntrustedReserveLocation);
+
+    accountBalance = await helper.balance.getSubstrate(targetAccount.address);
+    expect(accountBalance).to.be.equal(0n);
+  });
+});
deletedjs-packages/tsconfig.base.jsondiffbeforeafterboth
--- a/js-packages/tsconfig.base.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-    /**
-     * There uses the strictest configs as the base
-     * https://github.com/tsconfig/bases/blob/f674fa6cbca17062ff02511b02872f8729a597ec/bases/strictest.json
-     */
-    "extends": "@tsconfig/strictest/tsconfig.json",
-    "compilerOptions": {
-      /**
-       * This needs to align with scripts/polkadot-dev-build-ts & dev-ts/loader
-       * (target here is specifically tied to the minimum supported version)
-       */
-      "module": "nodenext",
-      "moduleResolution": "nodenext",
-      "target": "es2022",
-  
-      /**
-       * Specific compilation configs for polkadot-js projects as it is used
-       * (we only compile *.d.ts via the tsc command-line)
-       */
-      "declaration": true,
-      "emitDeclarationOnly": true,
-      "jsx": "preserve",
-      "verbatimModuleSyntax": true,
-  
-      /**
-       * These appear in strictest, however we don't (yet) use them. For the most part it means
-       * that we actually do have a large number of these lurking (especially on index checks)
-       */
-      "checkJs": false,
-      "exactOptionalPropertyTypes": false,
-      "noPropertyAccessFromIndexSignature": false,
-      "noUncheckedIndexedAccess": false,
-    }
-  }
\ No newline at end of file
modifiedjs-packages/tsconfig.jsondiffbeforeafterboth
--- a/js-packages/tsconfig.json
+++ b/js-packages/tsconfig.json
@@ -1,11 +1,15 @@
 {
-	"references": [
-	  { "path": "./types/tsconfig.json" },
-	  { "path": "./playgrounds/tsconfig.json" },
-	  { "path": "./scripts/tsconfig.json" },
-	  { "path": "./tests/tsconfig.json" }
+	"exclude": [
+		"**/node_modules"
 	],
-	"files": [],
-	"exclude": ["**/node_modules"]
-  }
-  
\ No newline at end of file
+	"compilerOptions": {
+		"skipLibCheck": true,
+		"module": "ESNext",
+		"moduleResolution": "Bundler",
+		"target": "ES2022",
+		"declaration": true,
+		"checkJs": false,
+		"strict": true,
+		"resolveJsonModule": true,
+	},
+}
deletedjs-packages/tsconfig.packages.jsondiffbeforeafterboth
--- a/js-packages/tsconfig.packages.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-  "compilerOptions": {
-    "composite": true,
-    "skipLibCheck": true,
-
-    /**
-     * This needs to align with scripts/polkadot-dev-build-ts & dev-ts/loader
-     * (target here is specifically tied to the minimum supported version)
-     */
-     "module": "nodenext",
-     "moduleResolution": "nodenext",
-     "target": "ES2022",
- 
-     /**
-      * Specific compilation configs for polkadot-js projects as it is used
-      * (we only compile *.d.ts via the tsc command-line)
-      */
-     "declaration": true,
-     "emitDeclarationOnly": true,
-     "jsx": "preserve",
-     "verbatimModuleSyntax": false,
- 
-     /**
-      * These appear in strictest, however we don't (yet) use them. For the most part it means
-      * that we actually do have a large number of these lurking (especially on index checks)
-      */
-     "checkJs": false,
-     "exactOptionalPropertyTypes": false,
-     "noPropertyAccessFromIndexSignature": false,
-     "noUncheckedIndexedAccess": false,
-  },
-}
\ No newline at end of file
addedjs-packages/types/.gitignorediffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/.gitignore
@@ -0,0 +1 @@
+metadata.json
addedjs-packages/types/appPromotion/definitions.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/appPromotion/definitions.ts
@@ -0,0 +1,58 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+type RpcParam = {
+  name: string;
+  type: string;
+  isOptional?: true;
+};
+
+const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr';
+
+const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE});
+const atParam = {name: 'at', type: 'Hash', isOptional: true};
+
+const fun = (description: string, params: RpcParam[], type: string) => ({
+  description,
+  params: [...params, atParam],
+  type,
+});
+
+export default {
+  types: {},
+  rpc: {
+    totalStaked: fun(
+      'Returns the total amount of staked tokens',
+      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
+      'u128',
+    ),
+    totalStakedPerBlock: fun(
+      'Returns the total amount of staked tokens per block when staked',
+      [crossAccountParam('staker')],
+      'Vec<(u32, u128)>',
+    ),
+    pendingUnstake: fun(
+      'Returns the total amount of unstaked tokens',
+      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
+      'u128',
+    ),
+    pendingUnstakePerBlock: fun(
+      'Returns the total amount of unstaked tokens per block',
+      [crossAccountParam('staker')],
+      'Vec<(u32, u128)>',
+    ),
+  },
+};
addedjs-packages/types/appPromotion/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/appPromotion/index.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export * from './types.js';
addedjs-packages/types/appPromotion/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/appPromotion/types.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export type PHANTOM_APPPROMOTION = 'appPromotion';
addedjs-packages/types/augment-api-consts.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api-consts.ts
@@ -0,0 +1,552 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/api-base/types/consts';
+
+import type { ApiTypes, AugmentedConst } from '@polkadot/api-base/types';
+import type { Option, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
+import type { Codec, ITuple } from '@polkadot/types-codec/types';
+import type { H160, Perbill, Permill } from '@polkadot/types/interfaces/runtime';
+import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, PalletReferendaTrackInfo, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, UpDataStructsCollectionLimits } from '@polkadot/types/lookup';
+
+export type __AugmentedConst<ApiType extends ApiTypes> = AugmentedConst<ApiType>;
+
+declare module '@polkadot/api-base/types/consts' {
+  interface AugmentedConsts<ApiType extends ApiTypes> {
+    appPromotion: {
+      /**
+       * Freeze identifier used by the pallet
+       **/
+      freezeIdentifier: U8aFixed & AugmentedConst<ApiType>;
+      /**
+       * Rate of return for interval in blocks defined in `RecalculationInterval`.
+       **/
+      intervalIncome: Perbill & AugmentedConst<ApiType>;
+      /**
+       * Decimals for the `Currency`.
+       **/
+      nominal: u128 & AugmentedConst<ApiType>;
+      /**
+       * The app's pallet id, used for deriving its sovereign account address.
+       **/
+      palletId: FrameSupportPalletId & AugmentedConst<ApiType>;
+      /**
+       * In parachain blocks.
+       **/
+      pendingInterval: u32 & AugmentedConst<ApiType>;
+      /**
+       * In relay blocks.
+       **/
+      recalculationInterval: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    balances: {
+      /**
+       * The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO!
+       * 
+       * If you *really* need it to be zero, you can enable the feature `insecure_zero_ed` for
+       * this pallet. However, you do so at your own risk: this will open up a major DoS vector.
+       * In case you have multiple sources of provider references, you may also get unexpected
+       * behaviour if you set this to zero.
+       * 
+       * Bottom line: Do yourself a favour and make it at least one!
+       **/
+      existentialDeposit: u128 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of individual freeze locks that can exist on an account at any time.
+       **/
+      maxFreezes: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of holds that can exist on an account at any time.
+       **/
+      maxHolds: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of locks that should exist on an account.
+       * Not strictly enforced, but used for weight estimation.
+       **/
+      maxLocks: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of named reserves that can exist on an account.
+       **/
+      maxReserves: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    common: {
+      /**
+       * Maximum admins per collection.
+       **/
+      collectionAdminsLimit: u32 & AugmentedConst<ApiType>;
+      /**
+       * Set price to create a collection.
+       **/
+      collectionCreationPrice: u128 & AugmentedConst<ApiType>;
+      /**
+       * Address under which the CollectionHelper contract would be available.
+       **/
+      contractAddress: H160 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    configuration: {
+      appPromotionDailyRate: Perbill & AugmentedConst<ApiType>;
+      dayRelayBlocks: u32 & AugmentedConst<ApiType>;
+      defaultCollatorSelectionKickThreshold: u32 & AugmentedConst<ApiType>;
+      defaultCollatorSelectionLicenseBond: u128 & AugmentedConst<ApiType>;
+      defaultCollatorSelectionMaxCollators: u32 & AugmentedConst<ApiType>;
+      defaultMinGasPrice: u64 & AugmentedConst<ApiType>;
+      defaultWeightToFeeCoefficient: u64 & AugmentedConst<ApiType>;
+      maxXcmAllowedLocations: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    council: {
+      /**
+       * The maximum weight of a dispatch call that can be proposed and executed.
+       **/
+      maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    democracy: {
+      /**
+       * Period in blocks where an external proposal may not be re-submitted after being vetoed.
+       **/
+      cooloffPeriod: u32 & AugmentedConst<ApiType>;
+      /**
+       * The period between a proposal being approved and enacted.
+       * 
+       * It should generally be a little more than the unstake period to ensure that
+       * voting stakers have an opportunity to remove themselves from the system in the case
+       * where they are on the losing side of a vote.
+       **/
+      enactmentPeriod: u32 & AugmentedConst<ApiType>;
+      /**
+       * Minimum voting period allowed for a fast-track referendum.
+       **/
+      fastTrackVotingPeriod: u32 & AugmentedConst<ApiType>;
+      /**
+       * Indicator for whether an emergency origin is even allowed to happen. Some chains may
+       * want to set this permanently to `false`, others may want to condition it on things such
+       * as an upgrade having happened recently.
+       **/
+      instantAllowed: bool & AugmentedConst<ApiType>;
+      /**
+       * How often (in blocks) new public referenda are launched.
+       **/
+      launchPeriod: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of items which can be blacklisted.
+       **/
+      maxBlacklisted: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of deposits a public proposal may have at any time.
+       **/
+      maxDeposits: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of public proposals that can exist at any time.
+       **/
+      maxProposals: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of votes for an account.
+       * 
+       * Also used to compute weight, an overly big value can
+       * lead to extrinsic with very big weight: see `delegate` for instance.
+       **/
+      maxVotes: u32 & AugmentedConst<ApiType>;
+      /**
+       * The minimum amount to be used as a deposit for a public referendum proposal.
+       **/
+      minimumDeposit: u128 & AugmentedConst<ApiType>;
+      /**
+       * The minimum period of vote locking.
+       * 
+       * It should be no shorter than enactment period to ensure that in the case of an approval,
+       * those successful voters are locked into the consequences that their votes entail.
+       **/
+      voteLockingPeriod: u32 & AugmentedConst<ApiType>;
+      /**
+       * How often (in blocks) to check for new votes.
+       **/
+      votingPeriod: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    evmContractHelpers: {
+      /**
+       * Address, under which magic contract will be available
+       **/
+      contractAddress: H160 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    fellowshipReferenda: {
+      /**
+       * Quantization level for the referendum wakeup scheduler. A higher number will result in
+       * fewer storage reads/writes needed for smaller voters, but also result in delays to the
+       * automatic referendum status changes. Explicit servicing instructions are unaffected.
+       **/
+      alarmInterval: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maximum size of the referendum queue for a single track.
+       **/
+      maxQueued: u32 & AugmentedConst<ApiType>;
+      /**
+       * The minimum amount to be used as a deposit for a public referendum proposal.
+       **/
+      submissionDeposit: u128 & AugmentedConst<ApiType>;
+      /**
+       * Information concerning the different referendum tracks.
+       **/
+      tracks: Vec<ITuple<[u16, PalletReferendaTrackInfo]>> & AugmentedConst<ApiType>;
+      /**
+       * The number of blocks after submission that a referendum must begin being decided by.
+       * Once this passes, then anyone may cancel the referendum.
+       **/
+      undecidingTimeout: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    identity: {
+      /**
+       * The amount held on deposit for a registered identity
+       **/
+      basicDeposit: u128 & AugmentedConst<ApiType>;
+      /**
+       * The amount held on deposit per additional field for a registered identity.
+       **/
+      fieldDeposit: u128 & AugmentedConst<ApiType>;
+      /**
+       * Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O
+       * required to access an identity, but can be pretty high.
+       **/
+      maxAdditionalFields: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maxmimum number of registrars allowed in the system. Needed to bound the complexity
+       * of, e.g., updating judgements.
+       **/
+      maxRegistrars: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of sub-accounts allowed per identified account.
+       **/
+      maxSubAccounts: u32 & AugmentedConst<ApiType>;
+      /**
+       * The amount held on deposit for a registered subaccount. This should account for the fact
+       * that one storage item's value will increase by the size of an account ID, and there will
+       * be another trie item whose value is the size of an account ID plus 32 bytes.
+       **/
+      subAccountDeposit: u128 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    inflation: {
+      /**
+       * Number of blocks that pass between treasury balance updates due to inflation
+       **/
+      inflationBlockInterval: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    scheduler: {
+      /**
+       * The maximum weight that may be scheduled per block for any dispatchables.
+       **/
+      maximumWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of scheduled calls in the queue for a single block.
+       * 
+       * NOTE:
+       * + Dependent pallets' benchmarks might require a higher limit for the setting. Set a
+       * higher limit under `runtime-benchmarks` feature.
+       **/
+      maxScheduledPerBlock: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    stateTrieMigration: {
+      /**
+       * Maximal number of bytes that a key can have.
+       * 
+       * FRAME itself does not limit the key length.
+       * The concrete value must therefore depend on your storage usage.
+       * A [`frame_support::storage::StorageNMap`] for example can have an arbitrary number of
+       * keys which are then hashed and concatenated, resulting in arbitrarily long keys.
+       * 
+       * Use the *state migration RPC* to retrieve the length of the longest key in your
+       * storage: <https://github.com/paritytech/substrate/issues/11642>
+       * 
+       * The migration will halt with a `Halted` event if this value is too small.
+       * Since there is no real penalty from over-estimating, it is advised to use a large
+       * value. The default is 512 byte.
+       * 
+       * Some key lengths for reference:
+       * - [`frame_support::storage::StorageValue`]: 32 byte
+       * - [`frame_support::storage::StorageMap`]: 64 byte
+       * - [`frame_support::storage::StorageDoubleMap`]: 96 byte
+       * 
+       * For more info see
+       * <https://www.shawntabrizi.com/substrate/querying-substrate-storage-via-rpc/>
+       **/
+      maxKeyLen: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    system: {
+      /**
+       * Maximum number of block number to block hash mappings to keep (oldest pruned first).
+       **/
+      blockHashCount: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum length of a block (in bytes).
+       **/
+      blockLength: FrameSystemLimitsBlockLength & AugmentedConst<ApiType>;
+      /**
+       * Block & extrinsics weights: base values and limits.
+       **/
+      blockWeights: FrameSystemLimitsBlockWeights & AugmentedConst<ApiType>;
+      /**
+       * The weight of runtime database operations the runtime can invoke.
+       **/
+      dbWeight: SpWeightsRuntimeDbWeight & AugmentedConst<ApiType>;
+      /**
+       * The designated SS58 prefix of this chain.
+       * 
+       * This replaces the "ss58Format" property declared in the chain spec. Reason is
+       * that the runtime should know about the prefix in order to make use of it as
+       * an identifier of the chain.
+       **/
+      ss58Prefix: u16 & AugmentedConst<ApiType>;
+      /**
+       * Get the chain's current version.
+       **/
+      version: SpVersionRuntimeVersion & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    technicalCommittee: {
+      /**
+       * The maximum weight of a dispatch call that can be proposed and executed.
+       **/
+      maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    timestamp: {
+      /**
+       * The minimum period between blocks. Beware that this is different to the *expected*
+       * period that the block production apparatus provides. Your chosen consensus system will
+       * generally work with this to determine a sensible block time. e.g. For Aura, it will be
+       * double this period on default settings.
+       **/
+      minimumPeriod: u64 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    tokens: {
+      maxLocks: u32 & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of named reserves that can exist on an account.
+       **/
+      maxReserves: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    transactionPayment: {
+      /**
+       * A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their
+       * `priority`
+       * 
+       * This value is multipled by the `final_fee` to obtain a "virtual tip" that is later
+       * added to a tip component in regular `priority` calculations.
+       * It means that a `Normal` transaction can front-run a similarly-sized `Operational`
+       * extrinsic (with no tip), by including a tip value greater than the virtual tip.
+       * 
+       * ```rust,ignore
+       * // For `Normal`
+       * let priority = priority_calc(tip);
+       * 
+       * // For `Operational`
+       * let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier;
+       * let priority = priority_calc(tip + virtual_tip);
+       * ```
+       * 
+       * Note that since we use `final_fee` the multiplier applies also to the regular `tip`
+       * sent with the transaction. So, not only does the transaction get a priority bump based
+       * on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational`
+       * transactions.
+       **/
+      operationalFeeMultiplier: u8 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    treasury: {
+      /**
+       * Percentage of spare funds (if any) that are burnt per spend period.
+       **/
+      burn: Permill & AugmentedConst<ApiType>;
+      /**
+       * The maximum number of approvals that can wait in the spending queue.
+       * 
+       * NOTE: This parameter is also used within the Bounties Pallet extension if enabled.
+       **/
+      maxApprovals: u32 & AugmentedConst<ApiType>;
+      /**
+       * The treasury's pallet id, used for deriving its sovereign account ID.
+       **/
+      palletId: FrameSupportPalletId & AugmentedConst<ApiType>;
+      /**
+       * Fraction of a proposal's value that should be bonded in order to place the proposal.
+       * An accepted proposal gets these back. A rejected proposal does not.
+       **/
+      proposalBond: Permill & AugmentedConst<ApiType>;
+      /**
+       * Maximum amount of funds that should be placed in a deposit for making a proposal.
+       **/
+      proposalBondMaximum: Option<u128> & AugmentedConst<ApiType>;
+      /**
+       * Minimum amount of funds that should be placed in a deposit for making a proposal.
+       **/
+      proposalBondMinimum: u128 & AugmentedConst<ApiType>;
+      /**
+       * Period between successive spends.
+       **/
+      spendPeriod: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    unique: {
+      /**
+       * Maximum admins per collection.
+       **/
+      collectionAdminsLimit: u32 & AugmentedConst<ApiType>;
+      /**
+       * Default FT collection limit.
+       **/
+      ftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+      /**
+       * Maximal length of a collection description.
+       **/
+      maxCollectionDescriptionLength: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maximal length of a collection name.
+       **/
+      maxCollectionNameLength: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maximum size for all collection properties.
+       **/
+      maxCollectionPropertiesSize: u32 & AugmentedConst<ApiType>;
+      /**
+       * A maximum number of token properties.
+       **/
+      maxPropertiesPerItem: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maximal length of a property key.
+       **/
+      maxPropertyKeyLength: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maximal length of a property value.
+       **/
+      maxPropertyValueLength: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maximal length of a token prefix.
+       **/
+      maxTokenPrefixLength: u32 & AugmentedConst<ApiType>;
+      /**
+       * Maximum size of all token properties.
+       **/
+      maxTokenPropertiesSize: u32 & AugmentedConst<ApiType>;
+      /**
+       * A maximum number of levels of depth in the token nesting tree.
+       **/
+      nestingBudget: u32 & AugmentedConst<ApiType>;
+      /**
+       * Default NFT collection limit.
+       **/
+      nftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+      /**
+       * Default RFT collection limit.
+       **/
+      rftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    utility: {
+      /**
+       * The limit on the number of batched calls.
+       **/
+      batchedCallsLimit: u32 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    vesting: {
+      /**
+       * The minimum amount transferred to call `vested_transfer`.
+       **/
+      minVestedTransfer: u128 & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+    xTokens: {
+      /**
+       * Base XCM weight.
+       * 
+       * The actually weight for an XCM message is `T::BaseXcmWeight +
+       * T::Weigher::weight(&msg)`.
+       **/
+      baseXcmWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
+      /**
+       * Self chain location.
+       **/
+      selfLocation: StagingXcmV3MultiLocation & AugmentedConst<ApiType>;
+      /**
+       * Generic const
+       **/
+      [key: string]: Codec;
+    };
+  } // AugmentedConsts
+} // declare module
addedjs-packages/types/augment-api-errors.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api-errors.ts
@@ -0,0 +1,1521 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/api-base/types/errors';
+
+import type { ApiTypes, AugmentedError } from '@polkadot/api-base/types';
+
+export type __AugmentedError<ApiType extends ApiTypes> = AugmentedError<ApiType>;
+
+declare module '@polkadot/api-base/types/errors' {
+  interface AugmentedErrors<ApiType extends ApiTypes> {
+    appPromotion: {
+      /**
+       * Error due to action requiring admin to be set.
+       **/
+      AdminNotSet: AugmentedError<ApiType>;
+      /**
+       * Errors caused by incorrect state of a staker in context of the pallet.
+       **/
+      InconsistencyState: AugmentedError<ApiType>;
+      /**
+       * Errors caused by insufficient staked balance.
+       **/
+      InsufficientStakedBalance: AugmentedError<ApiType>;
+      /**
+       * No permission to perform an action.
+       **/
+      NoPermission: AugmentedError<ApiType>;
+      /**
+       * Insufficient funds to perform an action.
+       **/
+      NotSufficientFunds: AugmentedError<ApiType>;
+      /**
+       * Occurs when a pending unstake cannot be added in this block. PENDING_LIMIT_PER_BLOCK` limits exceeded.
+       **/
+      PendingForBlockOverflow: AugmentedError<ApiType>;
+      /**
+       * The error is due to the fact that the collection/contract must already be sponsored in order to perform the action.
+       **/
+      SponsorNotSet: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    balances: {
+      /**
+       * Beneficiary account must pre-exist.
+       **/
+      DeadAccount: AugmentedError<ApiType>;
+      /**
+       * Value too low to create account due to existential deposit.
+       **/
+      ExistentialDeposit: AugmentedError<ApiType>;
+      /**
+       * A vesting schedule already exists for this account.
+       **/
+      ExistingVestingSchedule: AugmentedError<ApiType>;
+      /**
+       * Transfer/payment would kill account.
+       **/
+      Expendability: AugmentedError<ApiType>;
+      /**
+       * Balance too low to send value.
+       **/
+      InsufficientBalance: AugmentedError<ApiType>;
+      /**
+       * Account liquidity restrictions prevent withdrawal.
+       **/
+      LiquidityRestrictions: AugmentedError<ApiType>;
+      /**
+       * Number of freezes exceed `MaxFreezes`.
+       **/
+      TooManyFreezes: AugmentedError<ApiType>;
+      /**
+       * Number of holds exceed `MaxHolds`.
+       **/
+      TooManyHolds: AugmentedError<ApiType>;
+      /**
+       * Number of named reserves exceed `MaxReserves`.
+       **/
+      TooManyReserves: AugmentedError<ApiType>;
+      /**
+       * Vesting balance too high to send value.
+       **/
+      VestingBalance: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    collatorSelection: {
+      /**
+       * User is already a candidate
+       **/
+      AlreadyCandidate: AugmentedError<ApiType>;
+      /**
+       * User already holds license to collate
+       **/
+      AlreadyHoldingLicense: AugmentedError<ApiType>;
+      /**
+       * User is already an Invulnerable
+       **/
+      AlreadyInvulnerable: AugmentedError<ApiType>;
+      /**
+       * Account has no associated validator ID
+       **/
+      NoAssociatedValidatorId: AugmentedError<ApiType>;
+      /**
+       * User does not hold a license to collate
+       **/
+      NoLicense: AugmentedError<ApiType>;
+      /**
+       * User is not a candidate
+       **/
+      NotCandidate: AugmentedError<ApiType>;
+      /**
+       * User is not an Invulnerable
+       **/
+      NotInvulnerable: AugmentedError<ApiType>;
+      /**
+       * Permission issue
+       **/
+      Permission: AugmentedError<ApiType>;
+      /**
+       * Too few invulnerables
+       **/
+      TooFewInvulnerables: AugmentedError<ApiType>;
+      /**
+       * Too many candidates
+       **/
+      TooManyCandidates: AugmentedError<ApiType>;
+      /**
+       * Too many invulnerables
+       **/
+      TooManyInvulnerables: AugmentedError<ApiType>;
+      /**
+       * Unknown error
+       **/
+      Unknown: AugmentedError<ApiType>;
+      /**
+       * Validator ID is not yet registered
+       **/
+      ValidatorNotRegistered: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    common: {
+      /**
+       * Account token limit exceeded per collection
+       **/
+      AccountTokenLimitExceeded: AugmentedError<ApiType>;
+      /**
+       * Only spending from eth mirror could be approved
+       **/
+      AddressIsNotEthMirror: AugmentedError<ApiType>;
+      /**
+       * Can't transfer tokens to ethereum zero address
+       **/
+      AddressIsZero: AugmentedError<ApiType>;
+      /**
+       * Address is not in allow list.
+       **/
+      AddressNotInAllowlist: AugmentedError<ApiType>;
+      /**
+       * Requested value is more than the approved
+       **/
+      ApprovedValueTooLow: AugmentedError<ApiType>;
+      /**
+       * Tried to approve more than owned
+       **/
+      CantApproveMoreThanOwned: AugmentedError<ApiType>;
+      /**
+       * Destroying only empty collections is allowed
+       **/
+      CantDestroyNotEmptyCollection: AugmentedError<ApiType>;
+      /**
+       * Exceeded max admin count
+       **/
+      CollectionAdminCountExceeded: AugmentedError<ApiType>;
+      /**
+       * Collection description can not be longer than 255 char.
+       **/
+      CollectionDescriptionLimitExceeded: AugmentedError<ApiType>;
+      /**
+       * Tried to store more data than allowed in collection field
+       **/
+      CollectionFieldSizeExceeded: AugmentedError<ApiType>;
+      /**
+       * Tried to access an external collection with an internal API
+       **/
+      CollectionIsExternal: AugmentedError<ApiType>;
+      /**
+       * Tried to access an internal collection with an external API
+       **/
+      CollectionIsInternal: AugmentedError<ApiType>;
+      /**
+       * Collection limit bounds per collection exceeded
+       **/
+      CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
+      /**
+       * Collection name can not be longer than 63 char.
+       **/
+      CollectionNameLimitExceeded: AugmentedError<ApiType>;
+      /**
+       * This collection does not exist.
+       **/
+      CollectionNotFound: AugmentedError<ApiType>;
+      /**
+       * Collection token limit exceeded
+       **/
+      CollectionTokenLimitExceeded: AugmentedError<ApiType>;
+      /**
+       * Token prefix can not be longer than 15 char.
+       **/
+      CollectionTokenPrefixLimitExceeded: AugmentedError<ApiType>;
+      /**
+       * This address is not set as sponsor, use setCollectionSponsor first.
+       **/
+      ConfirmSponsorshipFail: AugmentedError<ApiType>;
+      /**
+       * Empty property keys are forbidden
+       **/
+      EmptyPropertyKey: AugmentedError<ApiType>;
+      /**
+       * Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.
+       **/
+      FungibleItemsHaveNoId: AugmentedError<ApiType>;
+      /**
+       * Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed
+       **/
+      InvalidCharacterInPropertyKey: AugmentedError<ApiType>;
+      /**
+       * Metadata flag frozen
+       **/
+      MetadataFlagFrozen: AugmentedError<ApiType>;
+      /**
+       * Sender parameter and item owner must be equal.
+       **/
+      MustBeTokenOwner: AugmentedError<ApiType>;
+      /**
+       * No permission to perform action
+       **/
+      NoPermission: AugmentedError<ApiType>;
+      /**
+       * Tried to store more property data than allowed
+       **/
+      NoSpaceForProperty: AugmentedError<ApiType>;
+      /**
+       * Insufficient funds to perform an action
+       **/
+      NotSufficientFounds: AugmentedError<ApiType>;
+      /**
+       * Tried to enable permissions which are only permitted to be disabled
+       **/
+      OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
+      /**
+       * Property key is too long
+       **/
+      PropertyKeyIsTooLong: AugmentedError<ApiType>;
+      /**
+       * Tried to store more property keys than allowed
+       **/
+      PropertyLimitReached: AugmentedError<ApiType>;
+      /**
+       * Collection is not in mint mode.
+       **/
+      PublicMintingNotAllowed: AugmentedError<ApiType>;
+      /**
+       * Only tokens from specific collections may nest tokens under this one
+       **/
+      SourceCollectionIsNotAllowedToNest: AugmentedError<ApiType>;
+      /**
+       * Item does not exist
+       **/
+      TokenNotFound: AugmentedError<ApiType>;
+      /**
+       * Item is balance not enough
+       **/
+      TokenValueTooLow: AugmentedError<ApiType>;
+      /**
+       * Total collections bound exceeded.
+       **/
+      TotalCollectionsLimitExceeded: AugmentedError<ApiType>;
+      /**
+       * Collection settings not allowing items transferring
+       **/
+      TransferNotAllowed: AugmentedError<ApiType>;
+      /**
+       * The operation is not supported
+       **/
+      UnsupportedOperation: AugmentedError<ApiType>;
+      /**
+       * User does not satisfy the nesting rule
+       **/
+      UserIsNotAllowedToNest: AugmentedError<ApiType>;
+      /**
+       * The user is not an administrator.
+       **/
+      UserIsNotCollectionAdmin: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    configuration: {
+      InconsistentConfiguration: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    council: {
+      /**
+       * Members are already initialized!
+       **/
+      AlreadyInitialized: AugmentedError<ApiType>;
+      /**
+       * Duplicate proposals not allowed
+       **/
+      DuplicateProposal: AugmentedError<ApiType>;
+      /**
+       * Duplicate vote ignored
+       **/
+      DuplicateVote: AugmentedError<ApiType>;
+      /**
+       * Account is not a member
+       **/
+      NotMember: AugmentedError<ApiType>;
+      /**
+       * Prime account is not a member
+       **/
+      PrimeAccountNotMember: AugmentedError<ApiType>;
+      /**
+       * Proposal must exist
+       **/
+      ProposalMissing: AugmentedError<ApiType>;
+      /**
+       * The close call was made too early, before the end of the voting.
+       **/
+      TooEarly: AugmentedError<ApiType>;
+      /**
+       * There can only be a maximum of `MaxProposals` active proposals.
+       **/
+      TooManyProposals: AugmentedError<ApiType>;
+      /**
+       * Mismatched index
+       **/
+      WrongIndex: AugmentedError<ApiType>;
+      /**
+       * The given length bound for the proposal was too low.
+       **/
+      WrongProposalLength: AugmentedError<ApiType>;
+      /**
+       * The given weight bound for the proposal was too low.
+       **/
+      WrongProposalWeight: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    councilMembership: {
+      /**
+       * Already a member.
+       **/
+      AlreadyMember: AugmentedError<ApiType>;
+      /**
+       * Not a member.
+       **/
+      NotMember: AugmentedError<ApiType>;
+      /**
+       * Too many members.
+       **/
+      TooManyMembers: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    cumulusXcm: {
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    democracy: {
+      /**
+       * Cannot cancel the same proposal twice
+       **/
+      AlreadyCanceled: AugmentedError<ApiType>;
+      /**
+       * The account is already delegating.
+       **/
+      AlreadyDelegating: AugmentedError<ApiType>;
+      /**
+       * Identity may not veto a proposal twice
+       **/
+      AlreadyVetoed: AugmentedError<ApiType>;
+      /**
+       * Proposal already made
+       **/
+      DuplicateProposal: AugmentedError<ApiType>;
+      /**
+       * The instant referendum origin is currently disallowed.
+       **/
+      InstantNotAllowed: AugmentedError<ApiType>;
+      /**
+       * Too high a balance was provided that the account cannot afford.
+       **/
+      InsufficientFunds: AugmentedError<ApiType>;
+      /**
+       * Invalid hash
+       **/
+      InvalidHash: AugmentedError<ApiType>;
+      /**
+       * Maximum number of votes reached.
+       **/
+      MaxVotesReached: AugmentedError<ApiType>;
+      /**
+       * No proposals waiting
+       **/
+      NoneWaiting: AugmentedError<ApiType>;
+      /**
+       * Delegation to oneself makes no sense.
+       **/
+      Nonsense: AugmentedError<ApiType>;
+      /**
+       * The actor has no permission to conduct the action.
+       **/
+      NoPermission: AugmentedError<ApiType>;
+      /**
+       * No external proposal
+       **/
+      NoProposal: AugmentedError<ApiType>;
+      /**
+       * The account is not currently delegating.
+       **/
+      NotDelegating: AugmentedError<ApiType>;
+      /**
+       * Next external proposal not simple majority
+       **/
+      NotSimpleMajority: AugmentedError<ApiType>;
+      /**
+       * The given account did not vote on the referendum.
+       **/
+      NotVoter: AugmentedError<ApiType>;
+      /**
+       * The preimage does not exist.
+       **/
+      PreimageNotExist: AugmentedError<ApiType>;
+      /**
+       * Proposal still blacklisted
+       **/
+      ProposalBlacklisted: AugmentedError<ApiType>;
+      /**
+       * Proposal does not exist
+       **/
+      ProposalMissing: AugmentedError<ApiType>;
+      /**
+       * Vote given for invalid referendum
+       **/
+      ReferendumInvalid: AugmentedError<ApiType>;
+      /**
+       * Maximum number of items reached.
+       **/
+      TooMany: AugmentedError<ApiType>;
+      /**
+       * Value too low
+       **/
+      ValueLow: AugmentedError<ApiType>;
+      /**
+       * The account currently has votes attached to it and the operation cannot succeed until
+       * these are removed, either through `unvote` or `reap_vote`.
+       **/
+      VotesExist: AugmentedError<ApiType>;
+      /**
+       * Voting period too low
+       **/
+      VotingPeriodLow: AugmentedError<ApiType>;
+      /**
+       * Invalid upper bound.
+       **/
+      WrongUpperBound: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    dmpQueue: {
+      /**
+       * The amount of weight given is possibly not enough for executing the message.
+       **/
+      OverLimit: AugmentedError<ApiType>;
+      /**
+       * The message index given is unknown.
+       **/
+      Unknown: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    ethereum: {
+      /**
+       * Signature is invalid.
+       **/
+      InvalidSignature: AugmentedError<ApiType>;
+      /**
+       * Pre-log is present, therefore transact is not allowed.
+       **/
+      PreLogExists: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    evm: {
+      /**
+       * Not enough balance to perform action
+       **/
+      BalanceLow: AugmentedError<ApiType>;
+      /**
+       * Calculating total fee overflowed
+       **/
+      FeeOverflow: AugmentedError<ApiType>;
+      /**
+       * Gas limit is too high.
+       **/
+      GasLimitTooHigh: AugmentedError<ApiType>;
+      /**
+       * Gas limit is too low.
+       **/
+      GasLimitTooLow: AugmentedError<ApiType>;
+      /**
+       * Gas price is too low.
+       **/
+      GasPriceTooLow: AugmentedError<ApiType>;
+      /**
+       * Nonce is invalid
+       **/
+      InvalidNonce: AugmentedError<ApiType>;
+      /**
+       * Calculating total payment overflowed
+       **/
+      PaymentOverflow: AugmentedError<ApiType>;
+      /**
+       * EVM reentrancy
+       **/
+      Reentrancy: AugmentedError<ApiType>;
+      /**
+       * EIP-3607,
+       **/
+      TransactionMustComeFromEOA: AugmentedError<ApiType>;
+      /**
+       * Undefined error.
+       **/
+      Undefined: AugmentedError<ApiType>;
+      /**
+       * Withdraw fee failed
+       **/
+      WithdrawFailed: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    evmCoderSubstrate: {
+      OutOfFund: AugmentedError<ApiType>;
+      OutOfGas: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    evmContractHelpers: {
+      /**
+       * No pending sponsor for contract.
+       **/
+      NoPendingSponsor: AugmentedError<ApiType>;
+      /**
+       * This method is only executable by contract owner
+       **/
+      NoPermission: AugmentedError<ApiType>;
+      /**
+       * Number of methods that sponsored limit is defined for exceeds maximum.
+       **/
+      TooManyMethodsHaveSponsoredLimit: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    evmMigration: {
+      /**
+       * Migration of this account is not yet started, or already finished.
+       **/
+      AccountIsNotMigrating: AugmentedError<ApiType>;
+      /**
+       * Can only migrate to empty address.
+       **/
+      AccountNotEmpty: AugmentedError<ApiType>;
+      /**
+       * Failed to decode event bytes
+       **/
+      BadEvent: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    fellowshipCollective: {
+      /**
+       * Account is already a member.
+       **/
+      AlreadyMember: AugmentedError<ApiType>;
+      /**
+       * Unexpected error in state.
+       **/
+      Corruption: AugmentedError<ApiType>;
+      /**
+       * The information provided is incorrect.
+       **/
+      InvalidWitness: AugmentedError<ApiType>;
+      /**
+       * There are no further records to be removed.
+       **/
+      NoneRemaining: AugmentedError<ApiType>;
+      /**
+       * The origin is not sufficiently privileged to do the operation.
+       **/
+      NoPermission: AugmentedError<ApiType>;
+      /**
+       * Account is not a member.
+       **/
+      NotMember: AugmentedError<ApiType>;
+      /**
+       * The given poll index is unknown or has closed.
+       **/
+      NotPolling: AugmentedError<ApiType>;
+      /**
+       * The given poll is still ongoing.
+       **/
+      Ongoing: AugmentedError<ApiType>;
+      /**
+       * The member's rank is too low to vote.
+       **/
+      RankTooLow: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    fellowshipReferenda: {
+      /**
+       * The referendum index provided is invalid in this context.
+       **/
+      BadReferendum: AugmentedError<ApiType>;
+      /**
+       * The referendum status is invalid for this operation.
+       **/
+      BadStatus: AugmentedError<ApiType>;
+      /**
+       * The track identifier given was invalid.
+       **/
+      BadTrack: AugmentedError<ApiType>;
+      /**
+       * There are already a full complement of referenda in progress for this track.
+       **/
+      Full: AugmentedError<ApiType>;
+      /**
+       * Referendum's decision deposit is already paid.
+       **/
+      HasDeposit: AugmentedError<ApiType>;
+      /**
+       * The deposit cannot be refunded since none was made.
+       **/
+      NoDeposit: AugmentedError<ApiType>;
+      /**
+       * The deposit refunder is not the depositor.
+       **/
+      NoPermission: AugmentedError<ApiType>;
+      /**
+       * There was nothing to do in the advancement.
+       **/
+      NothingToDo: AugmentedError<ApiType>;
+      /**
+       * Referendum is not ongoing.
+       **/
+      NotOngoing: AugmentedError<ApiType>;
+      /**
+       * No track exists for the proposal origin.
+       **/
+      NoTrack: AugmentedError<ApiType>;
+      /**
+       * The preimage does not exist.
+       **/
+      PreimageNotExist: AugmentedError<ApiType>;
+      /**
+       * The queue of the track is empty.
+       **/
+      QueueEmpty: AugmentedError<ApiType>;
+      /**
+       * Any deposit cannot be refunded until after the decision is over.
+       **/
+      Unfinished: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    foreignAssets: {
+      /**
+       * AssetId exists
+       **/
+      AssetIdExisted: AugmentedError<ApiType>;
+      /**
+       * AssetId not exists
+       **/
+      AssetIdNotExists: AugmentedError<ApiType>;
+      /**
+       * The given location could not be used (e.g. because it cannot be expressed in the
+       * desired version of XCM).
+       **/
+      BadLocation: AugmentedError<ApiType>;
+      /**
+       * MultiLocation existed
+       **/
+      MultiLocationExisted: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    fungible: {
+      /**
+       * Fungible token does not support nesting.
+       **/
+      FungibleDisallowsNesting: AugmentedError<ApiType>;
+      /**
+       * Tried to set data for fungible item.
+       **/
+      FungibleItemsDontHaveData: AugmentedError<ApiType>;
+      /**
+       * Only a fungible collection could be possibly broken; any fungible token is valid.
+       **/
+      FungibleTokensAreAlwaysValid: AugmentedError<ApiType>;
+      /**
+       * Not Fungible item data used to mint in Fungible collection.
+       **/
+      NotFungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
+      /**
+       * Setting allowance for all is not allowed.
+       **/
+      SettingAllowanceForAllNotAllowed: AugmentedError<ApiType>;
+      /**
+       * Setting item properties is not allowed.
+       **/
+      SettingPropertiesNotAllowed: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    identity: {
+      /**
+       * Account ID is already named.
+       **/
+      AlreadyClaimed: AugmentedError<ApiType>;
+      /**
+       * Empty index.
+       **/
+      EmptyIndex: AugmentedError<ApiType>;
+      /**
+       * Fee is changed.
+       **/
+      FeeChanged: AugmentedError<ApiType>;
+      /**
+       * The index is invalid.
+       **/
+      InvalidIndex: AugmentedError<ApiType>;
+      /**
+       * Invalid judgement.
+       **/
+      InvalidJudgement: AugmentedError<ApiType>;
+      /**
+       * The target is invalid.
+       **/
+      InvalidTarget: AugmentedError<ApiType>;
+      /**
+       * The provided judgement was for a different identity.
+       **/
+      JudgementForDifferentIdentity: AugmentedError<ApiType>;
+      /**
+       * Judgement given.
+       **/
+      JudgementGiven: AugmentedError<ApiType>;
+      /**
+       * Error that occurs when there is an issue paying for judgement.
+       **/
+      JudgementPaymentFailed: AugmentedError<ApiType>;
+      /**
+       * No identity found.
+       **/
+      NoIdentity: AugmentedError<ApiType>;
+      /**
+       * Account isn't found.
+       **/
+      NotFound: AugmentedError<ApiType>;
+      /**
+       * Account isn't named.
+       **/
+      NotNamed: AugmentedError<ApiType>;
+      /**
+       * Sub-account isn't owned by sender.
+       **/
+      NotOwned: AugmentedError<ApiType>;
+      /**
+       * Sender is not a sub-account.
+       **/
+      NotSub: AugmentedError<ApiType>;
+      /**
+       * Sticky judgement.
+       **/
+      StickyJudgement: AugmentedError<ApiType>;
+      /**
+       * Too many additional fields.
+       **/
+      TooManyFields: AugmentedError<ApiType>;
+      /**
+       * Maximum amount of registrars reached. Cannot add any more.
+       **/
+      TooManyRegistrars: AugmentedError<ApiType>;
+      /**
+       * Too many subs-accounts.
+       **/
+      TooManySubAccounts: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    maintenance: {
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    nonfungible: {
+      /**
+       * Unable to burn NFT with children
+       **/
+      CantBurnNftWithChildren: AugmentedError<ApiType>;
+      /**
+       * Used amount > 1 with NFT
+       **/
+      NonfungibleItemsHaveNoAmount: AugmentedError<ApiType>;
+      /**
+       * Not Nonfungible item data used to mint in Nonfungible collection.
+       **/
+      NotNonfungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    parachainSystem: {
+      /**
+       * The inherent which supplies the host configuration did not run this block.
+       **/
+      HostConfigurationNotAvailable: AugmentedError<ApiType>;
+      /**
+       * No code upgrade has been authorized.
+       **/
+      NothingAuthorized: AugmentedError<ApiType>;
+      /**
+       * No validation function upgrade is currently scheduled.
+       **/
+      NotScheduled: AugmentedError<ApiType>;
+      /**
+       * Attempt to upgrade validation function while existing upgrade pending.
+       **/
+      OverlappingUpgrades: AugmentedError<ApiType>;
+      /**
+       * Polkadot currently prohibits this parachain from upgrading its validation function.
+       **/
+      ProhibitedByPolkadot: AugmentedError<ApiType>;
+      /**
+       * The supplied validation function has compiled into a blob larger than Polkadot is
+       * willing to run.
+       **/
+      TooBig: AugmentedError<ApiType>;
+      /**
+       * The given code upgrade has not been authorized.
+       **/
+      Unauthorized: AugmentedError<ApiType>;
+      /**
+       * The inherent which supplies the validation data did not run this block.
+       **/
+      ValidationDataNotAvailable: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    polkadotXcm: {
+      /**
+       * The given account is not an identifiable sovereign account for any location.
+       **/
+      AccountNotSovereign: AugmentedError<ApiType>;
+      /**
+       * The location is invalid since it already has a subscription from us.
+       **/
+      AlreadySubscribed: AugmentedError<ApiType>;
+      /**
+       * The given location could not be used (e.g. because it cannot be expressed in the
+       * desired version of XCM).
+       **/
+      BadLocation: AugmentedError<ApiType>;
+      /**
+       * The version of the `Versioned` value used is not able to be interpreted.
+       **/
+      BadVersion: AugmentedError<ApiType>;
+      /**
+       * Could not re-anchor the assets to declare the fees for the destination chain.
+       **/
+      CannotReanchor: AugmentedError<ApiType>;
+      /**
+       * The destination `MultiLocation` provided cannot be inverted.
+       **/
+      DestinationNotInvertible: AugmentedError<ApiType>;
+      /**
+       * The assets to be sent are empty.
+       **/
+      Empty: AugmentedError<ApiType>;
+      /**
+       * The operation required fees to be paid which the initiator could not meet.
+       **/
+      FeesNotMet: AugmentedError<ApiType>;
+      /**
+       * The message execution fails the filter.
+       **/
+      Filtered: AugmentedError<ApiType>;
+      /**
+       * The unlock operation cannot succeed because there are still consumers of the lock.
+       **/
+      InUse: AugmentedError<ApiType>;
+      /**
+       * Invalid asset for the operation.
+       **/
+      InvalidAsset: AugmentedError<ApiType>;
+      /**
+       * Origin is invalid for sending.
+       **/
+      InvalidOrigin: AugmentedError<ApiType>;
+      /**
+       * A remote lock with the corresponding data could not be found.
+       **/
+      LockNotFound: AugmentedError<ApiType>;
+      /**
+       * The owner does not own (all) of the asset that they wish to do the operation on.
+       **/
+      LowBalance: AugmentedError<ApiType>;
+      /**
+       * The referenced subscription could not be found.
+       **/
+      NoSubscription: AugmentedError<ApiType>;
+      /**
+       * There was some other issue (i.e. not to do with routing) in sending the message.
+       * Perhaps a lack of space for buffering the message.
+       **/
+      SendFailure: AugmentedError<ApiType>;
+      /**
+       * Too many assets have been attempted for transfer.
+       **/
+      TooManyAssets: AugmentedError<ApiType>;
+      /**
+       * The asset owner has too many locks on the asset.
+       **/
+      TooManyLocks: AugmentedError<ApiType>;
+      /**
+       * The desired destination was unreachable, generally because there is a no way of routing
+       * to it.
+       **/
+      Unreachable: AugmentedError<ApiType>;
+      /**
+       * The message's weight could not be determined.
+       **/
+      UnweighableMessage: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    preimage: {
+      /**
+       * Preimage has already been noted on-chain.
+       **/
+      AlreadyNoted: AugmentedError<ApiType>;
+      /**
+       * The user is not authorized to perform this action.
+       **/
+      NotAuthorized: AugmentedError<ApiType>;
+      /**
+       * The preimage cannot be removed since it has not yet been noted.
+       **/
+      NotNoted: AugmentedError<ApiType>;
+      /**
+       * The preimage request cannot be removed since no outstanding requests exist.
+       **/
+      NotRequested: AugmentedError<ApiType>;
+      /**
+       * A preimage may not be removed when there are outstanding requests.
+       **/
+      Requested: AugmentedError<ApiType>;
+      /**
+       * Preimage is too large to store on-chain.
+       **/
+      TooBig: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    refungible: {
+      /**
+       * Not Refungible item data used to mint in Refungible collection.
+       **/
+      NotRefungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
+      /**
+       * Refungible token can't nest other tokens.
+       **/
+      RefungibleDisallowsNesting: AugmentedError<ApiType>;
+      /**
+       * Refungible token can't be repartitioned by user who isn't owns all pieces.
+       **/
+      RepartitionWhileNotOwningAllPieces: AugmentedError<ApiType>;
+      /**
+       * Setting item properties is not allowed.
+       **/
+      SettingPropertiesNotAllowed: AugmentedError<ApiType>;
+      /**
+       * Maximum refungibility exceeded.
+       **/
+      WrongRefungiblePieces: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    scheduler: {
+      /**
+       * Failed to schedule a call
+       **/
+      FailedToSchedule: AugmentedError<ApiType>;
+      /**
+       * Attempt to use a non-named function on a named task.
+       **/
+      Named: AugmentedError<ApiType>;
+      /**
+       * Cannot find the scheduled call.
+       **/
+      NotFound: AugmentedError<ApiType>;
+      /**
+       * Reschedule failed because it does not change scheduled time.
+       **/
+      RescheduleNoChange: AugmentedError<ApiType>;
+      /**
+       * Given target block number is in the past.
+       **/
+      TargetBlockNumberInPast: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    session: {
+      /**
+       * Registered duplicate key.
+       **/
+      DuplicatedKey: AugmentedError<ApiType>;
+      /**
+       * Invalid ownership proof.
+       **/
+      InvalidProof: AugmentedError<ApiType>;
+      /**
+       * Key setting account is not live, so it's impossible to associate keys.
+       **/
+      NoAccount: AugmentedError<ApiType>;
+      /**
+       * No associated validator ID for account.
+       **/
+      NoAssociatedValidatorId: AugmentedError<ApiType>;
+      /**
+       * No keys are associated with this account.
+       **/
+      NoKeys: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    stateTrieMigration: {
+      /**
+       * Bad child root provided.
+       **/
+      BadChildRoot: AugmentedError<ApiType>;
+      /**
+       * Bad witness data provided.
+       **/
+      BadWitness: AugmentedError<ApiType>;
+      /**
+       * A key was longer than the configured maximum.
+       * 
+       * This means that the migration halted at the current [`Progress`] and
+       * can be resumed with a larger [`crate::Config::MaxKeyLen`] value.
+       * Retrying with the same [`crate::Config::MaxKeyLen`] value will not work.
+       * The value should only be increased to avoid a storage migration for the currently
+       * stored [`crate::Progress::LastKey`].
+       **/
+      KeyTooLong: AugmentedError<ApiType>;
+      /**
+       * Max signed limits not respected.
+       **/
+      MaxSignedLimits: AugmentedError<ApiType>;
+      /**
+       * submitter does not have enough funds.
+       **/
+      NotEnoughFunds: AugmentedError<ApiType>;
+      /**
+       * Signed migration is not allowed because the maximum limit is not set yet.
+       **/
+      SignedMigrationNotAllowed: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    structure: {
+      /**
+       * While nesting, reached the breadth limit of nesting, exceeding the provided budget.
+       **/
+      BreadthLimit: AugmentedError<ApiType>;
+      /**
+       * Tried to nest token under collection contract address, instead of token address
+       **/
+      CantNestTokenUnderCollection: AugmentedError<ApiType>;
+      /**
+       * While nesting, reached the depth limit of nesting, exceeding the provided budget.
+       **/
+      DepthLimit: AugmentedError<ApiType>;
+      /**
+       * While nesting, encountered an already checked account, detecting a loop.
+       **/
+      OuroborosDetected: AugmentedError<ApiType>;
+      /**
+       * Couldn't find the token owner that is itself a token.
+       **/
+      TokenNotFound: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    sudo: {
+      /**
+       * Sender must be the Sudo account
+       **/
+      RequireSudo: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    system: {
+      /**
+       * The origin filter prevent the call to be dispatched.
+       **/
+      CallFiltered: AugmentedError<ApiType>;
+      /**
+       * Failed to extract the runtime version from the new runtime.
+       * 
+       * Either calling `Core_version` or decoding `RuntimeVersion` failed.
+       **/
+      FailedToExtractRuntimeVersion: AugmentedError<ApiType>;
+      /**
+       * The name of specification does not match between the current runtime
+       * and the new runtime.
+       **/
+      InvalidSpecName: AugmentedError<ApiType>;
+      /**
+       * Suicide called when the account has non-default composite data.
+       **/
+      NonDefaultComposite: AugmentedError<ApiType>;
+      /**
+       * There is a non-zero reference count preventing the account from being purged.
+       **/
+      NonZeroRefCount: AugmentedError<ApiType>;
+      /**
+       * The specification version is not allowed to decrease between the current runtime
+       * and the new runtime.
+       **/
+      SpecVersionNeedsToIncrease: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    technicalCommittee: {
+      /**
+       * Members are already initialized!
+       **/
+      AlreadyInitialized: AugmentedError<ApiType>;
+      /**
+       * Duplicate proposals not allowed
+       **/
+      DuplicateProposal: AugmentedError<ApiType>;
+      /**
+       * Duplicate vote ignored
+       **/
+      DuplicateVote: AugmentedError<ApiType>;
+      /**
+       * Account is not a member
+       **/
+      NotMember: AugmentedError<ApiType>;
+      /**
+       * Prime account is not a member
+       **/
+      PrimeAccountNotMember: AugmentedError<ApiType>;
+      /**
+       * Proposal must exist
+       **/
+      ProposalMissing: AugmentedError<ApiType>;
+      /**
+       * The close call was made too early, before the end of the voting.
+       **/
+      TooEarly: AugmentedError<ApiType>;
+      /**
+       * There can only be a maximum of `MaxProposals` active proposals.
+       **/
+      TooManyProposals: AugmentedError<ApiType>;
+      /**
+       * Mismatched index
+       **/
+      WrongIndex: AugmentedError<ApiType>;
+      /**
+       * The given length bound for the proposal was too low.
+       **/
+      WrongProposalLength: AugmentedError<ApiType>;
+      /**
+       * The given weight bound for the proposal was too low.
+       **/
+      WrongProposalWeight: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    technicalCommitteeMembership: {
+      /**
+       * Already a member.
+       **/
+      AlreadyMember: AugmentedError<ApiType>;
+      /**
+       * Not a member.
+       **/
+      NotMember: AugmentedError<ApiType>;
+      /**
+       * Too many members.
+       **/
+      TooManyMembers: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    testUtils: {
+      TestPalletDisabled: AugmentedError<ApiType>;
+      TriggerRollback: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    tokens: {
+      /**
+       * Cannot convert Amount into Balance type
+       **/
+      AmountIntoBalanceFailed: AugmentedError<ApiType>;
+      /**
+       * The balance is too low
+       **/
+      BalanceTooLow: AugmentedError<ApiType>;
+      /**
+       * Beneficiary account must pre-exist
+       **/
+      DeadAccount: AugmentedError<ApiType>;
+      /**
+       * Value too low to create account due to existential deposit
+       **/
+      ExistentialDeposit: AugmentedError<ApiType>;
+      /**
+       * Transfer/payment would kill account
+       **/
+      KeepAlive: AugmentedError<ApiType>;
+      /**
+       * Failed because liquidity restrictions due to locking
+       **/
+      LiquidityRestrictions: AugmentedError<ApiType>;
+      /**
+       * Failed because the maximum locks was exceeded
+       **/
+      MaxLocksExceeded: AugmentedError<ApiType>;
+      TooManyReserves: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    treasury: {
+      /**
+       * The spend origin is valid but the amount it is allowed to spend is lower than the
+       * amount to be spent.
+       **/
+      InsufficientPermission: AugmentedError<ApiType>;
+      /**
+       * Proposer's balance is too low.
+       **/
+      InsufficientProposersBalance: AugmentedError<ApiType>;
+      /**
+       * No proposal or bounty at that index.
+       **/
+      InvalidIndex: AugmentedError<ApiType>;
+      /**
+       * Proposal has not been approved.
+       **/
+      ProposalNotApproved: AugmentedError<ApiType>;
+      /**
+       * Too many approvals in the queue.
+       **/
+      TooManyApprovals: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    unique: {
+      /**
+       * Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].
+       **/
+      CollectionDecimalPointLimitExceeded: AugmentedError<ApiType>;
+      /**
+       * Length of items properties must be greater than 0.
+       **/
+      EmptyArgument: AugmentedError<ApiType>;
+      /**
+       * Repertition is only supported by refungible collection.
+       **/
+      RepartitionCalledOnNonRefungibleCollection: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    utility: {
+      /**
+       * Too many calls batched.
+       **/
+      TooManyCalls: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    vesting: {
+      /**
+       * The vested transfer amount is too low
+       **/
+      AmountLow: AugmentedError<ApiType>;
+      /**
+       * Insufficient amount of balance to lock
+       **/
+      InsufficientBalanceToLock: AugmentedError<ApiType>;
+      /**
+       * Failed because the maximum vesting schedules was exceeded
+       **/
+      MaxVestingSchedulesExceeded: AugmentedError<ApiType>;
+      /**
+       * This account have too many vesting schedules
+       **/
+      TooManyVestingSchedules: AugmentedError<ApiType>;
+      /**
+       * Vesting period is zero
+       **/
+      ZeroVestingPeriod: AugmentedError<ApiType>;
+      /**
+       * Number of vests is zero
+       **/
+      ZeroVestingPeriodCount: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    xcmpQueue: {
+      /**
+       * Bad overweight index.
+       **/
+      BadOverweightIndex: AugmentedError<ApiType>;
+      /**
+       * Bad XCM data.
+       **/
+      BadXcm: AugmentedError<ApiType>;
+      /**
+       * Bad XCM origin.
+       **/
+      BadXcmOrigin: AugmentedError<ApiType>;
+      /**
+       * Failed to send XCM message.
+       **/
+      FailedToSend: AugmentedError<ApiType>;
+      /**
+       * Provided weight is possibly not enough to execute the message.
+       **/
+      WeightOverLimit: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+    xTokens: {
+      /**
+       * Asset has no reserve location.
+       **/
+      AssetHasNoReserve: AugmentedError<ApiType>;
+      /**
+       * The specified index does not exist in a MultiAssets struct.
+       **/
+      AssetIndexNonExistent: AugmentedError<ApiType>;
+      /**
+       * The version of the `Versioned` value used is not able to be
+       * interpreted.
+       **/
+      BadVersion: AugmentedError<ApiType>;
+      /**
+       * Could not re-anchor the assets to declare the fees for the
+       * destination chain.
+       **/
+      CannotReanchor: AugmentedError<ApiType>;
+      /**
+       * The destination `MultiLocation` provided cannot be inverted.
+       **/
+      DestinationNotInvertible: AugmentedError<ApiType>;
+      /**
+       * We tried sending distinct asset and fee but they have different
+       * reserve chains.
+       **/
+      DistinctReserveForAssetAndFee: AugmentedError<ApiType>;
+      /**
+       * Fee is not enough.
+       **/
+      FeeNotEnough: AugmentedError<ApiType>;
+      /**
+       * Could not get ancestry of asset reserve location.
+       **/
+      InvalidAncestry: AugmentedError<ApiType>;
+      /**
+       * The MultiAsset is invalid.
+       **/
+      InvalidAsset: AugmentedError<ApiType>;
+      /**
+       * Invalid transfer destination.
+       **/
+      InvalidDest: AugmentedError<ApiType>;
+      /**
+       * MinXcmFee not registered for certain reserve location
+       **/
+      MinXcmFeeNotDefined: AugmentedError<ApiType>;
+      /**
+       * Not cross-chain transfer.
+       **/
+      NotCrossChainTransfer: AugmentedError<ApiType>;
+      /**
+       * Currency is not cross-chain transferable.
+       **/
+      NotCrossChainTransferableCurrency: AugmentedError<ApiType>;
+      /**
+       * Not supported MultiLocation
+       **/
+      NotSupportedMultiLocation: AugmentedError<ApiType>;
+      /**
+       * The number of assets to be sent is over the maximum.
+       **/
+      TooManyAssetsBeingSent: AugmentedError<ApiType>;
+      /**
+       * The message's weight could not be determined.
+       **/
+      UnweighableMessage: AugmentedError<ApiType>;
+      /**
+       * XCM execution failed.
+       **/
+      XcmExecutionFailed: AugmentedError<ApiType>;
+      /**
+       * The transfering asset amount is zero.
+       **/
+      ZeroAmount: AugmentedError<ApiType>;
+      /**
+       * The fee is zero.
+       **/
+      ZeroFee: AugmentedError<ApiType>;
+      /**
+       * Generic error
+       **/
+      [key: string]: AugmentedError<ApiType>;
+    };
+  } // AugmentedErrors
+} // declare module
addedjs-packages/types/augment-api-events.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api-events.ts
@@ -0,0 +1,1294 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/api-base/types/events';
+
+import type { ApiTypes, AugmentedEvent } from '@polkadot/api-base/types';
+import type { Bytes, Null, Option, Result, U8aFixed, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
+import type { ITuple } from '@polkadot/types-codec/types';
+import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime';
+import type { EthereumLog, EvmCoreErrorExitReason, FrameSupportDispatchDispatchInfo, FrameSupportPreimagesBounded, FrameSupportTokensMiscBalanceStatus, OrmlVestingVestingSchedule, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletDemocracyVoteThreshold, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletStateTrieMigrationError, PalletStateTrieMigrationMigrationCompute, SpRuntimeDispatchError, SpWeightsWeightV2Weight, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetMultiAssets, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3Xcm, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation } from '@polkadot/types/lookup';
+
+export type __AugmentedEvent<ApiType extends ApiTypes> = AugmentedEvent<ApiType>;
+
+declare module '@polkadot/api-base/types/events' {
+  interface AugmentedEvents<ApiType extends ApiTypes> {
+    appPromotion: {
+      /**
+       * The admin was set
+       * 
+       * # Arguments
+       * * AccountId: account address of the admin
+       **/
+      SetAdmin: AugmentedEvent<ApiType, [AccountId32]>;
+      /**
+       * Staking was performed
+       * 
+       * # Arguments
+       * * AccountId: account of the staker
+       * * Balance : staking amount
+       **/
+      Stake: AugmentedEvent<ApiType, [AccountId32, u128]>;
+      /**
+       * Staking recalculation was performed
+       * 
+       * # Arguments
+       * * AccountId: account of the staker.
+       * * Balance : recalculation base
+       * * Balance : total income
+       **/
+      StakingRecalculation: AugmentedEvent<ApiType, [AccountId32, u128, u128]>;
+      /**
+       * Unstaking was performed
+       * 
+       * # Arguments
+       * * AccountId: account of the staker
+       * * Balance : unstaking amount
+       **/
+      Unstake: AugmentedEvent<ApiType, [AccountId32, u128]>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    balances: {
+      /**
+       * A balance was set by root.
+       **/
+      BalanceSet: AugmentedEvent<ApiType, [who: AccountId32, free: u128], { who: AccountId32, free: u128 }>;
+      /**
+       * Some amount was burned from an account.
+       **/
+      Burned: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Some amount was deposited (e.g. for transaction fees).
+       **/
+      Deposit: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * An account was removed whose balance was non-zero but below ExistentialDeposit,
+       * resulting in an outright loss.
+       **/
+      DustLost: AugmentedEvent<ApiType, [account: AccountId32, amount: u128], { account: AccountId32, amount: u128 }>;
+      /**
+       * An account was created with some free balance.
+       **/
+      Endowed: AugmentedEvent<ApiType, [account: AccountId32, freeBalance: u128], { account: AccountId32, freeBalance: u128 }>;
+      /**
+       * Some balance was frozen.
+       **/
+      Frozen: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Total issuance was increased by `amount`, creating a credit to be balanced.
+       **/
+      Issued: AugmentedEvent<ApiType, [amount: u128], { amount: u128 }>;
+      /**
+       * Some balance was locked.
+       **/
+      Locked: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Some amount was minted into an account.
+       **/
+      Minted: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Total issuance was decreased by `amount`, creating a debt to be balanced.
+       **/
+      Rescinded: AugmentedEvent<ApiType, [amount: u128], { amount: u128 }>;
+      /**
+       * Some balance was reserved (moved from free to reserved).
+       **/
+      Reserved: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Some balance was moved from the reserve of the first account to the second account.
+       * Final argument indicates the destination balance type.
+       **/
+      ReserveRepatriated: AugmentedEvent<ApiType, [from: AccountId32, to: AccountId32, amount: u128, destinationStatus: FrameSupportTokensMiscBalanceStatus], { from: AccountId32, to: AccountId32, amount: u128, destinationStatus: FrameSupportTokensMiscBalanceStatus }>;
+      /**
+       * Some amount was restored into an account.
+       **/
+      Restored: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Some amount was removed from the account (e.g. for misbehavior).
+       **/
+      Slashed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Some amount was suspended from an account (it can be restored later).
+       **/
+      Suspended: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Some balance was thawed.
+       **/
+      Thawed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Transfer succeeded.
+       **/
+      Transfer: AugmentedEvent<ApiType, [from: AccountId32, to: AccountId32, amount: u128], { from: AccountId32, to: AccountId32, amount: u128 }>;
+      /**
+       * Some balance was unlocked.
+       **/
+      Unlocked: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Some balance was unreserved (moved from reserved to free).
+       **/
+      Unreserved: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * An account was upgraded.
+       **/
+      Upgraded: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
+      /**
+       * Some amount was withdrawn from the account (e.g. for transaction fees).
+       **/
+      Withdraw: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    collatorSelection: {
+      CandidateAdded: AugmentedEvent<ApiType, [accountId: AccountId32], { accountId: AccountId32 }>;
+      CandidateRemoved: AugmentedEvent<ApiType, [accountId: AccountId32], { accountId: AccountId32 }>;
+      InvulnerableAdded: AugmentedEvent<ApiType, [invulnerable: AccountId32], { invulnerable: AccountId32 }>;
+      InvulnerableRemoved: AugmentedEvent<ApiType, [invulnerable: AccountId32], { invulnerable: AccountId32 }>;
+      LicenseObtained: AugmentedEvent<ApiType, [accountId: AccountId32, deposit: u128], { accountId: AccountId32, deposit: u128 }>;
+      LicenseReleased: AugmentedEvent<ApiType, [accountId: AccountId32, depositReturned: u128], { accountId: AccountId32, depositReturned: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    common: {
+      /**
+       * Address was added to the allow list.
+       **/
+      AllowListAddressAdded: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Address was removed from the allow list.
+       **/
+      AllowListAddressRemoved: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Amount pieces of token owned by `sender` was approved for `spender`.
+       **/
+      Approved: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+      /**
+       * A `sender` approves operations on all owned tokens for `spender`.
+       **/
+      ApprovedForAll: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
+      /**
+       * Collection admin was added.
+       **/
+      CollectionAdminAdded: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Collection admin was removed.
+       **/
+      CollectionAdminRemoved: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * New collection was created
+       **/
+      CollectionCreated: AugmentedEvent<ApiType, [u32, u8, AccountId32]>;
+      /**
+       * New collection was destroyed
+       **/
+      CollectionDestroyed: AugmentedEvent<ApiType, [u32]>;
+      /**
+       * Collection limits were set.
+       **/
+      CollectionLimitSet: AugmentedEvent<ApiType, [u32]>;
+      /**
+       * Collection owned was changed.
+       **/
+      CollectionOwnerChanged: AugmentedEvent<ApiType, [u32, AccountId32]>;
+      /**
+       * Collection permissions were set.
+       **/
+      CollectionPermissionSet: AugmentedEvent<ApiType, [u32]>;
+      /**
+       * The property has been deleted.
+       **/
+      CollectionPropertyDeleted: AugmentedEvent<ApiType, [u32, Bytes]>;
+      /**
+       * The colletion property has been added or edited.
+       **/
+      CollectionPropertySet: AugmentedEvent<ApiType, [u32, Bytes]>;
+      /**
+       * Collection sponsor was removed.
+       **/
+      CollectionSponsorRemoved: AugmentedEvent<ApiType, [u32]>;
+      /**
+       * Collection sponsor was set.
+       **/
+      CollectionSponsorSet: AugmentedEvent<ApiType, [u32, AccountId32]>;
+      /**
+       * New item was created.
+       **/
+      ItemCreated: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+      /**
+       * Collection item was burned.
+       **/
+      ItemDestroyed: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+      /**
+       * The token property permission of a collection has been set.
+       **/
+      PropertyPermissionSet: AugmentedEvent<ApiType, [u32, Bytes]>;
+      /**
+       * New sponsor was confirm.
+       **/
+      SponsorshipConfirmed: AugmentedEvent<ApiType, [u32, AccountId32]>;
+      /**
+       * The token property has been deleted.
+       **/
+      TokenPropertyDeleted: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
+      /**
+       * The token property has been added or edited.
+       **/
+      TokenPropertySet: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
+      /**
+       * Item was transferred
+       **/
+      Transfer: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    configuration: {
+      NewCollatorKickThreshold: AugmentedEvent<ApiType, [lengthInBlocks: Option<u32>], { lengthInBlocks: Option<u32> }>;
+      NewCollatorLicenseBond: AugmentedEvent<ApiType, [bondCost: Option<u128>], { bondCost: Option<u128> }>;
+      NewDesiredCollators: AugmentedEvent<ApiType, [desiredCollators: Option<u32>], { desiredCollators: Option<u32> }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    council: {
+      /**
+       * A motion was approved by the required threshold.
+       **/
+      Approved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
+      /**
+       * A proposal was closed because its threshold was reached or after its duration was up.
+       **/
+      Closed: AugmentedEvent<ApiType, [proposalHash: H256, yes: u32, no: u32], { proposalHash: H256, yes: u32, no: u32 }>;
+      /**
+       * A motion was not approved by the required threshold.
+       **/
+      Disapproved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
+      /**
+       * A motion was executed; result will be `Ok` if it returned without error.
+       **/
+      Executed: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * A single member did some action; result will be `Ok` if it returned without error.
+       **/
+      MemberExecuted: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * A motion (given hash) has been proposed (by given account) with a threshold (given
+       * `MemberCount`).
+       **/
+      Proposed: AugmentedEvent<ApiType, [account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32], { account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32 }>;
+      /**
+       * A motion (given hash) has been voted on by given account, leaving
+       * a tally (yes votes and no votes given respectively as `MemberCount`).
+       **/
+      Voted: AugmentedEvent<ApiType, [account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32], { account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    councilMembership: {
+      /**
+       * Phantom member, never used.
+       **/
+      Dummy: AugmentedEvent<ApiType, []>;
+      /**
+       * One of the members' keys changed.
+       **/
+      KeyChanged: AugmentedEvent<ApiType, []>;
+      /**
+       * The given member was added; see the transaction for who.
+       **/
+      MemberAdded: AugmentedEvent<ApiType, []>;
+      /**
+       * The given member was removed; see the transaction for who.
+       **/
+      MemberRemoved: AugmentedEvent<ApiType, []>;
+      /**
+       * The membership was reset; see the transaction for who the new set is.
+       **/
+      MembersReset: AugmentedEvent<ApiType, []>;
+      /**
+       * Two members were swapped; see the transaction for who.
+       **/
+      MembersSwapped: AugmentedEvent<ApiType, []>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    cumulusXcm: {
+      /**
+       * Downward message executed with the given outcome.
+       * \[ id, outcome \]
+       **/
+      ExecutedDownward: AugmentedEvent<ApiType, [U8aFixed, StagingXcmV3TraitsOutcome]>;
+      /**
+       * Downward message is invalid XCM.
+       * \[ id \]
+       **/
+      InvalidFormat: AugmentedEvent<ApiType, [U8aFixed]>;
+      /**
+       * Downward message is unsupported version of XCM.
+       * \[ id \]
+       **/
+      UnsupportedVersion: AugmentedEvent<ApiType, [U8aFixed]>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    democracy: {
+      /**
+       * A proposal_hash has been blacklisted permanently.
+       **/
+      Blacklisted: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
+      /**
+       * A referendum has been cancelled.
+       **/
+      Cancelled: AugmentedEvent<ApiType, [refIndex: u32], { refIndex: u32 }>;
+      /**
+       * An account has delegated their vote to another account.
+       **/
+      Delegated: AugmentedEvent<ApiType, [who: AccountId32, target: AccountId32], { who: AccountId32, target: AccountId32 }>;
+      /**
+       * An external proposal has been tabled.
+       **/
+      ExternalTabled: AugmentedEvent<ApiType, []>;
+      /**
+       * Metadata for a proposal or a referendum has been cleared.
+       **/
+      MetadataCleared: AugmentedEvent<ApiType, [owner: PalletDemocracyMetadataOwner, hash_: H256], { owner: PalletDemocracyMetadataOwner, hash_: H256 }>;
+      /**
+       * Metadata for a proposal or a referendum has been set.
+       **/
+      MetadataSet: AugmentedEvent<ApiType, [owner: PalletDemocracyMetadataOwner, hash_: H256], { owner: PalletDemocracyMetadataOwner, hash_: H256 }>;
+      /**
+       * Metadata has been transferred to new owner.
+       **/
+      MetadataTransferred: AugmentedEvent<ApiType, [prevOwner: PalletDemocracyMetadataOwner, owner: PalletDemocracyMetadataOwner, hash_: H256], { prevOwner: PalletDemocracyMetadataOwner, owner: PalletDemocracyMetadataOwner, hash_: H256 }>;
+      /**
+       * A proposal has been rejected by referendum.
+       **/
+      NotPassed: AugmentedEvent<ApiType, [refIndex: u32], { refIndex: u32 }>;
+      /**
+       * A proposal has been approved by referendum.
+       **/
+      Passed: AugmentedEvent<ApiType, [refIndex: u32], { refIndex: u32 }>;
+      /**
+       * A proposal got canceled.
+       **/
+      ProposalCanceled: AugmentedEvent<ApiType, [propIndex: u32], { propIndex: u32 }>;
+      /**
+       * A motion has been proposed by a public account.
+       **/
+      Proposed: AugmentedEvent<ApiType, [proposalIndex: u32, deposit: u128], { proposalIndex: u32, deposit: u128 }>;
+      /**
+       * An account has secconded a proposal
+       **/
+      Seconded: AugmentedEvent<ApiType, [seconder: AccountId32, propIndex: u32], { seconder: AccountId32, propIndex: u32 }>;
+      /**
+       * A referendum has begun.
+       **/
+      Started: AugmentedEvent<ApiType, [refIndex: u32, threshold: PalletDemocracyVoteThreshold], { refIndex: u32, threshold: PalletDemocracyVoteThreshold }>;
+      /**
+       * A public proposal has been tabled for referendum vote.
+       **/
+      Tabled: AugmentedEvent<ApiType, [proposalIndex: u32, deposit: u128], { proposalIndex: u32, deposit: u128 }>;
+      /**
+       * An account has cancelled a previous delegation operation.
+       **/
+      Undelegated: AugmentedEvent<ApiType, [account: AccountId32], { account: AccountId32 }>;
+      /**
+       * An external proposal has been vetoed.
+       **/
+      Vetoed: AugmentedEvent<ApiType, [who: AccountId32, proposalHash: H256, until: u32], { who: AccountId32, proposalHash: H256, until: u32 }>;
+      /**
+       * An account has voted in a referendum
+       **/
+      Voted: AugmentedEvent<ApiType, [voter: AccountId32, refIndex: u32, vote: PalletDemocracyVoteAccountVote], { voter: AccountId32, refIndex: u32, vote: PalletDemocracyVoteAccountVote }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    dmpQueue: {
+      /**
+       * Downward message executed with the given outcome.
+       **/
+      ExecutedDownward: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, outcome: StagingXcmV3TraitsOutcome], { messageHash: U8aFixed, messageId: U8aFixed, outcome: StagingXcmV3TraitsOutcome }>;
+      /**
+       * Downward message is invalid XCM.
+       **/
+      InvalidFormat: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
+      /**
+       * The maximum number of downward messages was reached.
+       **/
+      MaxMessagesExhausted: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
+      /**
+       * Downward message is overweight and was placed in the overweight queue.
+       **/
+      OverweightEnqueued: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, overweightIndex: u64, requiredWeight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, overweightIndex: u64, requiredWeight: SpWeightsWeightV2Weight }>;
+      /**
+       * Downward message from the overweight queue was executed.
+       **/
+      OverweightServiced: AugmentedEvent<ApiType, [overweightIndex: u64, weightUsed: SpWeightsWeightV2Weight], { overweightIndex: u64, weightUsed: SpWeightsWeightV2Weight }>;
+      /**
+       * Downward message is unsupported version of XCM.
+       **/
+      UnsupportedVersion: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
+      /**
+       * The weight limit for handling downward messages was reached.
+       **/
+      WeightExhausted: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, remainingWeight: SpWeightsWeightV2Weight, requiredWeight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, remainingWeight: SpWeightsWeightV2Weight, requiredWeight: SpWeightsWeightV2Weight }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    ethereum: {
+      /**
+       * An ethereum transaction was successfully executed.
+       **/
+      Executed: AugmentedEvent<ApiType, [from: H160, to: H160, transactionHash: H256, exitReason: EvmCoreErrorExitReason, extraData: Bytes], { from: H160, to: H160, transactionHash: H256, exitReason: EvmCoreErrorExitReason, extraData: Bytes }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    evm: {
+      /**
+       * A contract has been created at given address.
+       **/
+      Created: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
+      /**
+       * A contract was attempted to be created, but the execution failed.
+       **/
+      CreatedFailed: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
+      /**
+       * A contract has been executed successfully with states applied.
+       **/
+      Executed: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
+      /**
+       * A contract has been executed with errors. States are reverted with only gas fees applied.
+       **/
+      ExecutedFailed: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
+      /**
+       * Ethereum events from contracts.
+       **/
+      Log: AugmentedEvent<ApiType, [log: EthereumLog], { log: EthereumLog }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    evmContractHelpers: {
+      /**
+       * Collection sponsor was removed.
+       **/
+      ContractSponsorRemoved: AugmentedEvent<ApiType, [H160]>;
+      /**
+       * Contract sponsor was set.
+       **/
+      ContractSponsorSet: AugmentedEvent<ApiType, [H160, AccountId32]>;
+      /**
+       * New sponsor was confirm.
+       **/
+      ContractSponsorshipConfirmed: AugmentedEvent<ApiType, [H160, AccountId32]>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    evmMigration: {
+      /**
+       * This event is used in benchmarking and can be used for tests
+       **/
+      TestEvent: AugmentedEvent<ApiType, []>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    fellowshipCollective: {
+      /**
+       * A member `who` has been added.
+       **/
+      MemberAdded: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
+      /**
+       * The member `who` of given `rank` has been removed from the collective.
+       **/
+      MemberRemoved: AugmentedEvent<ApiType, [who: AccountId32, rank: u16], { who: AccountId32, rank: u16 }>;
+      /**
+       * The member `who`se rank has been changed to the given `rank`.
+       **/
+      RankChanged: AugmentedEvent<ApiType, [who: AccountId32, rank: u16], { who: AccountId32, rank: u16 }>;
+      /**
+       * The member `who` has voted for the `poll` with the given `vote` leading to an updated
+       * `tally`.
+       **/
+      Voted: AugmentedEvent<ApiType, [who: AccountId32, poll: u32, vote: PalletRankedCollectiveVoteRecord, tally: PalletRankedCollectiveTally], { who: AccountId32, poll: u32, vote: PalletRankedCollectiveVoteRecord, tally: PalletRankedCollectiveTally }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    fellowshipReferenda: {
+      /**
+       * A referendum has been approved and its proposal has been scheduled.
+       **/
+      Approved: AugmentedEvent<ApiType, [index: u32], { index: u32 }>;
+      /**
+       * A referendum has been cancelled.
+       **/
+      Cancelled: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
+      ConfirmAborted: AugmentedEvent<ApiType, [index: u32], { index: u32 }>;
+      /**
+       * A referendum has ended its confirmation phase and is ready for approval.
+       **/
+      Confirmed: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
+      ConfirmStarted: AugmentedEvent<ApiType, [index: u32], { index: u32 }>;
+      /**
+       * The decision deposit has been placed.
+       **/
+      DecisionDepositPlaced: AugmentedEvent<ApiType, [index: u32, who: AccountId32, amount: u128], { index: u32, who: AccountId32, amount: u128 }>;
+      /**
+       * The decision deposit has been refunded.
+       **/
+      DecisionDepositRefunded: AugmentedEvent<ApiType, [index: u32, who: AccountId32, amount: u128], { index: u32, who: AccountId32, amount: u128 }>;
+      /**
+       * A referendum has moved into the deciding phase.
+       **/
+      DecisionStarted: AugmentedEvent<ApiType, [index: u32, track: u16, proposal: FrameSupportPreimagesBounded, tally: PalletRankedCollectiveTally], { index: u32, track: u16, proposal: FrameSupportPreimagesBounded, tally: PalletRankedCollectiveTally }>;
+      /**
+       * A deposit has been slashaed.
+       **/
+      DepositSlashed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * A referendum has been killed.
+       **/
+      Killed: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
+      /**
+       * Metadata for a referendum has been cleared.
+       **/
+      MetadataCleared: AugmentedEvent<ApiType, [index: u32, hash_: H256], { index: u32, hash_: H256 }>;
+      /**
+       * Metadata for a referendum has been set.
+       **/
+      MetadataSet: AugmentedEvent<ApiType, [index: u32, hash_: H256], { index: u32, hash_: H256 }>;
+      /**
+       * A proposal has been rejected by referendum.
+       **/
+      Rejected: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
+      /**
+       * The submission deposit has been refunded.
+       **/
+      SubmissionDepositRefunded: AugmentedEvent<ApiType, [index: u32, who: AccountId32, amount: u128], { index: u32, who: AccountId32, amount: u128 }>;
+      /**
+       * A referendum has been submitted.
+       **/
+      Submitted: AugmentedEvent<ApiType, [index: u32, track: u16, proposal: FrameSupportPreimagesBounded], { index: u32, track: u16, proposal: FrameSupportPreimagesBounded }>;
+      /**
+       * A referendum has been timed out without being decided.
+       **/
+      TimedOut: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    foreignAssets: {
+      /**
+       * The asset registered.
+       **/
+      AssetRegistered: AugmentedEvent<ApiType, [assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata }>;
+      /**
+       * The asset updated.
+       **/
+      AssetUpdated: AugmentedEvent<ApiType, [assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata }>;
+      /**
+       * The foreign asset registered.
+       **/
+      ForeignAssetRegistered: AugmentedEvent<ApiType, [assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata }>;
+      /**
+       * The foreign asset updated.
+       **/
+      ForeignAssetUpdated: AugmentedEvent<ApiType, [assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    identity: {
+      /**
+       * A number of identities and associated info were forcibly inserted.
+       **/
+      IdentitiesInserted: AugmentedEvent<ApiType, [amount: u32], { amount: u32 }>;
+      /**
+       * A number of identities and all associated info were forcibly removed.
+       **/
+      IdentitiesRemoved: AugmentedEvent<ApiType, [amount: u32], { amount: u32 }>;
+      /**
+       * A name was cleared, and the given balance returned.
+       **/
+      IdentityCleared: AugmentedEvent<ApiType, [who: AccountId32, deposit: u128], { who: AccountId32, deposit: u128 }>;
+      /**
+       * A name was removed and the given balance slashed.
+       **/
+      IdentityKilled: AugmentedEvent<ApiType, [who: AccountId32, deposit: u128], { who: AccountId32, deposit: u128 }>;
+      /**
+       * A name was set or reset (which will remove all judgements).
+       **/
+      IdentitySet: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
+      /**
+       * A judgement was given by a registrar.
+       **/
+      JudgementGiven: AugmentedEvent<ApiType, [target: AccountId32, registrarIndex: u32], { target: AccountId32, registrarIndex: u32 }>;
+      /**
+       * A judgement was asked from a registrar.
+       **/
+      JudgementRequested: AugmentedEvent<ApiType, [who: AccountId32, registrarIndex: u32], { who: AccountId32, registrarIndex: u32 }>;
+      /**
+       * A judgement request was retracted.
+       **/
+      JudgementUnrequested: AugmentedEvent<ApiType, [who: AccountId32, registrarIndex: u32], { who: AccountId32, registrarIndex: u32 }>;
+      /**
+       * A registrar was added.
+       **/
+      RegistrarAdded: AugmentedEvent<ApiType, [registrarIndex: u32], { registrarIndex: u32 }>;
+      /**
+       * A number of identities were forcibly updated with new sub-identities.
+       **/
+      SubIdentitiesInserted: AugmentedEvent<ApiType, [amount: u32], { amount: u32 }>;
+      /**
+       * A sub-identity was added to an identity and the deposit paid.
+       **/
+      SubIdentityAdded: AugmentedEvent<ApiType, [sub: AccountId32, main: AccountId32, deposit: u128], { sub: AccountId32, main: AccountId32, deposit: u128 }>;
+      /**
+       * A sub-identity was removed from an identity and the deposit freed.
+       **/
+      SubIdentityRemoved: AugmentedEvent<ApiType, [sub: AccountId32, main: AccountId32, deposit: u128], { sub: AccountId32, main: AccountId32, deposit: u128 }>;
+      /**
+       * A sub-identity was cleared, and the given deposit repatriated from the
+       * main identity account to the sub-identity account.
+       **/
+      SubIdentityRevoked: AugmentedEvent<ApiType, [sub: AccountId32, main: AccountId32, deposit: u128], { sub: AccountId32, main: AccountId32, deposit: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    maintenance: {
+      MaintenanceDisabled: AugmentedEvent<ApiType, []>;
+      MaintenanceEnabled: AugmentedEvent<ApiType, []>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    parachainSystem: {
+      /**
+       * Downward messages were processed using the given weight.
+       **/
+      DownwardMessagesProcessed: AugmentedEvent<ApiType, [weightUsed: SpWeightsWeightV2Weight, dmqHead: H256], { weightUsed: SpWeightsWeightV2Weight, dmqHead: H256 }>;
+      /**
+       * Some downward messages have been received and will be processed.
+       **/
+      DownwardMessagesReceived: AugmentedEvent<ApiType, [count: u32], { count: u32 }>;
+      /**
+       * An upgrade has been authorized.
+       **/
+      UpgradeAuthorized: AugmentedEvent<ApiType, [codeHash: H256], { codeHash: H256 }>;
+      /**
+       * An upward message was sent to the relay chain.
+       **/
+      UpwardMessageSent: AugmentedEvent<ApiType, [messageHash: Option<U8aFixed>], { messageHash: Option<U8aFixed> }>;
+      /**
+       * The validation function was applied as of the contained relay chain block number.
+       **/
+      ValidationFunctionApplied: AugmentedEvent<ApiType, [relayChainBlockNum: u32], { relayChainBlockNum: u32 }>;
+      /**
+       * The relay-chain aborted the upgrade process.
+       **/
+      ValidationFunctionDiscarded: AugmentedEvent<ApiType, []>;
+      /**
+       * The validation function has been scheduled to apply.
+       **/
+      ValidationFunctionStored: AugmentedEvent<ApiType, []>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    polkadotXcm: {
+      /**
+       * Some assets have been claimed from an asset trap
+       **/
+      AssetsClaimed: AugmentedEvent<ApiType, [hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets], { hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets }>;
+      /**
+       * Some assets have been placed in an asset trap.
+       **/
+      AssetsTrapped: AugmentedEvent<ApiType, [hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets], { hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets }>;
+      /**
+       * Execution of an XCM message was attempted.
+       **/
+      Attempted: AugmentedEvent<ApiType, [outcome: StagingXcmV3TraitsOutcome], { outcome: StagingXcmV3TraitsOutcome }>;
+      /**
+       * Fees were paid from a location for an operation (often for using `SendXcm`).
+       **/
+      FeesPaid: AugmentedEvent<ApiType, [paying: StagingXcmV3MultiLocation, fees: StagingXcmV3MultiassetMultiAssets], { paying: StagingXcmV3MultiLocation, fees: StagingXcmV3MultiassetMultiAssets }>;
+      /**
+       * Expected query response has been received but the querier location of the response does
+       * not match the expected. The query remains registered for a later, valid, response to
+       * be received and acted upon.
+       **/
+      InvalidQuerier: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64, expectedQuerier: StagingXcmV3MultiLocation, maybeActualQuerier: Option<StagingXcmV3MultiLocation>], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedQuerier: StagingXcmV3MultiLocation, maybeActualQuerier: Option<StagingXcmV3MultiLocation> }>;
+      /**
+       * Expected query response has been received but the expected querier location placed in
+       * storage by this runtime previously cannot be decoded. The query remains registered.
+       * 
+       * This is unexpected (since a location placed in storage in a previously executing
+       * runtime should be readable prior to query timeout) and dangerous since the possibly
+       * valid response will be dropped. Manual governance intervention is probably going to be
+       * needed.
+       **/
+      InvalidQuerierVersion: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64], { origin: StagingXcmV3MultiLocation, queryId: u64 }>;
+      /**
+       * Expected query response has been received but the origin location of the response does
+       * not match that expected. The query remains registered for a later, valid, response to
+       * be received and acted upon.
+       **/
+      InvalidResponder: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64, expectedLocation: Option<StagingXcmV3MultiLocation>], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedLocation: Option<StagingXcmV3MultiLocation> }>;
+      /**
+       * Expected query response has been received but the expected origin location placed in
+       * storage by this runtime previously cannot be decoded. The query remains registered.
+       * 
+       * This is unexpected (since a location placed in storage in a previously executing
+       * runtime should be readable prior to query timeout) and dangerous since the possibly
+       * valid response will be dropped. Manual governance intervention is probably going to be
+       * needed.
+       **/
+      InvalidResponderVersion: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64], { origin: StagingXcmV3MultiLocation, queryId: u64 }>;
+      /**
+       * Query response has been received and query is removed. The registered notification has
+       * been dispatched and executed successfully.
+       **/
+      Notified: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8], { queryId: u64, palletIndex: u8, callIndex: u8 }>;
+      /**
+       * Query response has been received and query is removed. The dispatch was unable to be
+       * decoded into a `Call`; this might be due to dispatch function having a signature which
+       * is not `(origin, QueryId, Response)`.
+       **/
+      NotifyDecodeFailed: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8], { queryId: u64, palletIndex: u8, callIndex: u8 }>;
+      /**
+       * Query response has been received and query is removed. There was a general error with
+       * dispatching the notification call.
+       **/
+      NotifyDispatchError: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8], { queryId: u64, palletIndex: u8, callIndex: u8 }>;
+      /**
+       * Query response has been received and query is removed. The registered notification
+       * could not be dispatched because the dispatch weight is greater than the maximum weight
+       * originally budgeted by this runtime for the query result.
+       **/
+      NotifyOverweight: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8, actualWeight: SpWeightsWeightV2Weight, maxBudgetedWeight: SpWeightsWeightV2Weight], { queryId: u64, palletIndex: u8, callIndex: u8, actualWeight: SpWeightsWeightV2Weight, maxBudgetedWeight: SpWeightsWeightV2Weight }>;
+      /**
+       * A given location which had a version change subscription was dropped owing to an error
+       * migrating the location to our new XCM format.
+       **/
+      NotifyTargetMigrationFail: AugmentedEvent<ApiType, [location: StagingXcmVersionedMultiLocation, queryId: u64], { location: StagingXcmVersionedMultiLocation, queryId: u64 }>;
+      /**
+       * A given location which had a version change subscription was dropped owing to an error
+       * sending the notification to it.
+       **/
+      NotifyTargetSendFail: AugmentedEvent<ApiType, [location: StagingXcmV3MultiLocation, queryId: u64, error: StagingXcmV3TraitsError], { location: StagingXcmV3MultiLocation, queryId: u64, error: StagingXcmV3TraitsError }>;
+      /**
+       * Query response has been received and is ready for taking with `take_response`. There is
+       * no registered notification call.
+       **/
+      ResponseReady: AugmentedEvent<ApiType, [queryId: u64, response: StagingXcmV3Response], { queryId: u64, response: StagingXcmV3Response }>;
+      /**
+       * Received query response has been read and removed.
+       **/
+      ResponseTaken: AugmentedEvent<ApiType, [queryId: u64], { queryId: u64 }>;
+      /**
+       * A XCM message was sent.
+       **/
+      Sent: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, destination: StagingXcmV3MultiLocation, message: StagingXcmV3Xcm, messageId: U8aFixed], { origin: StagingXcmV3MultiLocation, destination: StagingXcmV3MultiLocation, message: StagingXcmV3Xcm, messageId: U8aFixed }>;
+      /**
+       * The supported version of a location has been changed. This might be through an
+       * automatic notification or a manual intervention.
+       **/
+      SupportedVersionChanged: AugmentedEvent<ApiType, [location: StagingXcmV3MultiLocation, version: u32], { location: StagingXcmV3MultiLocation, version: u32 }>;
+      /**
+       * Query response received which does not match a registered query. This may be because a
+       * matching query was never registered, it may be because it is a duplicate response, or
+       * because the query timed out.
+       **/
+      UnexpectedResponse: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64], { origin: StagingXcmV3MultiLocation, queryId: u64 }>;
+      /**
+       * An XCM version change notification message has been attempted to be sent.
+       * 
+       * The cost of sending it (borne by the chain) is included.
+       **/
+      VersionChangeNotified: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, result: u32, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, result: u32, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
+      /**
+       * We have requested that a remote chain send us XCM version change notifications.
+       **/
+      VersionNotifyRequested: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
+      /**
+       * A remote has requested XCM version change notification from us and we have honored it.
+       * A version information message is sent to them and its cost is included.
+       **/
+      VersionNotifyStarted: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
+      /**
+       * We have requested that a remote chain stops sending us XCM version change
+       * notifications.
+       **/
+      VersionNotifyUnrequested: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    preimage: {
+      /**
+       * A preimage has ben cleared.
+       **/
+      Cleared: AugmentedEvent<ApiType, [hash_: H256], { hash_: H256 }>;
+      /**
+       * A preimage has been noted.
+       **/
+      Noted: AugmentedEvent<ApiType, [hash_: H256], { hash_: H256 }>;
+      /**
+       * A preimage has been requested.
+       **/
+      Requested: AugmentedEvent<ApiType, [hash_: H256], { hash_: H256 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    scheduler: {
+      /**
+       * The call for the provided hash was not found so the task has been aborted.
+       **/
+      CallUnavailable: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed> }>;
+      /**
+       * Canceled some task.
+       **/
+      Canceled: AugmentedEvent<ApiType, [when: u32, index: u32], { when: u32, index: u32 }>;
+      /**
+       * Dispatched some task.
+       **/
+      Dispatched: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>, result: Result<Null, SpRuntimeDispatchError>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed>, result: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * The given task was unable to be renewed since the agenda is full at that block.
+       **/
+      PeriodicFailed: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed> }>;
+      /**
+       * The given task can never be executed since it is overweight.
+       **/
+      PermanentlyOverweight: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed> }>;
+      /**
+       * Scheduled some task.
+       **/
+      Scheduled: AugmentedEvent<ApiType, [when: u32, index: u32], { when: u32, index: u32 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    session: {
+      /**
+       * New session has happened. Note that the argument is the session index, not the
+       * block number as the type might suggest.
+       **/
+      NewSession: AugmentedEvent<ApiType, [sessionIndex: u32], { sessionIndex: u32 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    stateTrieMigration: {
+      /**
+       * The auto migration task finished.
+       **/
+      AutoMigrationFinished: AugmentedEvent<ApiType, []>;
+      /**
+       * Migration got halted due to an error or miss-configuration.
+       **/
+      Halted: AugmentedEvent<ApiType, [error: PalletStateTrieMigrationError], { error: PalletStateTrieMigrationError }>;
+      /**
+       * Given number of `(top, child)` keys were migrated respectively, with the given
+       * `compute`.
+       **/
+      Migrated: AugmentedEvent<ApiType, [top: u32, child: u32, compute: PalletStateTrieMigrationMigrationCompute], { top: u32, child: u32, compute: PalletStateTrieMigrationMigrationCompute }>;
+      /**
+       * Some account got slashed by the given amount.
+       **/
+      Slashed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    structure: {
+      /**
+       * Executed call on behalf of the token.
+       **/
+      Executed: AugmentedEvent<ApiType, [Result<Null, SpRuntimeDispatchError>]>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    sudo: {
+      /**
+       * The \[sudoer\] just switched identity; the old key is supplied if one existed.
+       **/
+      KeyChanged: AugmentedEvent<ApiType, [oldSudoer: Option<AccountId32>], { oldSudoer: Option<AccountId32> }>;
+      /**
+       * A sudo just took place. \[result\]
+       **/
+      Sudid: AugmentedEvent<ApiType, [sudoResult: Result<Null, SpRuntimeDispatchError>], { sudoResult: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * A sudo just took place. \[result\]
+       **/
+      SudoAsDone: AugmentedEvent<ApiType, [sudoResult: Result<Null, SpRuntimeDispatchError>], { sudoResult: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    system: {
+      /**
+       * `:code` was updated.
+       **/
+      CodeUpdated: AugmentedEvent<ApiType, []>;
+      /**
+       * An extrinsic failed.
+       **/
+      ExtrinsicFailed: AugmentedEvent<ApiType, [dispatchError: SpRuntimeDispatchError, dispatchInfo: FrameSupportDispatchDispatchInfo], { dispatchError: SpRuntimeDispatchError, dispatchInfo: FrameSupportDispatchDispatchInfo }>;
+      /**
+       * An extrinsic completed successfully.
+       **/
+      ExtrinsicSuccess: AugmentedEvent<ApiType, [dispatchInfo: FrameSupportDispatchDispatchInfo], { dispatchInfo: FrameSupportDispatchDispatchInfo }>;
+      /**
+       * An account was reaped.
+       **/
+      KilledAccount: AugmentedEvent<ApiType, [account: AccountId32], { account: AccountId32 }>;
+      /**
+       * A new account was created.
+       **/
+      NewAccount: AugmentedEvent<ApiType, [account: AccountId32], { account: AccountId32 }>;
+      /**
+       * On on-chain remark happened.
+       **/
+      Remarked: AugmentedEvent<ApiType, [sender: AccountId32, hash_: H256], { sender: AccountId32, hash_: H256 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    technicalCommittee: {
+      /**
+       * A motion was approved by the required threshold.
+       **/
+      Approved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
+      /**
+       * A proposal was closed because its threshold was reached or after its duration was up.
+       **/
+      Closed: AugmentedEvent<ApiType, [proposalHash: H256, yes: u32, no: u32], { proposalHash: H256, yes: u32, no: u32 }>;
+      /**
+       * A motion was not approved by the required threshold.
+       **/
+      Disapproved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
+      /**
+       * A motion was executed; result will be `Ok` if it returned without error.
+       **/
+      Executed: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * A single member did some action; result will be `Ok` if it returned without error.
+       **/
+      MemberExecuted: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * A motion (given hash) has been proposed (by given account) with a threshold (given
+       * `MemberCount`).
+       **/
+      Proposed: AugmentedEvent<ApiType, [account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32], { account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32 }>;
+      /**
+       * A motion (given hash) has been voted on by given account, leaving
+       * a tally (yes votes and no votes given respectively as `MemberCount`).
+       **/
+      Voted: AugmentedEvent<ApiType, [account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32], { account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    technicalCommitteeMembership: {
+      /**
+       * Phantom member, never used.
+       **/
+      Dummy: AugmentedEvent<ApiType, []>;
+      /**
+       * One of the members' keys changed.
+       **/
+      KeyChanged: AugmentedEvent<ApiType, []>;
+      /**
+       * The given member was added; see the transaction for who.
+       **/
+      MemberAdded: AugmentedEvent<ApiType, []>;
+      /**
+       * The given member was removed; see the transaction for who.
+       **/
+      MemberRemoved: AugmentedEvent<ApiType, []>;
+      /**
+       * The membership was reset; see the transaction for who the new set is.
+       **/
+      MembersReset: AugmentedEvent<ApiType, []>;
+      /**
+       * Two members were swapped; see the transaction for who.
+       **/
+      MembersSwapped: AugmentedEvent<ApiType, []>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    testUtils: {
+      BatchCompleted: AugmentedEvent<ApiType, []>;
+      ShouldRollback: AugmentedEvent<ApiType, []>;
+      ValueIsSet: AugmentedEvent<ApiType, []>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    tokens: {
+      /**
+       * A balance was set by root.
+       **/
+      BalanceSet: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, free: u128, reserved: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, free: u128, reserved: u128 }>;
+      /**
+       * Deposited some balance into an account
+       **/
+      Deposited: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      /**
+       * An account was removed whose balance was non-zero but below
+       * ExistentialDeposit, resulting in an outright loss.
+       **/
+      DustLost: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      /**
+       * An account was created with some free balance.
+       **/
+      Endowed: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      Issued: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, amount: u128], { currencyId: PalletForeignAssetsAssetId, amount: u128 }>;
+      /**
+       * Some free balance was locked.
+       **/
+      Locked: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      /**
+       * Some locked funds were unlocked
+       **/
+      LockRemoved: AugmentedEvent<ApiType, [lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32], { lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32 }>;
+      /**
+       * Some funds are locked
+       **/
+      LockSet: AugmentedEvent<ApiType, [lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      Rescinded: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, amount: u128], { currencyId: PalletForeignAssetsAssetId, amount: u128 }>;
+      /**
+       * Some balance was reserved (moved from free to reserved).
+       **/
+      Reserved: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      /**
+       * Some reserved balance was repatriated (moved from reserved to
+       * another account).
+       **/
+      ReserveRepatriated: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128, status: FrameSupportTokensMiscBalanceStatus], { currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128, status: FrameSupportTokensMiscBalanceStatus }>;
+      /**
+       * Some balances were slashed (e.g. due to mis-behavior)
+       **/
+      Slashed: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, freeAmount: u128, reservedAmount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, freeAmount: u128, reservedAmount: u128 }>;
+      /**
+       * The total issuance of an currency has been set
+       **/
+      TotalIssuanceSet: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, amount: u128], { currencyId: PalletForeignAssetsAssetId, amount: u128 }>;
+      /**
+       * Transfer succeeded.
+       **/
+      Transfer: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128 }>;
+      /**
+       * Some locked balance was freed.
+       **/
+      Unlocked: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      /**
+       * Some balance was unreserved (moved from reserved to free).
+       **/
+      Unreserved: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      /**
+       * Some balances were withdrawn (e.g. pay for transaction fee)
+       **/
+      Withdrawn: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    transactionPayment: {
+      /**
+       * A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,
+       * has been paid by `who`.
+       **/
+      TransactionFeePaid: AugmentedEvent<ApiType, [who: AccountId32, actualFee: u128, tip: u128], { who: AccountId32, actualFee: u128, tip: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    treasury: {
+      /**
+       * Some funds have been allocated.
+       **/
+      Awarded: AugmentedEvent<ApiType, [proposalIndex: u32, award: u128, account: AccountId32], { proposalIndex: u32, award: u128, account: AccountId32 }>;
+      /**
+       * Some of our funds have been burnt.
+       **/
+      Burnt: AugmentedEvent<ApiType, [burntFunds: u128], { burntFunds: u128 }>;
+      /**
+       * Some funds have been deposited.
+       **/
+      Deposit: AugmentedEvent<ApiType, [value: u128], { value: u128 }>;
+      /**
+       * New proposal.
+       **/
+      Proposed: AugmentedEvent<ApiType, [proposalIndex: u32], { proposalIndex: u32 }>;
+      /**
+       * A proposal was rejected; funds were slashed.
+       **/
+      Rejected: AugmentedEvent<ApiType, [proposalIndex: u32, slashed: u128], { proposalIndex: u32, slashed: u128 }>;
+      /**
+       * Spending has finished; this is the amount that rolls over until next spend.
+       **/
+      Rollover: AugmentedEvent<ApiType, [rolloverBalance: u128], { rolloverBalance: u128 }>;
+      /**
+       * A new spend proposal has been approved.
+       **/
+      SpendApproved: AugmentedEvent<ApiType, [proposalIndex: u32, amount: u128, beneficiary: AccountId32], { proposalIndex: u32, amount: u128, beneficiary: AccountId32 }>;
+      /**
+       * We have ended a spend period and will now allocate funds.
+       **/
+      Spending: AugmentedEvent<ApiType, [budgetRemaining: u128], { budgetRemaining: u128 }>;
+      /**
+       * The inactive funds of the pallet have been updated.
+       **/
+      UpdatedInactive: AugmentedEvent<ApiType, [reactivated: u128, deactivated: u128], { reactivated: u128, deactivated: u128 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    utility: {
+      /**
+       * Batch of dispatches completed fully with no error.
+       **/
+      BatchCompleted: AugmentedEvent<ApiType, []>;
+      /**
+       * Batch of dispatches completed but has errors.
+       **/
+      BatchCompletedWithErrors: AugmentedEvent<ApiType, []>;
+      /**
+       * Batch of dispatches did not complete fully. Index of first failing dispatch given, as
+       * well as the error.
+       **/
+      BatchInterrupted: AugmentedEvent<ApiType, [index: u32, error: SpRuntimeDispatchError], { index: u32, error: SpRuntimeDispatchError }>;
+      /**
+       * A call was dispatched.
+       **/
+      DispatchedAs: AugmentedEvent<ApiType, [result: Result<Null, SpRuntimeDispatchError>], { result: Result<Null, SpRuntimeDispatchError> }>;
+      /**
+       * A single item within a Batch of dispatches has completed with no error.
+       **/
+      ItemCompleted: AugmentedEvent<ApiType, []>;
+      /**
+       * A single item within a Batch of dispatches has completed with error.
+       **/
+      ItemFailed: AugmentedEvent<ApiType, [error: SpRuntimeDispatchError], { error: SpRuntimeDispatchError }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    vesting: {
+      /**
+       * Claimed vesting.
+       **/
+      Claimed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
+      /**
+       * Added new vesting schedule.
+       **/
+      VestingScheduleAdded: AugmentedEvent<ApiType, [from: AccountId32, to: AccountId32, vestingSchedule: OrmlVestingVestingSchedule], { from: AccountId32, to: AccountId32, vestingSchedule: OrmlVestingVestingSchedule }>;
+      /**
+       * Updated vesting schedules.
+       **/
+      VestingSchedulesUpdated: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    xcmpQueue: {
+      /**
+       * Bad XCM format used.
+       **/
+      BadFormat: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
+      /**
+       * Bad XCM version used.
+       **/
+      BadVersion: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
+      /**
+       * Some XCM failed.
+       **/
+      Fail: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, error: StagingXcmV3TraitsError, weight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, error: StagingXcmV3TraitsError, weight: SpWeightsWeightV2Weight }>;
+      /**
+       * An XCM exceeded the individual message weight budget.
+       **/
+      OverweightEnqueued: AugmentedEvent<ApiType, [sender: u32, sentAt: u32, index: u64, required: SpWeightsWeightV2Weight], { sender: u32, sentAt: u32, index: u64, required: SpWeightsWeightV2Weight }>;
+      /**
+       * An XCM from the overweight queue was executed with the given actual weight used.
+       **/
+      OverweightServiced: AugmentedEvent<ApiType, [index: u64, used: SpWeightsWeightV2Weight], { index: u64, used: SpWeightsWeightV2Weight }>;
+      /**
+       * Some XCM was executed ok.
+       **/
+      Success: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, weight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, weight: SpWeightsWeightV2Weight }>;
+      /**
+       * An HRMP message was sent to a sibling parachain.
+       **/
+      XcmpMessageSent: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+    xTokens: {
+      /**
+       * Transferred `MultiAsset` with fee.
+       **/
+      TransferredMultiAssets: AugmentedEvent<ApiType, [sender: AccountId32, assets: StagingXcmV3MultiassetMultiAssets, fee: StagingXcmV3MultiAsset, dest: StagingXcmV3MultiLocation], { sender: AccountId32, assets: StagingXcmV3MultiassetMultiAssets, fee: StagingXcmV3MultiAsset, dest: StagingXcmV3MultiLocation }>;
+      /**
+       * Generic event
+       **/
+      [key: string]: AugmentedEvent<ApiType>;
+    };
+  } // AugmentedEvents
+} // declare module
addedjs-packages/types/augment-api-query.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api-query.ts
@@ -0,0 +1,1442 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/api-base/types/storage';
+
+import type { ApiTypes, AugmentedQuery, QueryableStorageEntry } from '@polkadot/api-base/types';
+import type { Data } from '@polkadot/types';
+import type { BTreeMap, Bytes, Option, Struct, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
+import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
+import type { AccountId32, Call, H160, H256 } from '@polkadot/types/interfaces/runtime';
+import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportDispatchPerDispatchClassWeight, FrameSupportPreimagesBounded, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensReserveData, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesIdAmount, PalletBalancesReserveData, PalletCollectiveVotes, PalletConfigurationAppPromotionConfiguration, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCodeMetadata, PalletEvmContractHelpersSponsoringModeT, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletNonfungibleItemData, PalletPreimageRequestStatus, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveVoteRecord, PalletReferendaReferendumInfo, PalletSchedulerScheduled, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmVersionedAssetId, StagingXcmVersionedMultiLocation, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild } from '@polkadot/types/lookup';
+import type { Observable } from '@polkadot/types/types';
+
+export type __AugmentedQuery<ApiType extends ApiTypes> = AugmentedQuery<ApiType, () => unknown>;
+export type __QueryableStorageEntry<ApiType extends ApiTypes> = QueryableStorageEntry<ApiType>;
+
+declare module '@polkadot/api-base/types/storage' {
+  interface AugmentedQueries<ApiType extends ApiTypes> {
+    appPromotion: {
+      /**
+       * Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`.
+       **/
+      admin: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Pending unstake records for an `Account`.
+       * 
+       * * **Key** - Staker account.
+       * * **Value** - Amount of stakes.
+       **/
+      pendingUnstake: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<ITuple<[AccountId32, u128]>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Stores a key for record for which the revenue recalculation was performed.
+       * If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.
+       **/
+      previousCalculatedRecord: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[AccountId32, u32]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Stores the amount of tokens staked by account in the blocknumber.
+       * 
+       * * **Key1** - Staker account.
+       * * **Key2** - Relay block number when the stake was made.
+       * * **(Balance, BlockNumber)** - Balance of the stake.
+       * The number of the relay block in which we must perform the interest recalculation
+       **/
+      staked: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<ITuple<[u128, u32]>>, [AccountId32, u32]> & QueryableStorageEntry<ApiType, [AccountId32, u32]>;
+      /**
+       * Stores number of stake records for an `Account`.
+       * 
+       * * **Key** - Staker account.
+       * * **Value** - Amount of stakes.
+       **/
+      stakesPerAccount: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u8>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Stores the total staked amount.
+       **/
+      totalStaked: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    aura: {
+      /**
+       * The current authority set.
+       **/
+      authorities: AugmentedQuery<ApiType, () => Observable<Vec<SpConsensusAuraSr25519AppSr25519Public>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current slot of this block.
+       * 
+       * This will be set in `on_initialize`.
+       **/
+      currentSlot: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    auraExt: {
+      /**
+       * Serves as cache for the authorities.
+       * 
+       * The authorities in AuRa are overwritten in `on_initialize` when we switch to a new session,
+       * but we require the old authorities to verify the seal when validating a PoV. This will
+       * always be updated to the latest AuRa authorities in `on_finalize`.
+       **/
+      authorities: AugmentedQuery<ApiType, () => Observable<Vec<SpConsensusAuraSr25519AppSr25519Public>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Current slot paired with a number of authored blocks.
+       * 
+       * Updated on each block initialization.
+       **/
+      slotInfo: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[u64, u32]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    authorship: {
+      /**
+       * Author of current block.
+       **/
+      author: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    balances: {
+      /**
+       * The Balances pallet example of storing the balance of an account.
+       * 
+       * # Example
+       * 
+       * ```nocompile
+       * impl pallet_balances::Config for Runtime {
+       * type AccountStore = StorageMapShim<Self::Account<Runtime>, frame_system::Provider<Runtime>, AccountId, Self::AccountData<Balance>>
+       * }
+       * ```
+       * 
+       * You can also store the balance of an account in the `System` pallet.
+       * 
+       * # Example
+       * 
+       * ```nocompile
+       * impl pallet_balances::Config for Runtime {
+       * type AccountStore = System
+       * }
+       * ```
+       * 
+       * But this comes with tradeoffs, storing account balances in the system pallet stores
+       * `frame_system` data alongside the account data contrary to storing account balances in the
+       * `Balances` pallet, which uses a `StorageMap` to store balances data only.
+       * NOTE: This is only used in the case that this pallet is used to store balances.
+       **/
+      account: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<PalletBalancesAccountData>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Freeze locks on account balances.
+       **/
+      freezes: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<PalletBalancesIdAmount>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Holds on account balances.
+       **/
+      holds: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<{
+    readonly id: OpalRuntimeRuntimeHoldReason;
+    readonly amount: u128;
+  } & Struct>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * The total units of outstanding deactivated balance in the system.
+       **/
+      inactiveIssuance: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Any liquidity locks on some account balances.
+       * NOTE: Should only be accessed when setting, changing and freeing a lock.
+       **/
+      locks: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<PalletBalancesBalanceLock>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Named reserves on some account balances.
+       **/
+      reserves: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<PalletBalancesReserveData>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * The total units issued in the system.
+       **/
+      totalIssuance: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    collatorSelection: {
+      /**
+       * The (community, limited) collation candidates.
+       **/
+      candidates: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The invulnerable, fixed collators.
+       **/
+      invulnerables: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Last block authored by collator.
+       **/
+      lastAuthoredBlock: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u32>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * The (community) collation license holders.
+       **/
+      licenseDepositOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u128>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    common: {
+      /**
+       * Storage of the amount of collection admins.
+       **/
+      adminAmount: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Allowlisted collection users.
+       **/
+      allowlist: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Storage of collection info.
+       **/
+      collectionById: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<UpDataStructsCollection>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Storage of collection properties.
+       **/
+      collectionProperties: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Storage of token property permissions of a collection.
+       **/
+      collectionPropertyPermissions: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsPropertiesMapPropertyPermission>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Storage of the count of created collections. Essentially contains the last collection ID.
+       **/
+      createdCollectionCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Storage of the count of deleted collections.
+       **/
+      destroyedCollectionCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Not used by code, exists only to provide some types to metadata.
+       **/
+      dummyStorageValue: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[UpDataStructsCollectionStats, u32, u32, UpDataStructsTokenChild, PhantomTypeUpDataStructs]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * List of collection admins.
+       **/
+      isAdmin: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    configuration: {
+      appPromomotionConfigurationOverride: AugmentedQuery<ApiType, () => Observable<PalletConfigurationAppPromotionConfiguration>, []> & QueryableStorageEntry<ApiType, []>;
+      collatorSelectionDesiredCollatorsOverride: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      collatorSelectionKickThresholdOverride: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      collatorSelectionLicenseBondOverride: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      minGasPriceOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
+      weightToFeeCoefficientOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    council: {
+      /**
+       * The current members of the collective. This is stored sorted (just by value).
+       **/
+      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The prime member that helps determine the default vote behavior in case of absentations.
+       **/
+      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Proposals so far.
+       **/
+      proposalCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Actual proposal for a given hash, if it's current.
+       **/
+      proposalOf: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<Call>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * The hashes of the active proposals.
+       **/
+      proposals: AugmentedQuery<ApiType, () => Observable<Vec<H256>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Votes on a given proposal, if it is ongoing.
+       **/
+      voting: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<PalletCollectiveVotes>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    councilMembership: {
+      /**
+       * The current membership, stored as an ordered Vec.
+       **/
+      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current prime member, if one exists.
+       **/
+      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    democracy: {
+      /**
+       * A record of who vetoed what. Maps proposal hash to a possible existent block number
+       * (until when it may not be resubmitted) and who vetoed it.
+       **/
+      blacklist: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<ITuple<[u32, Vec<AccountId32>]>>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * Record of all proposals that have been subject to emergency cancellation.
+       **/
+      cancellations: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<bool>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * Those who have locked a deposit.
+       * 
+       * TWOX-NOTE: Safe, as increasing integer keys are safe.
+       **/
+      depositOf: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<ITuple<[Vec<AccountId32>, u128]>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * True if the last referendum tabled was submitted externally. False if it was a public
+       * proposal.
+       **/
+      lastTabledWasExternal: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The lowest referendum index representing an unbaked referendum. Equal to
+       * `ReferendumCount` if there isn't a unbaked referendum.
+       **/
+      lowestUnbaked: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * General information concerning any proposal or referendum.
+       * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON
+       * dump or IPFS hash of a JSON file.
+       * 
+       * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove)
+       * large preimages.
+       **/
+      metadataOf: AugmentedQuery<ApiType, (arg: PalletDemocracyMetadataOwner | { External: any } | { Proposal: any } | { Referendum: any } | string | Uint8Array) => Observable<Option<H256>>, [PalletDemocracyMetadataOwner]> & QueryableStorageEntry<ApiType, [PalletDemocracyMetadataOwner]>;
+      /**
+       * The referendum to be tabled whenever it would be valid to table an external proposal.
+       * This happens when a referendum needs to be tabled and one of two conditions are met:
+       * - `LastTabledWasExternal` is `false`; or
+       * - `PublicProps` is empty.
+       **/
+      nextExternal: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[FrameSupportPreimagesBounded, PalletDemocracyVoteThreshold]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The number of (public) proposals that have been made so far.
+       **/
+      publicPropCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The public proposals. Unsorted. The second item is the proposal.
+       **/
+      publicProps: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[u32, FrameSupportPreimagesBounded, AccountId32]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The next free referendum index, aka the number of referenda started so far.
+       **/
+      referendumCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Information concerning any given referendum.
+       * 
+       * TWOX-NOTE: SAFE as indexes are not under an attacker’s control.
+       **/
+      referendumInfoOf: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletDemocracyReferendumInfo>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * All votes for a particular voter. We store the balance for the number of votes that we
+       * have recorded. The second item is the total amount of delegations, that will be added.
+       * 
+       * TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway.
+       **/
+      votingOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<PalletDemocracyVoteVoting>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    dmpQueue: {
+      /**
+       * The configuration.
+       **/
+      configuration: AugmentedQuery<ApiType, () => Observable<CumulusPalletDmpQueueConfigData>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Counter for the related counted storage map
+       **/
+      counterForOverweight: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The overweight messages.
+       **/
+      overweight: AugmentedQuery<ApiType, (arg: u64 | AnyNumber | Uint8Array) => Observable<Option<ITuple<[u32, Bytes]>>>, [u64]> & QueryableStorageEntry<ApiType, [u64]>;
+      /**
+       * The page index.
+       **/
+      pageIndex: AugmentedQuery<ApiType, () => Observable<CumulusPalletDmpQueuePageIndexData>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The queue pages.
+       **/
+      pages: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<ITuple<[u32, Bytes]>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    ethereum: {
+      blockHash: AugmentedQuery<ApiType, (arg: U256 | AnyNumber | Uint8Array) => Observable<H256>, [U256]> & QueryableStorageEntry<ApiType, [U256]>;
+      /**
+       * The current Ethereum block.
+       **/
+      currentBlock: AugmentedQuery<ApiType, () => Observable<Option<EthereumBlock>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current Ethereum receipts.
+       **/
+      currentReceipts: AugmentedQuery<ApiType, () => Observable<Option<Vec<EthereumReceiptReceiptV3>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current transaction statuses.
+       **/
+      currentTransactionStatuses: AugmentedQuery<ApiType, () => Observable<Option<Vec<FpRpcTransactionStatus>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Injected transactions should have unique nonce, here we store current
+       **/
+      injectedNonce: AugmentedQuery<ApiType, () => Observable<U256>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Current building block's transactions and receipts.
+       **/
+      pending: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[EthereumTransactionTransactionV2, FpRpcTransactionStatus, EthereumReceiptReceiptV3]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    evm: {
+      accountCodes: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<Bytes>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      accountCodesMetadata: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<Option<PalletEvmCodeMetadata>>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      accountStorages: AugmentedQuery<ApiType, (arg1: H160 | string | Uint8Array, arg2: H256 | string | Uint8Array) => Observable<H256>, [H160, H256]> & QueryableStorageEntry<ApiType, [H160, H256]>;
+      /**
+       * Written on log, reset after transaction
+       * Should be empty between transactions
+       **/
+      currentLogs: AugmentedQuery<ApiType, () => Observable<Vec<EthereumLog>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    evmContractHelpers: {
+      /**
+       * Storage for users that allowed for sponsorship.
+       * 
+       * ### Usage
+       * Prefer to delete record from storage if user no more allowed for sponsorship.
+       * 
+       * * **Key1** - contract address.
+       * * **Key2** - user that allowed for sponsorship.
+       * * **Value** - allowance for sponsorship.
+       **/
+      allowlist: AugmentedQuery<ApiType, (arg1: H160 | string | Uint8Array, arg2: H160 | string | Uint8Array) => Observable<bool>, [H160, H160]> & QueryableStorageEntry<ApiType, [H160, H160]>;
+      /**
+       * Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.
+       * 
+       * ### Usage
+       * Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.
+       * 
+       * * **Key** - contract address.
+       * * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.
+       **/
+      allowlistEnabled: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<bool>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      /**
+       * Store owner for contract.
+       * 
+       * * **Key** - contract address.
+       * * **Value** - owner for contract.
+       **/
+      owner: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<H160>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      /**
+       * Deprecated: this storage is deprecated
+       **/
+      selfSponsoring: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<bool>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      sponsorBasket: AugmentedQuery<ApiType, (arg1: H160 | string | Uint8Array, arg2: H160 | string | Uint8Array) => Observable<Option<u32>>, [H160, H160]> & QueryableStorageEntry<ApiType, [H160, H160]>;
+      /**
+       * Store for contract sponsorship state.
+       * 
+       * * **Key** - contract address.
+       * * **Value** - sponsorship state.
+       **/
+      sponsoring: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<UpDataStructsSponsorshipStateBasicCrossAccountIdRepr>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      /**
+       * Storage for last sponsored block.
+       * 
+       * * **Key1** - contract address.
+       * * **Key2** - sponsored user address.
+       * * **Value** - last sponsored block number.
+       **/
+      sponsoringFeeLimit: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<BTreeMap<u32, U256>>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      /**
+       * Store for sponsoring mode.
+       * 
+       * ### Usage
+       * Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).
+       * 
+       * * **Key** - contract address.
+       * * **Value** - [`sponsoring mode`](SponsoringModeT).
+       **/
+      sponsoringMode: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<Option<PalletEvmContractHelpersSponsoringModeT>>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      /**
+       * Storage for sponsoring rate limit in blocks.
+       * 
+       * * **Key** - contract address.
+       * * **Value** - amount of sponsored blocks.
+       **/
+      sponsoringRateLimit: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<u32>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    evmMigration: {
+      migrationPending: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<bool>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    fellowshipCollective: {
+      /**
+       * The index of each ranks's member into the group of members who have at least that rank.
+       **/
+      idToIndex: AugmentedQuery<ApiType, (arg1: u16 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u16, AccountId32]> & QueryableStorageEntry<ApiType, [u16, AccountId32]>;
+      /**
+       * The members in the collective by index. All indices in the range `0..MemberCount` will
+       * return `Some`, however a member's index is not guaranteed to remain unchanged over time.
+       **/
+      indexToId: AugmentedQuery<ApiType, (arg1: u16 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<AccountId32>>, [u16, u32]> & QueryableStorageEntry<ApiType, [u16, u32]>;
+      /**
+       * The number of members in the collective who have at least the rank according to the index
+       * of the vec.
+       **/
+      memberCount: AugmentedQuery<ApiType, (arg: u16 | AnyNumber | Uint8Array) => Observable<u32>, [u16]> & QueryableStorageEntry<ApiType, [u16]>;
+      /**
+       * The current members of the collective.
+       **/
+      members: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<PalletRankedCollectiveMemberRecord>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Votes on a given proposal, if it is ongoing.
+       **/
+      voting: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<PalletRankedCollectiveVoteRecord>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
+      votingCleanup: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<Bytes>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    fellowshipReferenda: {
+      /**
+       * The number of referenda being decided currently.
+       **/
+      decidingCount: AugmentedQuery<ApiType, (arg: u16 | AnyNumber | Uint8Array) => Observable<u32>, [u16]> & QueryableStorageEntry<ApiType, [u16]>;
+      /**
+       * The metadata is a general information concerning the referendum.
+       * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON
+       * dump or IPFS hash of a JSON file.
+       * 
+       * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove)
+       * large preimages.
+       **/
+      metadataOf: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<H256>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * The next free referendum index, aka the number of referenda started so far.
+       **/
+      referendumCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Information concerning any given referendum.
+       **/
+      referendumInfoFor: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletReferendaReferendumInfo>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * The sorted list of referenda ready to be decided but not yet being decided, ordered by
+       * conviction-weighted approvals.
+       * 
+       * This should be empty if `DecidingCount` is less than `TrackInfo::max_deciding`.
+       **/
+      trackQueue: AugmentedQuery<ApiType, (arg: u16 | AnyNumber | Uint8Array) => Observable<Vec<ITuple<[u32, u32]>>>, [u16]> & QueryableStorageEntry<ApiType, [u16]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    foreignAssets: {
+      /**
+       * The storages for assets to fungible collection binding
+       * 
+       **/
+      assetBinding: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * The storages for AssetMetadatas.
+       * 
+       * AssetMetadatas: map AssetIds => Option<AssetMetadata>
+       **/
+      assetMetadatas: AugmentedQuery<ApiType, (arg: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<Option<PalletForeignAssetsModuleAssetMetadata>>, [PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [PalletForeignAssetsAssetId]>;
+      /**
+       * The storages for MultiLocations.
+       * 
+       * ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>
+       **/
+      foreignAssetLocations: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<StagingXcmV3MultiLocation>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * The storages for CurrencyIds.
+       * 
+       * LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>
+       **/
+      locationToCurrencyIds: AugmentedQuery<ApiType, (arg: StagingXcmV3MultiLocation | { parents?: any; interior?: any } | string | Uint8Array) => Observable<Option<u32>>, [StagingXcmV3MultiLocation]> & QueryableStorageEntry<ApiType, [StagingXcmV3MultiLocation]>;
+      /**
+       * Next available Foreign AssetId ID.
+       * 
+       * NextForeignAssetId: ForeignAssetId
+       **/
+      nextForeignAssetId: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    fungible: {
+      /**
+       * Storage for assets delegated to a limited extent to other users.
+       **/
+      allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Amount of tokens owned by an account inside a collection.
+       **/
+      balance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Total amount of fungible tokens inside a collection.
+       **/
+      totalSupply: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u128>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    identity: {
+      /**
+       * Information that is pertinent to identify the entity behind an account.
+       * 
+       * TWOX-NOTE: OK ― `AccountId` is a secure hash.
+       **/
+      identityOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<PalletIdentityRegistration>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * The set of registrars. Not expected to get very big as can only be added through a
+       * special origin (likely a council motion).
+       * 
+       * The index into this can be cast to `RegistrarIndex` to get a valid value.
+       **/
+      registrars: AugmentedQuery<ApiType, () => Observable<Vec<Option<PalletIdentityRegistrarInfo>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Alternative "sub" identities of this account.
+       * 
+       * The first item is the deposit, the second is a vector of the accounts.
+       * 
+       * TWOX-NOTE: OK ― `AccountId` is a secure hash.
+       **/
+      subsOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<ITuple<[u128, Vec<AccountId32>]>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * The super-identity of an alternative "sub" identity together with its name, within that
+       * context. If the account is not some other account's sub-identity, then just `None`.
+       **/
+      superOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<ITuple<[AccountId32, Data]>>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    inflation: {
+      /**
+       * Current inflation for `InflationBlockInterval` number of blocks
+       **/
+      blockInflation: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Next target (relay) block when inflation will be applied
+       **/
+      nextInflationBlock: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Next target (relay) block when inflation is recalculated
+       **/
+      nextRecalculationBlock: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Relay block when inflation has started
+       **/
+      startBlock: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * starting year total issuance
+       **/
+      startingYearTotalIssuance: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    maintenance: {
+      enabled: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    nonfungible: {
+      /**
+       * Amount of tokens owned by an account in a collection.
+       **/
+      accountBalance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u32>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Allowance set by a token owner for another user to perform one of certain transactions on a token.
+       **/
+      allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
+       **/
+      collectionAllowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Used to enumerate tokens owned by account.
+       **/
+      owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
+      /**
+       * Custom data of a token that is serialized to bytes,
+       * primarily reserved for on-chain operations,
+       * normally obscured from the external users.
+       * 
+       * Auxiliary properties are slightly different from
+       * usual [`TokenProperties`] due to an unlimited number
+       * and separately stored and written-to key-value pairs.
+       * 
+       * Currently unused.
+       **/
+      tokenAuxProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: UpDataStructsPropertyScope | 'None' | 'Rmrk' | number | Uint8Array, arg4: Bytes | string | Uint8Array) => Observable<Option<Bytes>>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry<ApiType, [u32, u32, UpDataStructsPropertyScope, Bytes]>;
+      /**
+       * Used to enumerate token's children.
+       **/
+      tokenChildren: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable<bool>, [u32, u32, ITuple<[u32, u32]>]> & QueryableStorageEntry<ApiType, [u32, u32, ITuple<[u32, u32]>]>;
+      /**
+       * Token data, used to partially describe a token.
+       **/
+      tokenData: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletNonfungibleItemData>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Map of key-value pairs, describing the metadata of a token.
+       **/
+      tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<UpDataStructsProperties>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Amount of burnt tokens in a collection.
+       **/
+      tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Total amount of minted tokens in a collection.
+       **/
+      tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    parachainInfo: {
+      parachainId: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    parachainSystem: {
+      /**
+       * Storage field that keeps track of bandwidth used by the unincluded segment along with the
+       * latest the latest HRMP watermark. Used for limiting the acceptance of new blocks with
+       * respect to relay chain constraints.
+       **/
+      aggregatedUnincludedSegment: AugmentedQuery<ApiType, () => Observable<Option<CumulusPalletParachainSystemUnincludedSegmentSegmentTracker>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The number of HRMP messages we observed in `on_initialize` and thus used that number for
+       * announcing the weight of `on_initialize` and `on_finalize`.
+       **/
+      announcedHrmpMessagesPerCandidate: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The next authorized upgrade, if there is one.
+       **/
+      authorizedUpgrade: AugmentedQuery<ApiType, () => Observable<Option<CumulusPalletParachainSystemCodeUpgradeAuthorization>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * A custom head data that should be returned as result of `validate_block`.
+       * 
+       * See `Pallet::set_custom_validation_head_data` for more information.
+       **/
+      customValidationHeadData: AugmentedQuery<ApiType, () => Observable<Option<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Were the validation data set to notify the relay chain?
+       **/
+      didSetValidationCode: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The parachain host configuration that was obtained from the relay parent.
+       * 
+       * This field is meant to be updated each block with the validation data inherent. Therefore,
+       * before processing of the inherent, e.g. in `on_initialize` this data may be stale.
+       * 
+       * This data is also absent from the genesis.
+       **/
+      hostConfiguration: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5AbridgedHostConfiguration>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * HRMP messages that were sent in a block.
+       * 
+       * This will be cleared in `on_initialize` of each new block.
+       **/
+      hrmpOutboundMessages: AugmentedQuery<ApiType, () => Observable<Vec<PolkadotCorePrimitivesOutboundHrmpMessage>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * HRMP watermark that was set in a block.
+       * 
+       * This will be cleared in `on_initialize` of each new block.
+       **/
+      hrmpWatermark: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The last downward message queue chain head we have observed.
+       * 
+       * This value is loaded before and saved after processing inbound downward messages carried
+       * by the system inherent.
+       **/
+      lastDmqMqcHead: AugmentedQuery<ApiType, () => Observable<H256>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The message queue chain heads we have observed per each channel incoming channel.
+       * 
+       * This value is loaded before and saved after processing inbound downward messages carried
+       * by the system inherent.
+       **/
+      lastHrmpMqcHeads: AugmentedQuery<ApiType, () => Observable<BTreeMap<u32, H256>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The relay chain block number associated with the last parachain block.
+       **/
+      lastRelayChainBlockNumber: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Validation code that is set by the parachain and is to be communicated to collator and
+       * consequently the relay-chain.
+       * 
+       * This will be cleared in `on_initialize` of each new block if no other pallet already set
+       * the value.
+       **/
+      newValidationCode: AugmentedQuery<ApiType, () => Observable<Option<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Upward messages that are still pending and not yet send to the relay chain.
+       **/
+      pendingUpwardMessages: AugmentedQuery<ApiType, () => Observable<Vec<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * In case of a scheduled upgrade, this storage field contains the validation code to be
+       * applied.
+       * 
+       * As soon as the relay chain gives us the go-ahead signal, we will overwrite the
+       * [`:code`][sp_core::storage::well_known_keys::CODE] which will result the next block process
+       * with the new validation code. This concludes the upgrade process.
+       **/
+      pendingValidationCode: AugmentedQuery<ApiType, () => Observable<Bytes>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Number of downward messages processed in a block.
+       * 
+       * This will be cleared in `on_initialize` of each new block.
+       **/
+      processedDownwardMessages: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The state proof for the last relay parent block.
+       * 
+       * This field is meant to be updated each block with the validation data inherent. Therefore,
+       * before processing of the inherent, e.g. in `on_initialize` this data may be stale.
+       * 
+       * This data is also absent from the genesis.
+       **/
+      relayStateProof: AugmentedQuery<ApiType, () => Observable<Option<SpTrieStorageProof>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The snapshot of some state related to messaging relevant to the current parachain as per
+       * the relay parent.
+       * 
+       * This field is meant to be updated each block with the validation data inherent. Therefore,
+       * before processing of the inherent, e.g. in `on_initialize` this data may be stale.
+       * 
+       * This data is also absent from the genesis.
+       **/
+      relevantMessagingState: AugmentedQuery<ApiType, () => Observable<Option<CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The weight we reserve at the beginning of the block for processing DMP messages. This
+       * overrides the amount set in the Config trait.
+       **/
+      reservedDmpWeightOverride: AugmentedQuery<ApiType, () => Observable<Option<SpWeightsWeightV2Weight>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The weight we reserve at the beginning of the block for processing XCMP messages. This
+       * overrides the amount set in the Config trait.
+       **/
+      reservedXcmpWeightOverride: AugmentedQuery<ApiType, () => Observable<Option<SpWeightsWeightV2Weight>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Latest included block descendants the runtime accepted. In other words, these are
+       * ancestors of the currently executing block which have not been included in the observed
+       * relay-chain state.
+       * 
+       * The segment length is limited by the capacity returned from the [`ConsensusHook`] configured
+       * in the pallet.
+       **/
+      unincludedSegment: AugmentedQuery<ApiType, () => Observable<Vec<CumulusPalletParachainSystemUnincludedSegmentAncestor>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Optional upgrade go-ahead signal from the relay-chain.
+       * 
+       * This storage item is a mirror of the corresponding value for the current parachain from the
+       * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is
+       * set after the inherent.
+       **/
+      upgradeGoAhead: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5UpgradeGoAhead>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * An option which indicates if the relay-chain restricts signalling a validation code upgrade.
+       * In other words, if this is `Some` and [`NewValidationCode`] is `Some` then the produced
+       * candidate will be invalid.
+       * 
+       * This storage item is a mirror of the corresponding value for the current parachain from the
+       * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is
+       * set after the inherent.
+       **/
+      upgradeRestrictionSignal: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5UpgradeRestriction>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Upward messages that were sent in a block.
+       * 
+       * This will be cleared in `on_initialize` of each new block.
+       **/
+      upwardMessages: AugmentedQuery<ApiType, () => Observable<Vec<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The [`PersistedValidationData`] set for this block.
+       * This value is expected to be set only once per block and it's never stored
+       * in the trie.
+       **/
+      validationData: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5PersistedValidationData>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    polkadotXcm: {
+      /**
+       * The existing asset traps.
+       * 
+       * Key is the blake2 256 hash of (origin, versioned `MultiAssets`) pair. Value is the number of
+       * times this pair has been trapped (usually just 1 if it exists at all).
+       **/
+      assetTraps: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<u32>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * The current migration's stage, if any.
+       **/
+      currentMigration: AugmentedQuery<ApiType, () => Observable<Option<PalletXcmVersionMigrationStage>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Fungible assets which we know are locked on this chain.
+       **/
+      lockedFungibles: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<Vec<ITuple<[u128, StagingXcmVersionedMultiLocation]>>>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * The ongoing queries.
+       **/
+      queries: AugmentedQuery<ApiType, (arg: u64 | AnyNumber | Uint8Array) => Observable<Option<PalletXcmQueryStatus>>, [u64]> & QueryableStorageEntry<ApiType, [u64]>;
+      /**
+       * The latest available query index.
+       **/
+      queryCounter: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Fungible assets which we know are locked on a remote chain.
+       **/
+      remoteLockedFungibles: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array, arg3: StagingXcmVersionedAssetId | { V3: any } | string | Uint8Array) => Observable<Option<PalletXcmRemoteLockedFungibleRecord>>, [u32, AccountId32, StagingXcmVersionedAssetId]> & QueryableStorageEntry<ApiType, [u32, AccountId32, StagingXcmVersionedAssetId]>;
+      /**
+       * Default version to encode XCM when latest version of destination is unknown. If `None`,
+       * then the destinations whose XCM version is unknown are considered unreachable.
+       **/
+      safeXcmVersion: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The Latest versions that we know various locations support.
+       **/
+      supportedVersion: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => Observable<Option<u32>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry<ApiType, [u32, StagingXcmVersionedMultiLocation]>;
+      /**
+       * Destinations whose latest XCM version we would like to know. Duplicates not allowed, and
+       * the `u32` counter is the number of times that a send to the destination has been attempted,
+       * which is used as a prioritization.
+       **/
+      versionDiscoveryQueue: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[StagingXcmVersionedMultiLocation, u32]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * All locations that we have requested version notifications from.
+       **/
+      versionNotifiers: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => Observable<Option<u64>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry<ApiType, [u32, StagingXcmVersionedMultiLocation]>;
+      /**
+       * The target locations that are subscribed to our version changes, as well as the most recent
+       * of our versions we informed them of.
+       **/
+      versionNotifyTargets: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => Observable<Option<ITuple<[u64, SpWeightsWeightV2Weight, u32]>>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry<ApiType, [u32, StagingXcmVersionedMultiLocation]>;
+      /**
+       * Global suspension state of the XCM executor.
+       **/
+      xcmExecutionSuspended: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    preimage: {
+      preimageFor: AugmentedQuery<ApiType, (arg: ITuple<[H256, u32]> | [H256 | string | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable<Option<Bytes>>, [ITuple<[H256, u32]>]> & QueryableStorageEntry<ApiType, [ITuple<[H256, u32]>]>;
+      /**
+       * The request status of a given hash.
+       **/
+      statusFor: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<PalletPreimageRequestStatus>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    refungible: {
+      /**
+       * Amount of tokens (not pieces) partially owned by an account within a collection.
+       **/
+      accountBalance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u32>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token.
+       **/
+      allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg4: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Amount of token pieces owned by account.
+       **/
+      balance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Spender set by a wallet owner that could perform certain transactions on all tokens in the wallet.
+       **/
+      collectionAllowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * Used to enumerate tokens owned by account.
+       **/
+      owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
+      /**
+       * Amount of pieces a refungible token is split into.
+       **/
+      tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<UpDataStructsProperties>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Amount of tokens burnt in a collection.
+       **/
+      tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Total amount of minted tokens in a collection.
+       **/
+      tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Total amount of pieces for token
+       **/
+      totalSupply: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<u128>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    scheduler: {
+      /**
+       * Items to be executed, indexed by the block number that they should be executed on.
+       **/
+      agenda: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<Option<PalletSchedulerScheduled>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      incompleteSince: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Lookup from a name to the block number and index of the task.
+       * 
+       * For v3 -> v4 the previously unbounded identities are Blake2-256 hashed to form the v4
+       * identities.
+       **/
+      lookup: AugmentedQuery<ApiType, (arg: U8aFixed | string | Uint8Array) => Observable<Option<ITuple<[u32, u32]>>>, [U8aFixed]> & QueryableStorageEntry<ApiType, [U8aFixed]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    session: {
+      /**
+       * Current index of the session.
+       **/
+      currentIndex: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Indices of disabled validators.
+       * 
+       * The vec is always kept sorted so that we can find whether a given validator is
+       * disabled using binary search. It gets cleared when `on_session_ending` returns
+       * a new set of identities.
+       **/
+      disabledValidators: AugmentedQuery<ApiType, () => Observable<Vec<u32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The owner of a key. The key is the `KeyTypeId` + the encoded key.
+       **/
+      keyOwner: AugmentedQuery<ApiType, (arg: ITuple<[SpCoreCryptoKeyTypeId, Bytes]> | [SpCoreCryptoKeyTypeId | string | Uint8Array, Bytes | string | Uint8Array]) => Observable<Option<AccountId32>>, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]> & QueryableStorageEntry<ApiType, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]>;
+      /**
+       * The next session keys for a validator.
+       **/
+      nextKeys: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<OpalRuntimeRuntimeCommonSessionKeys>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * True if the underlying economic identities or weighting behind the validators
+       * has changed in the queued validator set.
+       **/
+      queuedChanged: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The queued keys for the next session. When the next session begins, these keys
+       * will be used to determine the validator's session keys.
+       **/
+      queuedKeys: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[AccountId32, OpalRuntimeRuntimeCommonSessionKeys]>>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current set of validators.
+       **/
+      validators: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    stateTrieMigration: {
+      /**
+       * The limits that are imposed on automatic migrations.
+       * 
+       * If set to None, then no automatic migration happens.
+       **/
+      autoLimits: AugmentedQuery<ApiType, () => Observable<Option<PalletStateTrieMigrationMigrationLimits>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Migration progress.
+       * 
+       * This stores the snapshot of the last migrated keys. It can be set into motion and move
+       * forward by any of the means provided by this pallet.
+       **/
+      migrationProcess: AugmentedQuery<ApiType, () => Observable<PalletStateTrieMigrationMigrationTask>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The maximum limits that the signed migration could use.
+       * 
+       * If not set, no signed submission is allowed.
+       **/
+      signedMigrationMaxLimits: AugmentedQuery<ApiType, () => Observable<Option<PalletStateTrieMigrationMigrationLimits>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    sudo: {
+      /**
+       * The `AccountId` of the sudo key.
+       **/
+      key: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    system: {
+      /**
+       * The full account information for a particular account ID.
+       **/
+      account: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<FrameSystemAccountInfo>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Total length (in bytes) for all extrinsics put together, for the current block.
+       **/
+      allExtrinsicsLen: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Map of block numbers to block hashes.
+       **/
+      blockHash: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<H256>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * The current weight for the block.
+       **/
+      blockWeight: AugmentedQuery<ApiType, () => Observable<FrameSupportDispatchPerDispatchClassWeight>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Digest of the current block, also part of the block header.
+       **/
+      digest: AugmentedQuery<ApiType, () => Observable<SpRuntimeDigest>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The number of events in the `Events<T>` list.
+       **/
+      eventCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Events deposited for the current block.
+       * 
+       * NOTE: The item is unbound and should therefore never be read on chain.
+       * It could otherwise inflate the PoV size of a block.
+       * 
+       * Events have a large in-memory size. Box the events to not go out-of-memory
+       * just in case someone still reads them from within the runtime.
+       **/
+      events: AugmentedQuery<ApiType, () => Observable<Vec<FrameSystemEventRecord>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Mapping between a topic (represented by T::Hash) and a vector of indexes
+       * of events in the `<Events<T>>` list.
+       * 
+       * All topic vectors have deterministic storage locations depending on the topic. This
+       * allows light-clients to leverage the changes trie storage tracking mechanism and
+       * in case of changes fetch the list of events of interest.
+       * 
+       * The value has the type `(BlockNumberFor<T>, EventIndex)` because if we used only just
+       * the `EventIndex` then in case if the topic has the same contents on the next block
+       * no notification will be triggered thus the event might be lost.
+       **/
+      eventTopics: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Vec<ITuple<[u32, u32]>>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * The execution phase of the block.
+       **/
+      executionPhase: AugmentedQuery<ApiType, () => Observable<Option<FrameSystemPhase>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Total extrinsics count for the current block.
+       **/
+      extrinsicCount: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Extrinsics data for the current block (maps an extrinsic's index to its data).
+       **/
+      extrinsicData: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened.
+       **/
+      lastRuntimeUpgrade: AugmentedQuery<ApiType, () => Observable<Option<FrameSystemLastRuntimeUpgradeInfo>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current block number being processed. Set by `execute_block`.
+       **/
+      number: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Hash of the previous block.
+       **/
+      parentHash: AugmentedQuery<ApiType, () => Observable<H256>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * True if we have upgraded so that AccountInfo contains three types of `RefCount`. False
+       * (default) if not.
+       **/
+      upgradedToTripleRefCount: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * True if we have upgraded so that `type RefCount` is `u32`. False (default) if not.
+       **/
+      upgradedToU32RefCount: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    technicalCommittee: {
+      /**
+       * The current members of the collective. This is stored sorted (just by value).
+       **/
+      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The prime member that helps determine the default vote behavior in case of absentations.
+       **/
+      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Proposals so far.
+       **/
+      proposalCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Actual proposal for a given hash, if it's current.
+       **/
+      proposalOf: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<Call>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * The hashes of the active proposals.
+       **/
+      proposals: AugmentedQuery<ApiType, () => Observable<Vec<H256>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Votes on a given proposal, if it is ongoing.
+       **/
+      voting: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<PalletCollectiveVotes>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    technicalCommitteeMembership: {
+      /**
+       * The current membership, stored as an ordered Vec.
+       **/
+      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The current prime member, if one exists.
+       **/
+      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    testUtils: {
+      enabled: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      testValue: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    timestamp: {
+      /**
+       * Did the timestamp get updated in this block?
+       **/
+      didUpdate: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Current time for the current block.
+       **/
+      now: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    tokens: {
+      /**
+       * The balance of a token type under an account.
+       * 
+       * NOTE: If the total is ever zero, decrease account ref account.
+       * 
+       * NOTE: This is only used in the case that this module is used to store
+       * balances.
+       **/
+      accounts: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<OrmlTokensAccountData>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [AccountId32, PalletForeignAssetsAssetId]>;
+      /**
+       * Any liquidity locks of a token type under an account.
+       * NOTE: Should only be accessed when setting, changing and freeing a lock.
+       **/
+      locks: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<Vec<OrmlTokensBalanceLock>>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [AccountId32, PalletForeignAssetsAssetId]>;
+      /**
+       * Named reserves on some account balances.
+       **/
+      reserves: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<Vec<OrmlTokensReserveData>>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [AccountId32, PalletForeignAssetsAssetId]>;
+      /**
+       * The total issuance of a token type.
+       **/
+      totalIssuance: AugmentedQuery<ApiType, (arg: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<u128>, [PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [PalletForeignAssetsAssetId]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    transactionPayment: {
+      nextFeeMultiplier: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      storageVersion: AugmentedQuery<ApiType, () => Observable<PalletTransactionPaymentReleases>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    treasury: {
+      /**
+       * Proposal indices that have been approved but not yet awarded.
+       **/
+      approvals: AugmentedQuery<ApiType, () => Observable<Vec<u32>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The amount which has been reported as inactive to Currency.
+       **/
+      deactivated: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Number of proposals that have been made.
+       **/
+      proposalCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Proposals that have been made.
+       **/
+      proposals: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletTreasuryProposal>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    unique: {
+      /**
+       * Used for migrations
+       **/
+      chainVersion: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * (Collection id (controlled?2), who created (real))
+       * TODO: Off chain worker should remove from this map when collection gets removed
+       **/
+      createItemBasket: AugmentedQuery<ApiType, (arg: ITuple<[u32, AccountId32]> | [u32 | AnyNumber | Uint8Array, AccountId32 | string | Uint8Array]) => Observable<Option<u32>>, [ITuple<[u32, AccountId32]>]> & QueryableStorageEntry<ApiType, [ITuple<[u32, AccountId32]>]>;
+      /**
+       * Last sponsoring of fungible tokens approval in a collection
+       **/
+      fungibleApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
+      /**
+       * Collection id (controlled?2), owning user (real)
+       **/
+      fungibleTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
+      /**
+       * Last sponsoring of NFT approval in a collection
+       **/
+      nftApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Collection id (controlled?2), token id (controlled?2)
+       **/
+      nftTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Last sponsoring of RFT approval in a collection
+       **/
+      refungibleApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, u32, AccountId32]>;
+      /**
+       * Collection id (controlled?2), token id (controlled?2)
+       **/
+      reFungibleTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, u32, AccountId32]>;
+      /**
+       * Last sponsoring of token property setting // todo:doc rephrase this and the following
+       **/
+      tokenPropertyBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    vesting: {
+      /**
+       * Vesting schedules of an account.
+       * 
+       * VestingSchedules: map AccountId => Vec<VestingSchedule>
+       **/
+      vestingSchedules: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<OrmlVestingVestingSchedule>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+    xcmpQueue: {
+      /**
+       * Counter for the related counted storage map
+       **/
+      counterForOverweight: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Inbound aggregate XCMP messages. It can only be one per ParaId/block.
+       **/
+      inboundXcmpMessages: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
+      /**
+       * Status of the inbound XCMP channels.
+       **/
+      inboundXcmpStatus: AugmentedQuery<ApiType, () => Observable<Vec<CumulusPalletXcmpQueueInboundChannelDetails>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The messages outbound in a given XCMP channel.
+       **/
+      outboundXcmpMessages: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u16 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32, u16]> & QueryableStorageEntry<ApiType, [u32, u16]>;
+      /**
+       * The non-empty XCMP channels in order of becoming non-empty, and the index of the first
+       * and last outbound message. If the two indices are equal, then it indicates an empty
+       * queue and there must be a non-`Ok` `OutboundStatus`. We assume queues grow no greater
+       * than 65535 items. Queue indices for normal messages begin at one; zero is reserved in
+       * case of the need to send a high-priority signal message this block.
+       * The bool is true if there is a signal message waiting to be sent.
+       **/
+      outboundXcmpStatus: AugmentedQuery<ApiType, () => Observable<Vec<CumulusPalletXcmpQueueOutboundChannelDetails>>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The messages that exceeded max individual message weight budget.
+       * 
+       * These message stay in this storage map until they are manually dispatched via
+       * `service_overweight`.
+       **/
+      overweight: AugmentedQuery<ApiType, (arg: u64 | AnyNumber | Uint8Array) => Observable<Option<ITuple<[u32, u32, Bytes]>>>, [u64]> & QueryableStorageEntry<ApiType, [u64]>;
+      /**
+       * The number of overweight messages ever recorded in `Overweight`. Also doubles as the next
+       * available free overweight index.
+       **/
+      overweightCount: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * The configuration which controls the dynamics of the outbound queue.
+       **/
+      queueConfig: AugmentedQuery<ApiType, () => Observable<CumulusPalletXcmpQueueQueueConfigData>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Whether or not the XCMP queue is suspended from executing incoming XCMs or not.
+       **/
+      queueSuspended: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
+      /**
+       * Any signal messages waiting to be sent.
+       **/
+      signalMessages: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
+      /**
+       * Generic query
+       **/
+      [key: string]: QueryableStorageEntry<ApiType>;
+    };
+  } // AugmentedQueries
+} // declare module
addedjs-packages/types/augment-api-rpc.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api-rpc.ts
@@ -0,0 +1,752 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/rpc-core/types/jsonrpc';
+
+import type { PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCollectionLimits, UpDataStructsCollectionStats, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsRpcCollection, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo } from './default';
+import type { AugmentedRpc } from '@polkadot/rpc-core/types';
+import type { Metadata, StorageKey } from '@polkadot/types';
+import type { Bytes, HashMap, Json, Null, Option, Text, U256, U64, Vec, bool, f64, u128, u32, u64 } from '@polkadot/types-codec';
+import type { AnyNumber, Codec, ITuple } from '@polkadot/types-codec/types';
+import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author';
+import type { EpochAuthorship } from '@polkadot/types/interfaces/babe';
+import type { BeefySignedCommitment } from '@polkadot/types/interfaces/beefy';
+import type { BlockHash } from '@polkadot/types/interfaces/chain';
+import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate';
+import type { AuthorityId } from '@polkadot/types/interfaces/consensus';
+import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequestV1 } from '@polkadot/types/interfaces/contracts';
+import type { BlockStats } from '@polkadot/types/interfaces/dev';
+import type { CreatedBlock } from '@polkadot/types/interfaces/engine';
+import type { EthAccount, EthCallRequest, EthFeeHistory, EthFilter, EthFilterChanges, EthLog, EthReceipt, EthRichBlock, EthSubKind, EthSubParams, EthSyncStatus, EthTransaction, EthTransactionRequest, EthWork } from '@polkadot/types/interfaces/eth';
+import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
+import type { EncodedFinalityProofs, JustificationNotification, ReportedRoundStates } from '@polkadot/types/interfaces/grandpa';
+import type { MmrHash, MmrLeafBatchProof } from '@polkadot/types/interfaces/mmr';
+import type { StorageKind } from '@polkadot/types/interfaces/offchain';
+import type { FeeDetails, RuntimeDispatchInfoV1 } from '@polkadot/types/interfaces/payment';
+import type { RpcMethods } from '@polkadot/types/interfaces/rpc';
+import type { AccountId, BlockNumber, H160, H256, H64, Hash, Header, Index, Justification, KeyValue, SignedBlock, StorageData } from '@polkadot/types/interfaces/runtime';
+import type { MigrationStatusResult, ReadProof, RuntimeVersion, TraceBlockResponse } from '@polkadot/types/interfaces/state';
+import type { ApplyExtrinsicResult, ChainProperties, ChainType, Health, NetworkState, NodeRole, PeerInfo, SyncState } from '@polkadot/types/interfaces/system';
+import type { IExtrinsic, Observable } from '@polkadot/types/types';
+
+export type __AugmentedRpc = AugmentedRpc<() => unknown>;
+
+declare module '@polkadot/rpc-core/types/jsonrpc' {
+  interface RpcInterface {
+    appPromotion: {
+      /**
+       * Returns the total amount of unstaked tokens
+       **/
+      pendingUnstake: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
+      /**
+       * Returns the total amount of unstaked tokens per block
+       **/
+      pendingUnstakePerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<ITuple<[u32, u128]>>>>;
+      /**
+       * Returns the total amount of staked tokens
+       **/
+      totalStaked: AugmentedRpc<(staker?: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
+      /**
+       * Returns the total amount of staked tokens per block when staked
+       **/
+      totalStakedPerBlock: AugmentedRpc<(staker: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<ITuple<[u32, u128]>>>>;
+    };
+    author: {
+      /**
+       * Returns true if the keystore has private keys for the given public key and key type.
+       **/
+      hasKey: AugmentedRpc<(publicKey: Bytes | string | Uint8Array, keyType: Text | string) => Observable<bool>>;
+      /**
+       * Returns true if the keystore has private keys for the given session public keys.
+       **/
+      hasSessionKeys: AugmentedRpc<(sessionKeys: Bytes | string | Uint8Array) => Observable<bool>>;
+      /**
+       * Insert a key into the keystore.
+       **/
+      insertKey: AugmentedRpc<(keyType: Text | string, suri: Text | string, publicKey: Bytes | string | Uint8Array) => Observable<Bytes>>;
+      /**
+       * Returns all pending extrinsics, potentially grouped by sender
+       **/
+      pendingExtrinsics: AugmentedRpc<() => Observable<Vec<Extrinsic>>>;
+      /**
+       * Remove given extrinsic from the pool and temporarily ban it to prevent reimporting
+       **/
+      removeExtrinsic: AugmentedRpc<(bytesOrHash: Vec<ExtrinsicOrHash> | (ExtrinsicOrHash | { Hash: any } | { Extrinsic: any } | string | Uint8Array)[]) => Observable<Vec<Hash>>>;
+      /**
+       * Generate new session keys and returns the corresponding public keys
+       **/
+      rotateKeys: AugmentedRpc<() => Observable<Bytes>>;
+      /**
+       * Submit and subscribe to watch an extrinsic until unsubscribed
+       **/
+      submitAndWatchExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable<ExtrinsicStatus>>;
+      /**
+       * Submit a fully formatted extrinsic for block inclusion
+       **/
+      submitExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable<Hash>>;
+    };
+    babe: {
+      /**
+       * Returns data about which slots (primary or secondary) can be claimed in the current epoch with the keys in the keystore
+       **/
+      epochAuthorship: AugmentedRpc<() => Observable<HashMap<AuthorityId, EpochAuthorship>>>;
+    };
+    beefy: {
+      /**
+       * Returns hash of the latest BEEFY finalized block as seen by this client.
+       **/
+      getFinalizedHead: AugmentedRpc<() => Observable<H256>>;
+      /**
+       * Returns the block most recently finalized by BEEFY, alongside side its justification.
+       **/
+      subscribeJustifications: AugmentedRpc<() => Observable<BeefySignedCommitment>>;
+    };
+    chain: {
+      /**
+       * Get header and body of a relay chain block
+       **/
+      getBlock: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable<SignedBlock>>;
+      /**
+       * Get the block hash for a specific block
+       **/
+      getBlockHash: AugmentedRpc<(blockNumber?: BlockNumber | AnyNumber | Uint8Array) => Observable<BlockHash>>;
+      /**
+       * Get hash of the last finalized block in the canon chain
+       **/
+      getFinalizedHead: AugmentedRpc<() => Observable<BlockHash>>;
+      /**
+       * Retrieves the header for a specific block
+       **/
+      getHeader: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable<Header>>;
+      /**
+       * Retrieves the newest header via subscription
+       **/
+      subscribeAllHeads: AugmentedRpc<() => Observable<Header>>;
+      /**
+       * Retrieves the best finalized header via subscription
+       **/
+      subscribeFinalizedHeads: AugmentedRpc<() => Observable<Header>>;
+      /**
+       * Retrieves the best header via subscription
+       **/
+      subscribeNewHeads: AugmentedRpc<() => Observable<Header>>;
+    };
+    childstate: {
+      /**
+       * Returns the keys with prefix from a child storage, leave empty to get all the keys
+       **/
+      getKeys: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
+      /**
+       * Returns the keys with prefix from a child storage with pagination support
+       **/
+      getKeysPaged: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
+      /**
+       * Returns a child storage entry at a specific block state
+       **/
+      getStorage: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Option<StorageData>>>;
+      /**
+       * Returns child storage entries for multiple keys at a specific block state
+       **/
+      getStorageEntries: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: Hash | string | Uint8Array) => Observable<Vec<Option<StorageData>>>>;
+      /**
+       * Returns the hash of a child storage entry at a block state
+       **/
+      getStorageHash: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Option<Hash>>>;
+      /**
+       * Returns the size of a child storage entry at a block state
+       **/
+      getStorageSize: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Option<u64>>>;
+    };
+    contracts: {
+      /**
+       * @deprecated Use the runtime interface `api.call.contractsApi.call` instead
+       * Executes a call to a contract
+       **/
+      call: AugmentedRpc<(callRequest: ContractCallRequest | { origin?: any; dest?: any; value?: any; gasLimit?: any; storageDepositLimit?: any; inputData?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ContractExecResult>>;
+      /**
+       * @deprecated Use the runtime interface `api.call.contractsApi.getStorage` instead
+       * Returns the value under a specified storage key in a contract
+       **/
+      getStorage: AugmentedRpc<(address: AccountId | string | Uint8Array, key: H256 | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<Option<Bytes>>>;
+      /**
+       * @deprecated Use the runtime interface `api.call.contractsApi.instantiate` instead
+       * Instantiate a new contract
+       **/
+      instantiate: AugmentedRpc<(request: InstantiateRequestV1 | { origin?: any; value?: any; gasLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ContractInstantiateResult>>;
+      /**
+       * @deprecated Not available in newer versions of the contracts interfaces
+       * Returns the projected time a given contract will be able to sustain paying its rent
+       **/
+      rentProjection: AugmentedRpc<(address: AccountId | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<Option<BlockNumber>>>;
+      /**
+       * @deprecated Use the runtime interface `api.call.contractsApi.uploadCode` instead
+       * Upload new code without instantiating a contract from it
+       **/
+      uploadCode: AugmentedRpc<(uploadRequest: CodeUploadRequest | { origin?: any; code?: any; storageDepositLimit?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<CodeUploadResult>>;
+    };
+    dev: {
+      /**
+       * Reexecute the specified `block_hash` and gather statistics while doing so
+       **/
+      getBlockStats: AugmentedRpc<(at: Hash | string | Uint8Array) => Observable<Option<BlockStats>>>;
+    };
+    engine: {
+      /**
+       * Instructs the manual-seal authorship task to create a new block
+       **/
+      createBlock: AugmentedRpc<(createEmpty: bool | boolean | Uint8Array, finalize: bool | boolean | Uint8Array, parentHash?: BlockHash | string | Uint8Array) => Observable<CreatedBlock>>;
+      /**
+       * Instructs the manual-seal authorship task to finalize a block
+       **/
+      finalizeBlock: AugmentedRpc<(hash: BlockHash | string | Uint8Array, justification?: Justification) => Observable<bool>>;
+    };
+    eth: {
+      /**
+       * Returns accounts list.
+       **/
+      accounts: AugmentedRpc<() => Observable<Vec<H160>>>;
+      /**
+       * Returns the blockNumber
+       **/
+      blockNumber: AugmentedRpc<() => Observable<U256>>;
+      /**
+       * Call contract, returning the output data.
+       **/
+      call: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<Bytes>>;
+      /**
+       * Returns the chain ID used for transaction signing at the current best block. None is returned if not available.
+       **/
+      chainId: AugmentedRpc<() => Observable<U64>>;
+      /**
+       * Returns block author.
+       **/
+      coinbase: AugmentedRpc<() => Observable<H160>>;
+      /**
+       * Estimate gas needed for execution of given contract.
+       **/
+      estimateGas: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns fee history for given block count & reward percentiles
+       **/
+      feeHistory: AugmentedRpc<(blockCount: U256 | AnyNumber | Uint8Array, newestBlock: BlockNumber | AnyNumber | Uint8Array, rewardPercentiles: Option<Vec<f64>> | null | Uint8Array | Vec<f64> | (f64)[]) => Observable<EthFeeHistory>>;
+      /**
+       * Returns current gas price.
+       **/
+      gasPrice: AugmentedRpc<() => Observable<U256>>;
+      /**
+       * Returns balance of the given account.
+       **/
+      getBalance: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns block with given hash.
+       **/
+      getBlockByHash: AugmentedRpc<(hash: H256 | string | Uint8Array, full: bool | boolean | Uint8Array) => Observable<Option<EthRichBlock>>>;
+      /**
+       * Returns block with given number.
+       **/
+      getBlockByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array, full: bool | boolean | Uint8Array) => Observable<Option<EthRichBlock>>>;
+      /**
+       * Returns the number of transactions in a block with given hash.
+       **/
+      getBlockTransactionCountByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns the number of transactions in a block with given block number.
+       **/
+      getBlockTransactionCountByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns the code at given address at given time (block number).
+       **/
+      getCode: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<Bytes>>;
+      /**
+       * Returns filter changes since last poll.
+       **/
+      getFilterChanges: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable<EthFilterChanges>>;
+      /**
+       * Returns all logs matching given filter (in a range 'from' - 'to').
+       **/
+      getFilterLogs: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable<Vec<EthLog>>>;
+      /**
+       * Returns logs matching given filter object.
+       **/
+      getLogs: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable<Vec<EthLog>>>;
+      /**
+       * Returns proof for account and storage.
+       **/
+      getProof: AugmentedRpc<(address: H160 | string | Uint8Array, storageKeys: Vec<H256> | (H256 | string | Uint8Array)[], number: BlockNumber | AnyNumber | Uint8Array) => Observable<EthAccount>>;
+      /**
+       * Returns content of the storage at given address.
+       **/
+      getStorageAt: AugmentedRpc<(address: H160 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<H256>>;
+      /**
+       * Returns transaction at given block hash and index.
+       **/
+      getTransactionByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthTransaction>>;
+      /**
+       * Returns transaction by given block number and index.
+       **/
+      getTransactionByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthTransaction>>;
+      /**
+       * Get transaction by its hash.
+       **/
+      getTransactionByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<EthTransaction>>;
+      /**
+       * Returns the number of transactions sent from given address at given time (block number).
+       **/
+      getTransactionCount: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns transaction receipt by transaction hash.
+       **/
+      getTransactionReceipt: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<EthReceipt>>;
+      /**
+       * Returns an uncles at given block and index.
+       **/
+      getUncleByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthRichBlock>>;
+      /**
+       * Returns an uncles at given block and index.
+       **/
+      getUncleByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthRichBlock>>;
+      /**
+       * Returns the number of uncles in a block with given hash.
+       **/
+      getUncleCountByBlockHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns the number of uncles in a block with given block number.
+       **/
+      getUncleCountByBlockNumber: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns the hash of the current block, the seedHash, and the boundary condition to be met.
+       **/
+      getWork: AugmentedRpc<() => Observable<EthWork>>;
+      /**
+       * Returns the number of hashes per second that the node is mining with.
+       **/
+      hashrate: AugmentedRpc<() => Observable<U256>>;
+      /**
+       * Returns max priority fee per gas
+       **/
+      maxPriorityFeePerGas: AugmentedRpc<() => Observable<U256>>;
+      /**
+       * Returns true if client is actively mining new blocks.
+       **/
+      mining: AugmentedRpc<() => Observable<bool>>;
+      /**
+       * Returns id of new block filter.
+       **/
+      newBlockFilter: AugmentedRpc<() => Observable<U256>>;
+      /**
+       * Returns id of new filter.
+       **/
+      newFilter: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable<U256>>;
+      /**
+       * Returns id of new block filter.
+       **/
+      newPendingTransactionFilter: AugmentedRpc<() => Observable<U256>>;
+      /**
+       * Returns protocol version encoded as a string (quotes are necessary).
+       **/
+      protocolVersion: AugmentedRpc<() => Observable<u64>>;
+      /**
+       * Sends signed transaction, returning its hash.
+       **/
+      sendRawTransaction: AugmentedRpc<(bytes: Bytes | string | Uint8Array) => Observable<H256>>;
+      /**
+       * Sends transaction; will block waiting for signer to return the transaction hash
+       **/
+      sendTransaction: AugmentedRpc<(tx: EthTransactionRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array) => Observable<H256>>;
+      /**
+       * Used for submitting mining hashrate.
+       **/
+      submitHashrate: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array, hash: H256 | string | Uint8Array) => Observable<bool>>;
+      /**
+       * Used for submitting a proof-of-work solution.
+       **/
+      submitWork: AugmentedRpc<(nonce: H64 | string | Uint8Array, headerHash: H256 | string | Uint8Array, mixDigest: H256 | string | Uint8Array) => Observable<bool>>;
+      /**
+       * Subscribe to Eth subscription.
+       **/
+      subscribe: AugmentedRpc<(kind: EthSubKind | 'newHeads' | 'logs' | 'newPendingTransactions' | 'syncing' | number | Uint8Array, params?: EthSubParams | { None: any } | { Logs: any } | string | Uint8Array) => Observable<Null>>;
+      /**
+       * Returns an object with data about the sync status or false.
+       **/
+      syncing: AugmentedRpc<() => Observable<EthSyncStatus>>;
+      /**
+       * Uninstalls filter.
+       **/
+      uninstallFilter: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable<bool>>;
+    };
+    grandpa: {
+      /**
+       * Prove finality for the given block number, returning the Justification for the last block in the set.
+       **/
+      proveFinality: AugmentedRpc<(blockNumber: BlockNumber | AnyNumber | Uint8Array) => Observable<Option<EncodedFinalityProofs>>>;
+      /**
+       * Returns the state of the current best round state as well as the ongoing background rounds
+       **/
+      roundState: AugmentedRpc<() => Observable<ReportedRoundStates>>;
+      /**
+       * Subscribes to grandpa justifications
+       **/
+      subscribeJustifications: AugmentedRpc<() => Observable<JustificationNotification>>;
+    };
+    mmr: {
+      /**
+       * Generate MMR proof for the given block numbers.
+       **/
+      generateProof: AugmentedRpc<(blockNumbers: Vec<u64> | (u64 | AnyNumber | Uint8Array)[], bestKnownBlockNumber?: u64 | AnyNumber | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<MmrLeafBatchProof>>;
+      /**
+       * Get the MMR root hash for the current best block.
+       **/
+      root: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<MmrHash>>;
+      /**
+       * Verify an MMR proof
+       **/
+      verifyProof: AugmentedRpc<(proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable<bool>>;
+      /**
+       * Verify an MMR proof statelessly given an mmr_root
+       **/
+      verifyProofStateless: AugmentedRpc<(root: MmrHash | string | Uint8Array, proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable<bool>>;
+    };
+    net: {
+      /**
+       * Returns true if client is actively listening for network connections. Otherwise false.
+       **/
+      listening: AugmentedRpc<() => Observable<bool>>;
+      /**
+       * Returns number of peers connected to node.
+       **/
+      peerCount: AugmentedRpc<() => Observable<Text>>;
+      /**
+       * Returns protocol version.
+       **/
+      version: AugmentedRpc<() => Observable<Text>>;
+    };
+    offchain: {
+      /**
+       * Get offchain local storage under given key and prefix
+       **/
+      localStorageGet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array) => Observable<Option<Bytes>>>;
+      /**
+       * Set offchain local storage under given key and prefix
+       **/
+      localStorageSet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array, value: Bytes | string | Uint8Array) => Observable<Null>>;
+    };
+    payment: {
+      /**
+       * @deprecated Use `api.call.transactionPaymentApi.queryFeeDetails` instead
+       * Query the detailed fee of a given encoded extrinsic
+       **/
+      queryFeeDetails: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<FeeDetails>>;
+      /**
+       * @deprecated Use `api.call.transactionPaymentApi.queryInfo` instead
+       * Retrieves the fee information for an encoded extrinsic
+       **/
+      queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<RuntimeDispatchInfoV1>>;
+    };
+    povinfo: {
+      /**
+       * Estimate PoV size of encoded signed extrinsics
+       **/
+      estimateExtrinsicPoV: AugmentedRpc<(encodedXt: Vec<Bytes> | (Bytes | string | Uint8Array)[], at?: Hash | string | Uint8Array) => Observable<UpPovEstimateRpcPovInfo>>;
+    };
+    rpc: {
+      /**
+       * Retrieves the list of RPC methods that are exposed by the node
+       **/
+      methods: AugmentedRpc<() => Observable<RpcMethods>>;
+    };
+    state: {
+      /**
+       * Perform a call to a builtin on the chain
+       **/
+      call: AugmentedRpc<(method: Text | string, data: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<Bytes>>;
+      /**
+       * Retrieves the keys with prefix of a specific child storage
+       **/
+      getChildKeys: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
+      /**
+       * Returns proof of storage for child key entries at a specific block state.
+       **/
+      getChildReadProof: AugmentedRpc<(childStorageKey: PrefixedStorageKey | string | Uint8Array, keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable<ReadProof>>;
+      /**
+       * Retrieves the child storage for a key
+       **/
+      getChildStorage: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<StorageData>>;
+      /**
+       * Retrieves the child storage hash
+       **/
+      getChildStorageHash: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Hash>>;
+      /**
+       * Retrieves the child storage size
+       **/
+      getChildStorageSize: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<u64>>;
+      /**
+       * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys
+       * Retrieves the keys with a certain prefix
+       **/
+      getKeys: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
+      /**
+       * Returns the keys with prefix with pagination support.
+       **/
+      getKeysPaged: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
+      /**
+       * Returns the runtime metadata
+       **/
+      getMetadata: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<Metadata>>;
+      /**
+       * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys
+       * Returns the keys with prefix, leave empty to get all the keys (deprecated: Use getKeysPaged)
+       **/
+      getPairs: AugmentedRpc<(prefix: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<KeyValue>>>;
+      /**
+       * Returns proof of storage entries at a specific block state
+       **/
+      getReadProof: AugmentedRpc<(keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable<ReadProof>>;
+      /**
+       * Get the runtime version
+       **/
+      getRuntimeVersion: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<RuntimeVersion>>;
+      /**
+       * Retrieves the storage for a key
+       **/
+      getStorage: AugmentedRpc<<T = Codec>(key: StorageKey | string | Uint8Array | any, block?: Hash | Uint8Array | string) => Observable<T>>;
+      /**
+       * Retrieves the storage hash
+       **/
+      getStorageHash: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Hash>>;
+      /**
+       * Retrieves the storage size
+       **/
+      getStorageSize: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<u64>>;
+      /**
+       * Query historical storage entries (by key) starting from a start block
+       **/
+      queryStorage: AugmentedRpc<<T = Codec[]>(keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string) => Observable<[Hash, T][]>>;
+      /**
+       * Query storage entries (by key) starting at block hash given as the second parameter
+       **/
+      queryStorageAt: AugmentedRpc<<T = Codec[]>(keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: Hash | Uint8Array | string) => Observable<T>>;
+      /**
+       * Retrieves the runtime version via subscription
+       **/
+      subscribeRuntimeVersion: AugmentedRpc<() => Observable<RuntimeVersion>>;
+      /**
+       * Subscribes to storage changes for the provided keys
+       **/
+      subscribeStorage: AugmentedRpc<<T = Codec[]>(keys?: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[]) => Observable<T>>;
+      /**
+       * Provides a way to trace the re-execution of a single block
+       **/
+      traceBlock: AugmentedRpc<(block: Hash | string | Uint8Array, targets: Option<Text> | null | Uint8Array | Text | string, storageKeys: Option<Text> | null | Uint8Array | Text | string, methods: Option<Text> | null | Uint8Array | Text | string) => Observable<TraceBlockResponse>>;
+      /**
+       * Check current migration state
+       **/
+      trieMigrationStatus: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<MigrationStatusResult>>;
+    };
+    syncstate: {
+      /**
+       * Returns the json-serialized chainspec running the node, with a sync state.
+       **/
+      genSyncSpec: AugmentedRpc<(raw: bool | boolean | Uint8Array) => Observable<Json>>;
+    };
+    system: {
+      /**
+       * Retrieves the next accountIndex as available on the node
+       **/
+      accountNextIndex: AugmentedRpc<(accountId: AccountId | string | Uint8Array) => Observable<Index>>;
+      /**
+       * Adds the supplied directives to the current log filter
+       **/
+      addLogFilter: AugmentedRpc<(directives: Text | string) => Observable<Null>>;
+      /**
+       * Adds a reserved peer
+       **/
+      addReservedPeer: AugmentedRpc<(peer: Text | string) => Observable<Text>>;
+      /**
+       * Retrieves the chain
+       **/
+      chain: AugmentedRpc<() => Observable<Text>>;
+      /**
+       * Retrieves the chain type
+       **/
+      chainType: AugmentedRpc<() => Observable<ChainType>>;
+      /**
+       * Dry run an extrinsic at a given block
+       **/
+      dryRun: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ApplyExtrinsicResult>>;
+      /**
+       * Return health status of the node
+       **/
+      health: AugmentedRpc<() => Observable<Health>>;
+      /**
+       * The addresses include a trailing /p2p/ with the local PeerId, and are thus suitable to be passed to addReservedPeer or as a bootnode address for example
+       **/
+      localListenAddresses: AugmentedRpc<() => Observable<Vec<Text>>>;
+      /**
+       * Returns the base58-encoded PeerId of the node
+       **/
+      localPeerId: AugmentedRpc<() => Observable<Text>>;
+      /**
+       * Retrieves the node name
+       **/
+      name: AugmentedRpc<() => Observable<Text>>;
+      /**
+       * Returns current state of the network
+       **/
+      networkState: AugmentedRpc<() => Observable<NetworkState>>;
+      /**
+       * Returns the roles the node is running as
+       **/
+      nodeRoles: AugmentedRpc<() => Observable<Vec<NodeRole>>>;
+      /**
+       * Returns the currently connected peers
+       **/
+      peers: AugmentedRpc<() => Observable<Vec<PeerInfo>>>;
+      /**
+       * Get a custom set of properties as a JSON object, defined in the chain spec
+       **/
+      properties: AugmentedRpc<() => Observable<ChainProperties>>;
+      /**
+       * Remove a reserved peer
+       **/
+      removeReservedPeer: AugmentedRpc<(peerId: Text | string) => Observable<Text>>;
+      /**
+       * Returns the list of reserved peers
+       **/
+      reservedPeers: AugmentedRpc<() => Observable<Vec<Text>>>;
+      /**
+       * Resets the log filter to Substrate defaults
+       **/
+      resetLogFilter: AugmentedRpc<() => Observable<Null>>;
+      /**
+       * Returns the state of the syncing of the node
+       **/
+      syncState: AugmentedRpc<() => Observable<SyncState>>;
+      /**
+       * Retrieves the version of the node
+       **/
+      version: AugmentedRpc<() => Observable<Text>>;
+    };
+    unique: {
+      /**
+       * Get the amount of any user tokens owned by an account
+       **/
+      accountBalance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u32>>;
+      /**
+       * Get tokens owned by an account in a collection
+       **/
+      accountTokens: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<u32>>>;
+      /**
+       * Get the list of admin accounts of a collection
+       **/
+      adminlist: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletEvmAccountBasicCrossAccountIdRepr>>>;
+      /**
+       * Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor
+       **/
+      allowance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, sender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
+      /**
+       * Tells whether the given `owner` approves the `operator`.
+       **/
+      allowanceForAll: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<bool>>>;
+      /**
+       * Check if a user is allowed to operate within a collection
+       **/
+      allowed: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, at?: Hash | string | Uint8Array) => Observable<bool>>;
+      /**
+       * Get the list of accounts allowed to operate within a collection
+       **/
+      allowlist: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletEvmAccountBasicCrossAccountIdRepr>>>;
+      /**
+       * Get the amount of a specific token owned by an account
+       **/
+      balance: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u128>>;
+      /**
+       * Get a collection by the specified ID
+       **/
+      collectionById: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<UpDataStructsRpcCollection>>>;
+      /**
+       * Get collection properties, optionally limited to the provided keys
+       **/
+      collectionProperties: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, propertyKeys?: Option<Vec<Text>> | null | Uint8Array | Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsProperty>>>;
+      /**
+       * Get chain stats about collections
+       **/
+      collectionStats: AugmentedRpc<(at?: Hash | string | Uint8Array) => Observable<UpDataStructsCollectionStats>>;
+      /**
+       * Get tokens contained within a collection
+       **/
+      collectionTokens: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<u32>>>;
+      /**
+       * Get token constant metadata
+       **/
+      constMetadata: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Bytes>>;
+      /**
+       * Get effective collection limits
+       **/
+      effectiveCollectionLimits: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<UpDataStructsCollectionLimits>>>;
+      /**
+       * Get the last token ID created in a collection
+       **/
+      lastTokenId: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u32>>;
+      /**
+       * Get the number of blocks until sponsoring a transaction is available
+       **/
+      nextSponsored: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, account: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<u64>>>;
+      /**
+       * Get property permissions, optionally limited to the provided keys
+       **/
+      propertyPermissions: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, propertyKeys?: Option<Vec<Text>> | null | Uint8Array | Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsPropertyKeyPermission>>>;
+      /**
+       * Get tokens nested directly into the token
+       **/
+      tokenChildren: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsTokenChild>>>;
+      /**
+       * Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT
+       **/
+      tokenData: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys?: Option<Vec<Text>> | null | Uint8Array | Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<UpDataStructsTokenData>>;
+      /**
+       * Check if the token exists
+       **/
+      tokenExists: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<bool>>;
+      /**
+       * Get the token owner
+       **/
+      tokenOwner: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>>;
+      /**
+       * Returns 10 tokens owners in no particular order
+       **/
+      tokenOwners: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Vec<PalletEvmAccountBasicCrossAccountIdRepr>>>;
+      /**
+       * Get token properties, optionally limited to the provided keys
+       **/
+      tokenProperties: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys?: Option<Vec<Text>> | null | Uint8Array | Vec<Text> | (Text | string)[], at?: Hash | string | Uint8Array) => Observable<Vec<UpDataStructsProperty>>>;
+      /**
+       * Get the topmost token owner in the hierarchy of a possibly nested token
+       **/
+      topmostTokenOwner: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>>;
+      /**
+       * Get the total amount of pieces of an RFT
+       **/
+      totalPieces: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Option<u128>>>;
+      /**
+       * Get the amount of distinctive tokens present in a collection
+       **/
+      totalSupply: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<u32>>;
+      /**
+       * Get token variable metadata
+       **/
+      variableMetadata: AugmentedRpc<(collection: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, at?: Hash | string | Uint8Array) => Observable<Bytes>>;
+    };
+    web3: {
+      /**
+       * Returns current client version.
+       **/
+      clientVersion: AugmentedRpc<() => Observable<Text>>;
+      /**
+       * Returns sha3 of the given data
+       **/
+      sha3: AugmentedRpc<(data: Bytes | string | Uint8Array) => Observable<H256>>;
+    };
+  } // RpcInterface
+} // declare module
addedjs-packages/types/augment-api-runtime.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api-runtime.ts
@@ -0,0 +1,264 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/api-base/types/calls';
+
+import type { ApiTypes, AugmentedCall, DecoratedCallBase } from '@polkadot/api-base/types';
+import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u32, u64 } from '@polkadot/types-codec';
+import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
+import type { CheckInherentsResult, InherentData } from '@polkadot/types/interfaces/blockbuilder';
+import type { BlockHash } from '@polkadot/types/interfaces/chain';
+import type { AuthorityId } from '@polkadot/types/interfaces/consensus';
+import type { CollationInfo } from '@polkadot/types/interfaces/cumulus';
+import type { BlockV2, EthReceiptV3, EthTransactionStatus, TransactionV2 } from '@polkadot/types/interfaces/eth';
+import type { EvmAccount, EvmCallInfoV2, EvmCreateInfoV2 } from '@polkadot/types/interfaces/evm';
+import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
+import type { OpaqueMetadata } from '@polkadot/types/interfaces/metadata';
+import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment';
+import type { AccountId, Balance, Block, H160, H256, Header, Index, KeyTypeId, Permill, SlotDuration, Weight } from '@polkadot/types/interfaces/runtime';
+import type { RuntimeVersion } from '@polkadot/types/interfaces/state';
+import type { ApplyExtrinsicResult, DispatchError } from '@polkadot/types/interfaces/system';
+import type { TransactionSource, TransactionValidity } from '@polkadot/types/interfaces/txqueue';
+import type { IExtrinsic, Observable } from '@polkadot/types/types';
+
+export type __AugmentedCall<ApiType extends ApiTypes> = AugmentedCall<ApiType>;
+export type __DecoratedCallBase<ApiType extends ApiTypes> = DecoratedCallBase<ApiType>;
+
+declare module '@polkadot/api-base/types/calls' {
+  interface AugmentedCalls<ApiType extends ApiTypes> {
+    /** 0xbc9d89904f5b923f/1 */
+    accountNonceApi: {
+      /**
+       * The API to query account nonce (aka transaction index)
+       **/
+      accountNonce: AugmentedCall<ApiType, (accountId: AccountId | string | Uint8Array) => Observable<Index>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0xdd718d5cc53262d4/1 */
+    auraApi: {
+      /**
+       * Return the current set of authorities.
+       **/
+      authorities: AugmentedCall<ApiType, () => Observable<Vec<AuthorityId>>>;
+      /**
+       * Returns the slot duration for Aura.
+       **/
+      slotDuration: AugmentedCall<ApiType, () => Observable<SlotDuration>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0x40fe3ad401f8959a/6 */
+    blockBuilder: {
+      /**
+       * Apply the given extrinsic.
+       **/
+      applyExtrinsic: AugmentedCall<ApiType, (extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable<ApplyExtrinsicResult>>;
+      /**
+       * Check that the inherents are valid.
+       **/
+      checkInherents: AugmentedCall<ApiType, (block: Block | { header?: any; extrinsics?: any } | string | Uint8Array, data: InherentData | { data?: any } | string | Uint8Array) => Observable<CheckInherentsResult>>;
+      /**
+       * Finish the current block.
+       **/
+      finalizeBlock: AugmentedCall<ApiType, () => Observable<Header>>;
+      /**
+       * Generate inherent extrinsics.
+       **/
+      inherentExtrinsics: AugmentedCall<ApiType, (inherent: InherentData | { data?: any } | string | Uint8Array) => Observable<Vec<Extrinsic>>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0xea93e3f16f3d6962/2 */
+    collectCollationInfo: {
+      /**
+       * Collect information about a collation.
+       **/
+      collectCollationInfo: AugmentedCall<ApiType, (header: Header | { parentHash?: any; number?: any; stateRoot?: any; extrinsicsRoot?: any; digest?: any } | string | Uint8Array) => Observable<CollationInfo>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0xe65b00e46cedd0aa/2 */
+    convertTransactionRuntimeApi: {
+      /**
+       * Converts an Ethereum-style transaction to Extrinsic
+       **/
+      convertTransaction: AugmentedCall<ApiType, (transaction: TransactionV2 | { Legacy: any } | { EIP2930: any } | { EIP1559: any } | string | Uint8Array) => Observable<Extrinsic>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0xdf6acb689907609b/4 */
+    core: {
+      /**
+       * Execute the given block.
+       **/
+      executeBlock: AugmentedCall<ApiType, (block: Block | { header?: any; extrinsics?: any } | string | Uint8Array) => Observable<Null>>;
+      /**
+       * Initialize a block with the given header.
+       **/
+      initializeBlock: AugmentedCall<ApiType, (header: Header | { parentHash?: any; number?: any; stateRoot?: any; extrinsicsRoot?: any; digest?: any } | string | Uint8Array) => Observable<Null>>;
+      /**
+       * Returns the version of the runtime.
+       **/
+      version: AugmentedCall<ApiType, () => Observable<RuntimeVersion>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0x582211f65bb14b89/5 */
+    ethereumRuntimeRPCApi: {
+      /**
+       * Returns pallet_evm::Accounts by address.
+       **/
+      accountBasic: AugmentedCall<ApiType, (address: H160 | string | Uint8Array) => Observable<EvmAccount>>;
+      /**
+       * For a given account address, returns pallet_evm::AccountCodes.
+       **/
+      accountCodeAt: AugmentedCall<ApiType, (address: H160 | string | Uint8Array) => Observable<Bytes>>;
+      /**
+       * Returns the converted FindAuthor::find_author authority id.
+       **/
+      author: AugmentedCall<ApiType, () => Observable<H160>>;
+      /**
+       * Returns a frame_ethereum::call response. If `estimate` is true,
+       **/
+      call: AugmentedCall<ApiType, (from: H160 | string | Uint8Array, to: H160 | string | Uint8Array, data: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: U256 | AnyNumber | Uint8Array, maxFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option<Vec<ITuple<[H160, Vec<H256>]>>> | null | Uint8Array | Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => Observable<Result<EvmCallInfoV2, DispatchError>>>;
+      /**
+       * Returns runtime defined pallet_evm::ChainId.
+       **/
+      chainId: AugmentedCall<ApiType, () => Observable<u64>>;
+      /**
+       * Returns a frame_ethereum::call response. If `estimate` is true,
+       **/
+      create: AugmentedCall<ApiType, (from: H160 | string | Uint8Array, data: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: U256 | AnyNumber | Uint8Array, maxFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option<Vec<ITuple<[H160, Vec<H256>]>>> | null | Uint8Array | Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => Observable<Result<EvmCreateInfoV2, DispatchError>>>;
+      /**
+       * Return all the current data for a block in a single runtime call.
+       **/
+      currentAll: AugmentedCall<ApiType, () => Observable<ITuple<[Option<BlockV2>, Option<Vec<EthReceiptV3>>, Option<Vec<EthTransactionStatus>>]>>>;
+      /**
+       * Return the current block.
+       **/
+      currentBlock: AugmentedCall<ApiType, () => Observable<BlockV2>>;
+      /**
+       * Return the current receipt.
+       **/
+      currentReceipts: AugmentedCall<ApiType, () => Observable<Option<Vec<EthReceiptV3>>>>;
+      /**
+       * Return the current transaction status.
+       **/
+      currentTransactionStatuses: AugmentedCall<ApiType, () => Observable<Option<Vec<EthTransactionStatus>>>>;
+      /**
+       * Return the elasticity multiplier.
+       **/
+      elasticity: AugmentedCall<ApiType, () => Observable<Option<Permill>>>;
+      /**
+       * Receives a `Vec<OpaqueExtrinsic>` and filters all the ethereum transactions.
+       **/
+      extrinsicFilter: AugmentedCall<ApiType, (xts: Vec<Extrinsic> | (Extrinsic | IExtrinsic | string | Uint8Array)[]) => Observable<Vec<TransactionV2>>>;
+      /**
+       * Returns FixedGasPrice::min_gas_price
+       **/
+      gasPrice: AugmentedCall<ApiType, () => Observable<u256>>;
+      /**
+       * For a given account address and index, returns pallet_evm::AccountStorages.
+       **/
+      storageAt: AugmentedCall<ApiType, (address: H160 | string | Uint8Array, index: u256 | AnyNumber | Uint8Array) => Observable<H256>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0x37e397fc7c91f5e4/2 */
+    metadata: {
+      /**
+       * Returns the metadata of a runtime
+       **/
+      metadata: AugmentedCall<ApiType, () => Observable<OpaqueMetadata>>;
+      /**
+       * Returns the metadata at a given version.
+       **/
+      metadataAtVersion: AugmentedCall<ApiType, (version: u32 | AnyNumber | Uint8Array) => Observable<Option<OpaqueMetadata>>>;
+      /**
+       * Returns the supported metadata versions.
+       **/
+      metadataVersions: AugmentedCall<ApiType, () => Observable<Vec<u32>>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0xf78b278be53f454c/2 */
+    offchainWorkerApi: {
+      /**
+       * Starts the off-chain task for given block header.
+       **/
+      offchainWorker: AugmentedCall<ApiType, (header: Header | { parentHash?: any; number?: any; stateRoot?: any; extrinsicsRoot?: any; digest?: any } | string | Uint8Array) => Observable<Null>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0xab3c0572291feb8b/1 */
+    sessionKeys: {
+      /**
+       * Decode the given public session keys.
+       **/
+      decodeSessionKeys: AugmentedCall<ApiType, (encoded: Bytes | string | Uint8Array) => Observable<Option<Vec<ITuple<[Bytes, KeyTypeId]>>>>>;
+      /**
+       * Generate a set of session keys with optionally using the given seed.
+       **/
+      generateSessionKeys: AugmentedCall<ApiType, (seed: Option<Bytes> | null | Uint8Array | Bytes | string) => Observable<Bytes>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0xd2bc9897eed08f15/3 */
+    taggedTransactionQueue: {
+      /**
+       * Validate the transaction.
+       **/
+      validateTransaction: AugmentedCall<ApiType, (source: TransactionSource | 'InBlock' | 'Local' | 'External' | number | Uint8Array, tx: Extrinsic | IExtrinsic | string | Uint8Array, blockHash: BlockHash | string | Uint8Array) => Observable<TransactionValidity>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+    /** 0x37c8bb1350a9a2a8/4 */
+    transactionPaymentApi: {
+      /**
+       * The transaction fee details
+       **/
+      queryFeeDetails: AugmentedCall<ApiType, (uxt: Extrinsic | IExtrinsic | string | Uint8Array, len: u32 | AnyNumber | Uint8Array) => Observable<FeeDetails>>;
+      /**
+       * The transaction info
+       **/
+      queryInfo: AugmentedCall<ApiType, (uxt: Extrinsic | IExtrinsic | string | Uint8Array, len: u32 | AnyNumber | Uint8Array) => Observable<RuntimeDispatchInfo>>;
+      /**
+       * Query the output of the current LengthToFee given some input
+       **/
+      queryLengthToFee: AugmentedCall<ApiType, (length: u32 | AnyNumber | Uint8Array) => Observable<Balance>>;
+      /**
+       * Query the output of the current WeightToFee given some input
+       **/
+      queryWeightToFee: AugmentedCall<ApiType, (weight: Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => Observable<Balance>>;
+      /**
+       * Generic call
+       **/
+      [key: string]: DecoratedCallBase<ApiType>;
+    };
+  } // AugmentedCalls
+} // declare module
addedjs-packages/types/augment-api-tx.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api-tx.ts
@@ -0,0 +1,1250 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/api-base/types/submittable';
+
+import type { ApiTypes, AugmentedSubmittable, SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api-base/types';
+import type { Data } from '@polkadot/types';
+import type { Bytes, Compact, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
+import type { AnyNumber, IMethod, ITuple } from '@polkadot/types-codec/types';
+import type { AccountId32, Call, H160, H256, MultiAddress } from '@polkadot/types/interfaces/runtime';
+import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumLog, EthereumTransactionTransactionV2, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, OpalRuntimeOriginCaller, OpalRuntimeRuntimeCommonSessionKeys, OrmlVestingVestingSchedule, PalletConfigurationAppPromotionConfiguration, PalletDemocracyConviction, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityBitFlags, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistration, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmV3WeightLimit, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission } from '@polkadot/types/lookup';
+
+export type __AugmentedSubmittable = AugmentedSubmittable<() => unknown>;
+export type __SubmittableExtrinsic<ApiType extends ApiTypes> = SubmittableExtrinsic<ApiType>;
+export type __SubmittableExtrinsicFunction<ApiType extends ApiTypes> = SubmittableExtrinsicFunction<ApiType>;
+
+declare module '@polkadot/api-base/types/submittable' {
+  interface AugmentedSubmittables<ApiType extends ApiTypes> {
+    appPromotion: {
+      /**
+       * See [`Pallet::force_unstake`].
+       **/
+      forceUnstake: AugmentedSubmittable<(pendingBlocks: Vec<u32> | (u32 | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<u32>]>;
+      /**
+       * See [`Pallet::payout_stakers`].
+       **/
+      payoutStakers: AugmentedSubmittable<(stakersNumber: Option<u8> | null | Uint8Array | u8 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u8>]>;
+      /**
+       * See [`Pallet::set_admin_address`].
+       **/
+      setAdminAddress: AugmentedSubmittable<(admin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * See [`Pallet::sponsor_collection`].
+       **/
+      sponsorCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::sponsor_contract`].
+       **/
+      sponsorContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
+      /**
+       * See [`Pallet::stake`].
+       **/
+      stake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
+      /**
+       * See [`Pallet::stop_sponsoring_collection`].
+       **/
+      stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::stop_sponsoring_contract`].
+       **/
+      stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
+      /**
+       * See [`Pallet::unstake_all`].
+       **/
+      unstakeAll: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::unstake_partial`].
+       **/
+      unstakePartial: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    balances: {
+      /**
+       * See [`Pallet::force_set_balance`].
+       **/
+      forceSetBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
+      /**
+       * See [`Pallet::force_transfer`].
+       **/
+      forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress, Compact<u128>]>;
+      /**
+       * See [`Pallet::force_unreserve`].
+       **/
+      forceUnreserve: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, u128]>;
+      /**
+       * See [`Pallet::set_balance_deprecated`].
+       **/
+      setBalanceDeprecated: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact<u128> | AnyNumber | Uint8Array, oldReserved: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>, Compact<u128>]>;
+      /**
+       * See [`Pallet::transfer`].
+       **/
+      transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
+      /**
+       * See [`Pallet::transfer_all`].
+       **/
+      transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, bool]>;
+      /**
+       * See [`Pallet::transfer_allow_death`].
+       **/
+      transferAllowDeath: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
+      /**
+       * See [`Pallet::transfer_keep_alive`].
+       **/
+      transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
+      /**
+       * See [`Pallet::upgrade_accounts`].
+       **/
+      upgradeAccounts: AugmentedSubmittable<(who: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    collatorSelection: {
+      /**
+       * See [`Pallet::add_invulnerable`].
+       **/
+      addInvulnerable: AugmentedSubmittable<(updated: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
+      /**
+       * See [`Pallet::force_release_license`].
+       **/
+      forceReleaseLicense: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
+      /**
+       * See [`Pallet::get_license`].
+       **/
+      getLicense: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::offboard`].
+       **/
+      offboard: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::onboard`].
+       **/
+      onboard: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::release_license`].
+       **/
+      releaseLicense: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::remove_invulnerable`].
+       **/
+      removeInvulnerable: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    configuration: {
+      /**
+       * See [`Pallet::set_app_promotion_configuration_override`].
+       **/
+      setAppPromotionConfigurationOverride: AugmentedSubmittable<(configuration: PalletConfigurationAppPromotionConfiguration | { recalculationInterval?: any; pendingInterval?: any; intervalIncome?: any; maxStakersPerCalculation?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletConfigurationAppPromotionConfiguration]>;
+      /**
+       * See [`Pallet::set_collator_selection_desired_collators`].
+       **/
+      setCollatorSelectionDesiredCollators: AugmentedSubmittable<(max: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
+      /**
+       * See [`Pallet::set_collator_selection_kick_threshold`].
+       **/
+      setCollatorSelectionKickThreshold: AugmentedSubmittable<(threshold: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
+      /**
+       * See [`Pallet::set_collator_selection_license_bond`].
+       **/
+      setCollatorSelectionLicenseBond: AugmentedSubmittable<(amount: Option<u128> | null | Uint8Array | u128 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u128>]>;
+      /**
+       * See [`Pallet::set_min_gas_price_override`].
+       **/
+      setMinGasPriceOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
+      /**
+       * See [`Pallet::set_weight_to_fee_coefficient_override`].
+       **/
+      setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    council: {
+      /**
+       * See [`Pallet::close`].
+       **/
+      close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, SpWeightsWeightV2Weight, Compact<u32>]>;
+      /**
+       * See [`Pallet::disapprove_proposal`].
+       **/
+      disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
+      /**
+       * See [`Pallet::execute`].
+       **/
+      execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, Compact<u32>]>;
+      /**
+       * See [`Pallet::propose`].
+       **/
+      propose: AugmentedSubmittable<(threshold: Compact<u32> | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Call, Compact<u32>]>;
+      /**
+       * See [`Pallet::set_members`].
+       **/
+      setMembers: AugmentedSubmittable<(newMembers: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[], prime: Option<AccountId32> | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>, Option<AccountId32>, u32]>;
+      /**
+       * See [`Pallet::vote`].
+       **/
+      vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, bool]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    councilMembership: {
+      /**
+       * See [`Pallet::add_member`].
+       **/
+      addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::change_key`].
+       **/
+      changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::clear_prime`].
+       **/
+      clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::remove_member`].
+       **/
+      removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::reset_members`].
+       **/
+      resetMembers: AugmentedSubmittable<(members: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
+      /**
+       * See [`Pallet::set_prime`].
+       **/
+      setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::swap_member`].
+       **/
+      swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    cumulusXcm: {
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    democracy: {
+      /**
+       * See [`Pallet::blacklist`].
+       **/
+      blacklist: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, maybeRefIndex: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [H256, Option<u32>]>;
+      /**
+       * See [`Pallet::cancel_proposal`].
+       **/
+      cancelProposal: AugmentedSubmittable<(propIndex: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
+      /**
+       * See [`Pallet::cancel_referendum`].
+       **/
+      cancelReferendum: AugmentedSubmittable<(refIndex: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
+      /**
+       * See [`Pallet::clear_public_proposals`].
+       **/
+      clearPublicProposals: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::delegate`].
+       **/
+      delegate: AugmentedSubmittable<(to: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, conviction: PalletDemocracyConviction | 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x' | number | Uint8Array, balance: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletDemocracyConviction, u128]>;
+      /**
+       * See [`Pallet::emergency_cancel`].
+       **/
+      emergencyCancel: AugmentedSubmittable<(refIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::external_propose`].
+       **/
+      externalPropose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded]>;
+      /**
+       * See [`Pallet::external_propose_default`].
+       **/
+      externalProposeDefault: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded]>;
+      /**
+       * See [`Pallet::external_propose_majority`].
+       **/
+      externalProposeMajority: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded]>;
+      /**
+       * See [`Pallet::fast_track`].
+       **/
+      fastTrack: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, votingPeriod: u32 | AnyNumber | Uint8Array, delay: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, u32, u32]>;
+      /**
+       * See [`Pallet::propose`].
+       **/
+      propose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded, Compact<u128>]>;
+      /**
+       * See [`Pallet::remove_other_vote`].
+       **/
+      removeOtherVote: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, u32]>;
+      /**
+       * See [`Pallet::remove_vote`].
+       **/
+      removeVote: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::second`].
+       **/
+      second: AugmentedSubmittable<(proposal: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
+      /**
+       * See [`Pallet::set_metadata`].
+       **/
+      setMetadata: AugmentedSubmittable<(owner: PalletDemocracyMetadataOwner | { External: any } | { Proposal: any } | { Referendum: any } | string | Uint8Array, maybeHash: Option<H256> | null | Uint8Array | H256 | string) => SubmittableExtrinsic<ApiType>, [PalletDemocracyMetadataOwner, Option<H256>]>;
+      /**
+       * See [`Pallet::undelegate`].
+       **/
+      undelegate: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::unlock`].
+       **/
+      unlock: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::veto_external`].
+       **/
+      vetoExternal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
+      /**
+       * See [`Pallet::vote`].
+       **/
+      vote: AugmentedSubmittable<(refIndex: Compact<u32> | AnyNumber | Uint8Array, vote: PalletDemocracyVoteAccountVote | { Standard: any } | { Split: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, PalletDemocracyVoteAccountVote]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    dmpQueue: {
+      /**
+       * See [`Pallet::service_overweight`].
+       **/
+      serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u64, SpWeightsWeightV2Weight]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    ethereum: {
+      /**
+       * See [`Pallet::transact`].
+       **/
+      transact: AugmentedSubmittable<(transaction: EthereumTransactionTransactionV2 | { Legacy: any } | { EIP2930: any } | { EIP1559: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [EthereumTransactionTransactionV2]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    evm: {
+      /**
+       * See [`Pallet::call`].
+       **/
+      call: AugmentedSubmittable<(source: H160 | string | Uint8Array, target: H160 | string | Uint8Array, input: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, accessList: Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic<ApiType>, [H160, H160, Bytes, U256, u64, U256, Option<U256>, Option<U256>, Vec<ITuple<[H160, Vec<H256>]>>]>;
+      /**
+       * See [`Pallet::create`].
+       **/
+      create: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, accessList: Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic<ApiType>, [H160, Bytes, U256, u64, U256, Option<U256>, Option<U256>, Vec<ITuple<[H160, Vec<H256>]>>]>;
+      /**
+       * See [`Pallet::create2`].
+       **/
+      create2: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, salt: H256 | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, accessList: Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic<ApiType>, [H160, Bytes, H256, U256, u64, U256, Option<U256>, Option<U256>, Vec<ITuple<[H160, Vec<H256>]>>]>;
+      /**
+       * See [`Pallet::withdraw`].
+       **/
+      withdraw: AugmentedSubmittable<(address: H160 | string | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160, u128]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    evmContractHelpers: {
+      /**
+       * See [`Pallet::migrate_from_self_sponsoring`].
+       **/
+      migrateFromSelfSponsoring: AugmentedSubmittable<(addresses: Vec<H160> | (H160 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<H160>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    evmMigration: {
+      /**
+       * See [`Pallet::begin`].
+       **/
+      begin: AugmentedSubmittable<(address: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
+      /**
+       * See [`Pallet::finish`].
+       **/
+      finish: AugmentedSubmittable<(address: H160 | string | Uint8Array, code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160, Bytes]>;
+      /**
+       * See [`Pallet::insert_eth_logs`].
+       **/
+      insertEthLogs: AugmentedSubmittable<(logs: Vec<EthereumLog> | (EthereumLog | { address?: any; topics?: any; data?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<EthereumLog>]>;
+      /**
+       * See [`Pallet::insert_events`].
+       **/
+      insertEvents: AugmentedSubmittable<(events: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Bytes>]>;
+      /**
+       * See [`Pallet::remove_rmrk_data`].
+       **/
+      removeRmrkData: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::set_data`].
+       **/
+      setData: AugmentedSubmittable<(address: H160 | string | Uint8Array, data: Vec<ITuple<[H256, H256]>> | ([H256 | string | Uint8Array, H256 | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [H160, Vec<ITuple<[H256, H256]>>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    fellowshipCollective: {
+      /**
+       * See [`Pallet::add_member`].
+       **/
+      addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::cleanup_poll`].
+       **/
+      cleanupPoll: AugmentedSubmittable<(pollIndex: u32 | AnyNumber | Uint8Array, max: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
+      /**
+       * See [`Pallet::demote_member`].
+       **/
+      demoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::promote_member`].
+       **/
+      promoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::remove_member`].
+       **/
+      removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, minRank: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, u16]>;
+      /**
+       * See [`Pallet::vote`].
+       **/
+      vote: AugmentedSubmittable<(poll: u32 | AnyNumber | Uint8Array, aye: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, bool]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    fellowshipReferenda: {
+      /**
+       * See [`Pallet::cancel`].
+       **/
+      cancel: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::kill`].
+       **/
+      kill: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::nudge_referendum`].
+       **/
+      nudgeReferendum: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::one_fewer_deciding`].
+       **/
+      oneFewerDeciding: AugmentedSubmittable<(track: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u16]>;
+      /**
+       * See [`Pallet::place_decision_deposit`].
+       **/
+      placeDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::refund_decision_deposit`].
+       **/
+      refundDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::refund_submission_deposit`].
+       **/
+      refundSubmissionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::set_metadata`].
+       **/
+      setMetadata: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array, maybeHash: Option<H256> | null | Uint8Array | H256 | string) => SubmittableExtrinsic<ApiType>, [u32, Option<H256>]>;
+      /**
+       * See [`Pallet::submit`].
+       **/
+      submit: AugmentedSubmittable<(proposalOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, enactmentMoment: FrameSupportScheduleDispatchTime | { At: any } | { After: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [OpalRuntimeOriginCaller, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    foreignAssets: {
+      /**
+       * See [`Pallet::register_foreign_asset`].
+       **/
+      registerForeignAsset: AugmentedSubmittable<(owner: AccountId32 | string | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>;
+      /**
+       * See [`Pallet::update_foreign_asset`].
+       **/
+      updateForeignAsset: AugmentedSubmittable<(foreignAssetId: u32 | AnyNumber | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    identity: {
+      /**
+       * See [`Pallet::add_registrar`].
+       **/
+      addRegistrar: AugmentedSubmittable<(account: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::add_sub`].
+       **/
+      addSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Data]>;
+      /**
+       * See [`Pallet::cancel_request`].
+       **/
+      cancelRequest: AugmentedSubmittable<(regIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::clear_identity`].
+       **/
+      clearIdentity: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::force_insert_identities`].
+       **/
+      forceInsertIdentities: AugmentedSubmittable<(identities: Vec<ITuple<[AccountId32, PalletIdentityRegistration]>> | ([AccountId32 | string | Uint8Array, PalletIdentityRegistration | { judgements?: any; deposit?: any; info?: any } | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[AccountId32, PalletIdentityRegistration]>>]>;
+      /**
+       * See [`Pallet::force_remove_identities`].
+       **/
+      forceRemoveIdentities: AugmentedSubmittable<(identities: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
+      /**
+       * See [`Pallet::force_set_subs`].
+       **/
+      forceSetSubs: AugmentedSubmittable<(subs: Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>> | ([AccountId32 | string | Uint8Array, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]> | [u128 | AnyNumber | Uint8Array, Vec<ITuple<[AccountId32, Data]>> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]]])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>>]>;
+      /**
+       * See [`Pallet::kill_identity`].
+       **/
+      killIdentity: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::provide_judgement`].
+       **/
+      provideJudgement: AugmentedSubmittable<(regIndex: Compact<u32> | AnyNumber | Uint8Array, target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, judgement: PalletIdentityJudgement | { Unknown: any } | { FeePaid: any } | { Reasonable: any } | { KnownGood: any } | { OutOfDate: any } | { LowQuality: any } | { Erroneous: any } | string | Uint8Array, identity: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, MultiAddress, PalletIdentityJudgement, H256]>;
+      /**
+       * See [`Pallet::quit_sub`].
+       **/
+      quitSub: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::remove_sub`].
+       **/
+      removeSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::rename_sub`].
+       **/
+      renameSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Data]>;
+      /**
+       * See [`Pallet::request_judgement`].
+       **/
+      requestJudgement: AugmentedSubmittable<(regIndex: Compact<u32> | AnyNumber | Uint8Array, maxFee: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Compact<u128>]>;
+      /**
+       * See [`Pallet::set_account_id`].
+       **/
+      setAccountId: AugmentedSubmittable<(index: Compact<u32> | AnyNumber | Uint8Array, updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, MultiAddress]>;
+      /**
+       * See [`Pallet::set_fee`].
+       **/
+      setFee: AugmentedSubmittable<(index: Compact<u32> | AnyNumber | Uint8Array, fee: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Compact<u128>]>;
+      /**
+       * See [`Pallet::set_fields`].
+       **/
+      setFields: AugmentedSubmittable<(index: Compact<u32> | AnyNumber | Uint8Array, fields: PalletIdentityBitFlags) => SubmittableExtrinsic<ApiType>, [Compact<u32>, PalletIdentityBitFlags]>;
+      /**
+       * See [`Pallet::set_identity`].
+       **/
+      setIdentity: AugmentedSubmittable<(info: PalletIdentityIdentityInfo | { additional?: any; display?: any; legal?: any; web?: any; riot?: any; email?: any; pgpFingerprint?: any; image?: any; twitter?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletIdentityIdentityInfo]>;
+      /**
+       * See [`Pallet::set_subs`].
+       **/
+      setSubs: AugmentedSubmittable<(subs: Vec<ITuple<[AccountId32, Data]>> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[AccountId32, Data]>>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    inflation: {
+      /**
+       * See [`Pallet::start_inflation`].
+       **/
+      startInflation: AugmentedSubmittable<(inflationStartRelayBlock: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    maintenance: {
+      /**
+       * See [`Pallet::disable`].
+       **/
+      disable: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::enable`].
+       **/
+      enable: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    parachainInfo: {
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    parachainSystem: {
+      /**
+       * See [`Pallet::authorize_upgrade`].
+       **/
+      authorizeUpgrade: AugmentedSubmittable<(codeHash: H256 | string | Uint8Array, checkVersion: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, bool]>;
+      /**
+       * See [`Pallet::enact_authorized_upgrade`].
+       **/
+      enactAuthorizedUpgrade: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
+      /**
+       * See [`Pallet::set_validation_data`].
+       **/
+      setValidationData: AugmentedSubmittable<(data: CumulusPrimitivesParachainInherentParachainInherentData | { validationData?: any; relayChainState?: any; downwardMessages?: any; horizontalMessages?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [CumulusPrimitivesParachainInherentParachainInherentData]>;
+      /**
+       * See [`Pallet::sudo_send_upward_message`].
+       **/
+      sudoSendUpwardMessage: AugmentedSubmittable<(message: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    polkadotXcm: {
+      /**
+       * See [`Pallet::execute`].
+       **/
+      execute: AugmentedSubmittable<(message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array, maxWeight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedXcm, SpWeightsWeightV2Weight]>;
+      /**
+       * See [`Pallet::force_default_xcm_version`].
+       **/
+      forceDefaultXcmVersion: AugmentedSubmittable<(maybeXcmVersion: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
+      /**
+       * See [`Pallet::force_subscribe_version_notify`].
+       **/
+      forceSubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation]>;
+      /**
+       * See [`Pallet::force_suspension`].
+       **/
+      forceSuspension: AugmentedSubmittable<(suspended: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [bool]>;
+      /**
+       * See [`Pallet::force_unsubscribe_version_notify`].
+       **/
+      forceUnsubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation]>;
+      /**
+       * See [`Pallet::force_xcm_version`].
+       **/
+      forceXcmVersion: AugmentedSubmittable<(location: StagingXcmV3MultiLocation | { parents?: any; interior?: any } | string | Uint8Array, version: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmV3MultiLocation, u32]>;
+      /**
+       * See [`Pallet::limited_reserve_transfer_assets`].
+       **/
+      limitedReserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>;
+      /**
+       * See [`Pallet::limited_teleport_assets`].
+       **/
+      limitedTeleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>;
+      /**
+       * See [`Pallet::reserve_transfer_assets`].
+       **/
+      reserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>;
+      /**
+       * See [`Pallet::send`].
+       **/
+      send: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm]>;
+      /**
+       * See [`Pallet::teleport_assets`].
+       **/
+      teleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    preimage: {
+      /**
+       * See [`Pallet::note_preimage`].
+       **/
+      notePreimage: AugmentedSubmittable<(bytes: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
+      /**
+       * See [`Pallet::request_preimage`].
+       **/
+      requestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
+      /**
+       * See [`Pallet::unnote_preimage`].
+       **/
+      unnotePreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
+      /**
+       * See [`Pallet::unrequest_preimage`].
+       **/
+      unrequestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    scheduler: {
+      /**
+       * See [`Pallet::cancel`].
+       **/
+      cancel: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
+      /**
+       * See [`Pallet::cancel_named`].
+       **/
+      cancelNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [U8aFixed]>;
+      /**
+       * See [`Pallet::schedule`].
+       **/
+      schedule: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
+      /**
+       * See [`Pallet::schedule_after`].
+       **/
+      scheduleAfter: AugmentedSubmittable<(after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
+      /**
+       * See [`Pallet::schedule_named`].
+       **/
+      scheduleNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [U8aFixed, u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
+      /**
+       * See [`Pallet::schedule_named_after`].
+       **/
+      scheduleNamedAfter: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [U8aFixed, u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    session: {
+      /**
+       * See [`Pallet::purge_keys`].
+       **/
+      purgeKeys: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::set_keys`].
+       **/
+      setKeys: AugmentedSubmittable<(keys: OpalRuntimeRuntimeCommonSessionKeys | { aura?: any } | string | Uint8Array, proof: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [OpalRuntimeRuntimeCommonSessionKeys, Bytes]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    stateTrieMigration: {
+      /**
+       * See [`Pallet::continue_migrate`].
+       **/
+      continueMigrate: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array, realSizeUpper: u32 | AnyNumber | Uint8Array, witnessTask: PalletStateTrieMigrationMigrationTask | { progressTop?: any; progressChild?: any; size_?: any; topItems?: any; childItems?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletStateTrieMigrationMigrationLimits, u32, PalletStateTrieMigrationMigrationTask]>;
+      /**
+       * See [`Pallet::control_auto_migration`].
+       **/
+      controlAutoMigration: AugmentedSubmittable<(maybeConfig: Option<PalletStateTrieMigrationMigrationLimits> | null | Uint8Array | PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string) => SubmittableExtrinsic<ApiType>, [Option<PalletStateTrieMigrationMigrationLimits>]>;
+      /**
+       * See [`Pallet::force_set_progress`].
+       **/
+      forceSetProgress: AugmentedSubmittable<(progressTop: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array, progressChild: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletStateTrieMigrationProgress, PalletStateTrieMigrationProgress]>;
+      /**
+       * See [`Pallet::migrate_custom_child`].
+       **/
+      migrateCustomChild: AugmentedSubmittable<(root: Bytes | string | Uint8Array, childKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[], totalSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes, Vec<Bytes>, u32]>;
+      /**
+       * See [`Pallet::migrate_custom_top`].
+       **/
+      migrateCustomTop: AugmentedSubmittable<(keys: Vec<Bytes> | (Bytes | string | Uint8Array)[], witnessSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<Bytes>, u32]>;
+      /**
+       * See [`Pallet::set_signed_max_limits`].
+       **/
+      setSignedMaxLimits: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletStateTrieMigrationMigrationLimits]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    structure: {
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    sudo: {
+      /**
+       * See [`Pallet::set_key`].
+       **/
+      setKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::sudo`].
+       **/
+      sudo: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call]>;
+      /**
+       * See [`Pallet::sudo_as`].
+       **/
+      sudoAs: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Call]>;
+      /**
+       * See [`Pallet::sudo_unchecked_weight`].
+       **/
+      sudoUncheckedWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, SpWeightsWeightV2Weight]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    system: {
+      /**
+       * See [`Pallet::kill_prefix`].
+       **/
+      killPrefix: AugmentedSubmittable<(prefix: Bytes | string | Uint8Array, subkeys: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes, u32]>;
+      /**
+       * See [`Pallet::kill_storage`].
+       **/
+      killStorage: AugmentedSubmittable<(keys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Bytes>]>;
+      /**
+       * See [`Pallet::remark`].
+       **/
+      remark: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
+      /**
+       * See [`Pallet::remark_with_event`].
+       **/
+      remarkWithEvent: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
+      /**
+       * See [`Pallet::set_code`].
+       **/
+      setCode: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
+      /**
+       * See [`Pallet::set_code_without_checks`].
+       **/
+      setCodeWithoutChecks: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
+      /**
+       * See [`Pallet::set_heap_pages`].
+       **/
+      setHeapPages: AugmentedSubmittable<(pages: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u64]>;
+      /**
+       * See [`Pallet::set_storage`].
+       **/
+      setStorage: AugmentedSubmittable<(items: Vec<ITuple<[Bytes, Bytes]>> | ([Bytes | string | Uint8Array, Bytes | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[Bytes, Bytes]>>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    technicalCommittee: {
+      /**
+       * See [`Pallet::close`].
+       **/
+      close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, SpWeightsWeightV2Weight, Compact<u32>]>;
+      /**
+       * See [`Pallet::disapprove_proposal`].
+       **/
+      disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
+      /**
+       * See [`Pallet::execute`].
+       **/
+      execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, Compact<u32>]>;
+      /**
+       * See [`Pallet::propose`].
+       **/
+      propose: AugmentedSubmittable<(threshold: Compact<u32> | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Call, Compact<u32>]>;
+      /**
+       * See [`Pallet::set_members`].
+       **/
+      setMembers: AugmentedSubmittable<(newMembers: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[], prime: Option<AccountId32> | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>, Option<AccountId32>, u32]>;
+      /**
+       * See [`Pallet::vote`].
+       **/
+      vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, bool]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    technicalCommitteeMembership: {
+      /**
+       * See [`Pallet::add_member`].
+       **/
+      addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::change_key`].
+       **/
+      changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::clear_prime`].
+       **/
+      clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::remove_member`].
+       **/
+      removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::reset_members`].
+       **/
+      resetMembers: AugmentedSubmittable<(members: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
+      /**
+       * See [`Pallet::set_prime`].
+       **/
+      setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::swap_member`].
+       **/
+      swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    testUtils: {
+      /**
+       * See `Pallet::batch_all`.
+       **/
+      batchAll: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
+      /**
+       * See `Pallet::enable`.
+       **/
+      enable: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See `Pallet::inc_test_value`.
+       **/
+      incTestValue: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See `Pallet::just_take_fee`.
+       **/
+      justTakeFee: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See `Pallet::set_test_value`.
+       **/
+      setTestValue: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See `Pallet::set_test_value_and_rollback`.
+       **/
+      setTestValueAndRollback: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    timestamp: {
+      /**
+       * See [`Pallet::set`].
+       **/
+      set: AugmentedSubmittable<(now: Compact<u64> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u64>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    tokens: {
+      /**
+       * See [`Pallet::force_transfer`].
+       **/
+      forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress, PalletForeignAssetsAssetId, Compact<u128>]>;
+      /**
+       * See [`Pallet::set_balance`].
+       **/
+      setBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, newFree: Compact<u128> | AnyNumber | Uint8Array, newReserved: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, Compact<u128>, Compact<u128>]>;
+      /**
+       * See [`Pallet::transfer`].
+       **/
+      transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, Compact<u128>]>;
+      /**
+       * See [`Pallet::transfer_all`].
+       **/
+      transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, bool]>;
+      /**
+       * See [`Pallet::transfer_keep_alive`].
+       **/
+      transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, Compact<u128>]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    treasury: {
+      /**
+       * See [`Pallet::approve_proposal`].
+       **/
+      approveProposal: AugmentedSubmittable<(proposalId: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
+      /**
+       * See [`Pallet::propose_spend`].
+       **/
+      proposeSpend: AugmentedSubmittable<(value: Compact<u128> | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u128>, MultiAddress]>;
+      /**
+       * See [`Pallet::reject_proposal`].
+       **/
+      rejectProposal: AugmentedSubmittable<(proposalId: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
+      /**
+       * See [`Pallet::remove_approval`].
+       **/
+      removeApproval: AugmentedSubmittable<(proposalId: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
+      /**
+       * See [`Pallet::spend`].
+       **/
+      spend: AugmentedSubmittable<(amount: Compact<u128> | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u128>, MultiAddress]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    unique: {
+      /**
+       * See [`Pallet::add_collection_admin`].
+       **/
+      addCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newAdminId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * See [`Pallet::add_to_allow_list`].
+       **/
+      addToAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * See [`Pallet::approve`].
+       **/
+      approve: AugmentedSubmittable<(spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
+      /**
+       * See [`Pallet::approve_from`].
+       **/
+      approveFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, to: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
+      /**
+       * See [`Pallet::burn_from`].
+       **/
+      burnFrom: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32, u128]>;
+      /**
+       * See [`Pallet::burn_item`].
+       **/
+      burnItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
+      /**
+       * See [`Pallet::change_collection_owner`].
+       **/
+      changeCollectionOwner: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newOwner: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, AccountId32]>;
+      /**
+       * See [`Pallet::confirm_sponsorship`].
+       **/
+      confirmSponsorship: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::create_collection`].
+       **/
+      createCollection: AugmentedSubmittable<(collectionName: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<u16>, Vec<u16>, Bytes, UpDataStructsCollectionMode]>;
+      /**
+       * See [`Pallet::create_collection_ex`].
+       **/
+      createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; limits?: any; permissions?: any; tokenPropertyPermissions?: any; properties?: any; adminList?: any; pendingSponsor?: any; flags?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [UpDataStructsCreateCollectionData]>;
+      /**
+       * See [`Pallet::create_item`].
+       **/
+      createItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, data: UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCreateItemData]>;
+      /**
+       * See [`Pallet::create_multiple_items`].
+       **/
+      createMultipleItems: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemsData: Vec<UpDataStructsCreateItemData> | (UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, Vec<UpDataStructsCreateItemData>]>;
+      /**
+       * See [`Pallet::create_multiple_items_ex`].
+       **/
+      createMultipleItemsEx: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, data: UpDataStructsCreateItemExData | { NFT: any } | { Fungible: any } | { RefungibleMultipleItems: any } | { RefungibleMultipleOwners: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCreateItemExData]>;
+      /**
+       * See [`Pallet::delete_collection_properties`].
+       **/
+      deleteCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<Bytes>]>;
+      /**
+       * See [`Pallet::delete_token_properties`].
+       **/
+      deleteTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, u32, Vec<Bytes>]>;
+      /**
+       * See [`Pallet::destroy_collection`].
+       **/
+      destroyCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::force_repair_collection`].
+       **/
+      forceRepairCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::force_repair_item`].
+       **/
+      forceRepairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
+      /**
+       * See [`Pallet::remove_collection_admin`].
+       **/
+      removeCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, accountId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * See [`Pallet::remove_collection_sponsor`].
+       **/
+      removeCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::remove_from_allow_list`].
+       **/
+      removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+      /**
+       * See [`Pallet::repartition`].
+       **/
+      repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
+      /**
+       * See [`Pallet::set_allowance_for_all`].
+       **/
+      setAllowanceForAll: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
+      /**
+       * See [`Pallet::set_collection_limits`].
+       **/
+      setCollectionLimits: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionLimits | { accountTokenOwnershipLimit?: any; sponsoredDataSize?: any; sponsoredDataRateLimit?: any; tokenLimit?: any; sponsorTransferTimeout?: any; sponsorApproveTimeout?: any; ownerCanTransfer?: any; ownerCanDestroy?: any; transfersEnabled?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCollectionLimits]>;
+      /**
+       * See [`Pallet::set_collection_permissions`].
+       **/
+      setCollectionPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newPermission: UpDataStructsCollectionPermissions | { access?: any; mintMode?: any; nesting?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCollectionPermissions]>;
+      /**
+       * See [`Pallet::set_collection_properties`].
+       **/
+      setCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, properties: Vec<UpDataStructsProperty> | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsProperty>]>;
+      /**
+       * See [`Pallet::set_collection_sponsor`].
+       **/
+      setCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newSponsor: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, AccountId32]>;
+      /**
+       * See [`Pallet::set_token_properties`].
+       **/
+      setTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, properties: Vec<UpDataStructsProperty> | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, u32, Vec<UpDataStructsProperty>]>;
+      /**
+       * See [`Pallet::set_token_property_permissions`].
+       **/
+      setTokenPropertyPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyPermissions: Vec<UpDataStructsPropertyKeyPermission> | (UpDataStructsPropertyKeyPermission | { key?: any; permission?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsPropertyKeyPermission>]>;
+      /**
+       * See [`Pallet::set_transfers_enabled_flag`].
+       **/
+      setTransfersEnabledFlag: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, value: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, bool]>;
+      /**
+       * See [`Pallet::transfer`].
+       **/
+      transfer: AugmentedSubmittable<(recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
+      /**
+       * See [`Pallet::transfer_from`].
+       **/
+      transferFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    utility: {
+      /**
+       * See [`Pallet::as_derivative`].
+       **/
+      asDerivative: AugmentedSubmittable<(index: u16 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u16, Call]>;
+      /**
+       * See [`Pallet::batch`].
+       **/
+      batch: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
+      /**
+       * See [`Pallet::batch_all`].
+       **/
+      batchAll: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
+      /**
+       * See [`Pallet::dispatch_as`].
+       **/
+      dispatchAs: AugmentedSubmittable<(asOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [OpalRuntimeOriginCaller, Call]>;
+      /**
+       * See [`Pallet::force_batch`].
+       **/
+      forceBatch: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
+      /**
+       * See [`Pallet::with_weight`].
+       **/
+      withWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, SpWeightsWeightV2Weight]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    vesting: {
+      /**
+       * See [`Pallet::claim`].
+       **/
+      claim: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::claim_for`].
+       **/
+      claimFor: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
+      /**
+       * See [`Pallet::update_vesting_schedules`].
+       **/
+      updateVestingSchedules: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, vestingSchedules: Vec<OrmlVestingVestingSchedule> | (OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [MultiAddress, Vec<OrmlVestingVestingSchedule>]>;
+      /**
+       * See [`Pallet::vested_transfer`].
+       **/
+      vestedTransfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, schedule: OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, OrmlVestingVestingSchedule]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    xcmpQueue: {
+      /**
+       * See [`Pallet::resume_xcm_execution`].
+       **/
+      resumeXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::service_overweight`].
+       **/
+      serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u64, SpWeightsWeightV2Weight]>;
+      /**
+       * See [`Pallet::suspend_xcm_execution`].
+       **/
+      suspendXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
+      /**
+       * See [`Pallet::update_drop_threshold`].
+       **/
+      updateDropThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::update_resume_threshold`].
+       **/
+      updateResumeThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::update_suspend_threshold`].
+       **/
+      updateSuspendThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
+      /**
+       * See [`Pallet::update_threshold_weight`].
+       **/
+      updateThresholdWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [SpWeightsWeightV2Weight]>;
+      /**
+       * See [`Pallet::update_weight_restrict_decay`].
+       **/
+      updateWeightRestrictDecay: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [SpWeightsWeightV2Weight]>;
+      /**
+       * See [`Pallet::update_xcmp_max_individual_weight`].
+       **/
+      updateXcmpMaxIndividualWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [SpWeightsWeightV2Weight]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+    xTokens: {
+      /**
+       * See [`Pallet::transfer`].
+       **/
+      transfer: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletForeignAssetsAssetId, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
+      /**
+       * See [`Pallet::transfer_multiasset`].
+       **/
+      transferMultiasset: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
+      /**
+       * See [`Pallet::transfer_multiassets`].
+       **/
+      transferMultiassets: AugmentedSubmittable<(assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiAssets, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
+      /**
+       * See [`Pallet::transfer_multiasset_with_fee`].
+       **/
+      transferMultiassetWithFee: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, fee: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
+      /**
+       * See [`Pallet::transfer_multicurrencies`].
+       **/
+      transferMulticurrencies: AugmentedSubmittable<(currencies: Vec<ITuple<[PalletForeignAssetsAssetId, u128]>> | ([PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, u128 | AnyNumber | Uint8Array])[], feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[PalletForeignAssetsAssetId, u128]>>, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
+      /**
+       * See [`Pallet::transfer_with_fee`].
+       **/
+      transferWithFee: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, fee: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletForeignAssetsAssetId, u128, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
+      /**
+       * Generic tx
+       **/
+      [key: string]: SubmittableExtrinsicFunction<ApiType>;
+    };
+  } // AugmentedSubmittables
+} // declare module
addedjs-packages/types/augment-api.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-api.ts
@@ -0,0 +1,10 @@
+// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
+/* eslint-disable */
+
+import './augment-api-consts.js';
+import './augment-api-errors.js';
+import './augment-api-events.js';
+import './augment-api-query.js';
+import './augment-api-tx.js';
+import './augment-api-rpc.js';
+import './augment-api-runtime.js';
addedjs-packages/types/augment-types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/augment-types.ts
@@ -0,0 +1,1587 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/types/types/registry';
+
+import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesIdAmount, PalletBalancesReasons, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCollatorSelectionHoldReason, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCodeMetadata, PalletEvmCoderSubstrateError, PalletEvmContractHelpersCall, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletGovOriginsOrigin, PalletIdentityBitFlags, PalletIdentityCall, PalletIdentityError, PalletIdentityEvent, PalletIdentityIdentityField, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletRankedCollectiveCall, PalletRankedCollectiveError, PalletRankedCollectiveEvent, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletReferendaCall, PalletReferendaCurve, PalletReferendaDecidingStatus, PalletReferendaDeposit, PalletReferendaError, PalletReferendaEvent, PalletReferendaReferendumInfo, PalletReferendaReferendumStatus, PalletReferendaTrackInfo, PalletRefungibleError, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduled, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStateTrieMigrationCall, PalletStateTrieMigrationError, PalletStateTrieMigrationEvent, PalletStateTrieMigrationMigrationCompute, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, ParachainInfoCall, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5AbridgedHrmpChannel, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, PolkadotPrimitivesVstagingAsyncBackingParams, SpArithmeticArithmeticError, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmDoubleEncoded, StagingXcmV2BodyId, StagingXcmV2BodyPart, StagingXcmV2Instruction, StagingXcmV2Junction, StagingXcmV2MultiAsset, StagingXcmV2MultiLocation, StagingXcmV2MultiassetAssetId, StagingXcmV2MultiassetAssetInstance, StagingXcmV2MultiassetFungibility, StagingXcmV2MultiassetMultiAssetFilter, StagingXcmV2MultiassetMultiAssets, StagingXcmV2MultiassetWildFungibility, StagingXcmV2MultiassetWildMultiAsset, StagingXcmV2MultilocationJunctions, StagingXcmV2NetworkId, StagingXcmV2OriginKind, StagingXcmV2Response, StagingXcmV2TraitsError, StagingXcmV2WeightLimit, StagingXcmV2Xcm, StagingXcmV3Instruction, StagingXcmV3Junction, StagingXcmV3JunctionBodyId, StagingXcmV3JunctionBodyPart, StagingXcmV3JunctionNetworkId, StagingXcmV3Junctions, StagingXcmV3MaybeErrorCode, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetAssetId, StagingXcmV3MultiassetAssetInstance, StagingXcmV3MultiassetFungibility, StagingXcmV3MultiassetMultiAssetFilter, StagingXcmV3MultiassetMultiAssets, StagingXcmV3MultiassetWildFungibility, StagingXcmV3MultiassetWildMultiAsset, StagingXcmV3PalletInfo, StagingXcmV3QueryResponseInfo, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3WeightLimit, StagingXcmV3Xcm, StagingXcmVersionedAssetId, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedResponse, StagingXcmVersionedXcm, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue } from './default';
+import type { Data, StorageKey } from '@polkadot/types';
+import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, ISize, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, isize, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
+import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
+import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations';
+import type { RawAuraPreDigest } from '@polkadot/types/interfaces/aura';
+import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author';
+import type { UncleEntryItem } from '@polkadot/types/interfaces/authorship';
+import type { AllowedSlots, BabeAuthorityWeight, BabeBlockWeight, BabeEpochConfiguration, BabeEquivocationProof, BabeGenesisConfiguration, BabeGenesisConfigurationV1, BabeWeight, Epoch, EpochAuthorship, MaybeRandomness, MaybeVrf, NextConfigDescriptor, NextConfigDescriptorV1, OpaqueKeyOwnershipProof, Randomness, RawBabePreDigest, RawBabePreDigestCompat, RawBabePreDigestPrimary, RawBabePreDigestPrimaryTo159, RawBabePreDigestSecondaryPlain, RawBabePreDigestSecondaryTo159, RawBabePreDigestSecondaryVRF, RawBabePreDigestTo159, SlotNumber, VrfData, VrfOutput, VrfProof } from '@polkadot/types/interfaces/babe';
+import type { AccountData, BalanceLock, BalanceLockTo212, BalanceStatus, Reasons, ReserveData, ReserveIdentifier, VestingSchedule, WithdrawReasons } from '@polkadot/types/interfaces/balances';
+import type { BeefyAuthoritySet, BeefyCommitment, BeefyEquivocationProof, BeefyId, BeefyNextAuthoritySet, BeefyPayload, BeefyPayloadId, BeefySignedCommitment, BeefyVoteMessage, MmrRootHash, ValidatorSet, ValidatorSetId } from '@polkadot/types/interfaces/beefy';
+import type { BenchmarkBatch, BenchmarkConfig, BenchmarkList, BenchmarkMetadata, BenchmarkParameter, BenchmarkResult } from '@polkadot/types/interfaces/benchmark';
+import type { CheckInherentsResult, InherentData, InherentIdentifier } from '@polkadot/types/interfaces/blockbuilder';
+import type { BridgeMessageId, BridgedBlockHash, BridgedBlockNumber, BridgedHeader, CallOrigin, ChainId, DeliveredMessages, DispatchFeePayment, InboundLaneData, InboundRelayer, InitializationData, LaneId, MessageData, MessageKey, MessageNonce, MessagesDeliveryProofOf, MessagesProofOf, OperatingMode, OutboundLaneData, OutboundMessageFee, OutboundPayload, Parameter, RelayerId, UnrewardedRelayer, UnrewardedRelayersState } from '@polkadot/types/interfaces/bridges';
+import type { BlockHash } from '@polkadot/types/interfaces/chain';
+import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate';
+import type { StatementKind } from '@polkadot/types/interfaces/claims';
+import type { CollectiveOrigin, MemberCount, ProposalIndex, Votes, VotesTo230 } from '@polkadot/types/interfaces/collective';
+import type { AuthorityId, RawVRFOutput } from '@polkadot/types/interfaces/consensus';
+import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractExecResultU64, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractInstantiateResultU64, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts';
+import type { ContractConstructorSpecLatest, ContractConstructorSpecV0, ContractConstructorSpecV1, ContractConstructorSpecV2, ContractConstructorSpecV3, ContractConstructorSpecV4, ContractContractSpecV0, ContractContractSpecV1, ContractContractSpecV2, ContractContractSpecV3, ContractContractSpecV4, ContractCryptoHasher, ContractDiscriminant, ContractDisplayName, ContractEnvironmentV4, ContractEventParamSpecLatest, ContractEventParamSpecV0, ContractEventParamSpecV2, ContractEventSpecLatest, ContractEventSpecV0, ContractEventSpecV1, ContractEventSpecV2, ContractLayoutArray, ContractLayoutCell, ContractLayoutEnum, ContractLayoutHash, ContractLayoutHashingStrategy, ContractLayoutKey, ContractLayoutStruct, ContractLayoutStructField, ContractMessageParamSpecLatest, ContractMessageParamSpecV0, ContractMessageParamSpecV2, ContractMessageSpecLatest, ContractMessageSpecV0, ContractMessageSpecV1, ContractMessageSpecV2, ContractMessageSpecV3, ContractMetadata, ContractMetadataLatest, ContractMetadataV0, ContractMetadataV1, ContractMetadataV2, ContractMetadataV3, ContractMetadataV4, ContractProject, ContractProjectContract, ContractProjectInfo, ContractProjectSource, ContractProjectV0, ContractSelector, ContractStorageLayout, ContractTypeSpec } from '@polkadot/types/interfaces/contractsAbi';
+import type { FundIndex, FundInfo, LastContribution, TrieIndex } from '@polkadot/types/interfaces/crowdloan';
+import type { CollationInfo, CollationInfoV1, ConfigData, MessageId, OverweightIndex, PageCounter, PageIndexData } from '@polkadot/types/interfaces/cumulus';
+import type { AccountVote, AccountVoteSplit, AccountVoteStandard, Conviction, Delegations, PreimageStatus, PreimageStatusAvailable, PriorLock, PropIndex, Proposal, ProxyState, ReferendumIndex, ReferendumInfo, ReferendumInfoFinished, ReferendumInfoTo239, ReferendumStatus, Tally, Voting, VotingDelegating, VotingDirect, VotingDirectVote } from '@polkadot/types/interfaces/democracy';
+import type { BlockStats } from '@polkadot/types/interfaces/dev';
+import type { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, VoteThreshold, VoterInfo } from '@polkadot/types/interfaces/elections';
+import type { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine';
+import type { BlockV0, BlockV1, BlockV2, EIP1559Transaction, EIP2930Transaction, EthAccessList, EthAccessListItem, EthAccount, EthAddress, EthBlock, EthBloom, EthCallRequest, EthFeeHistory, EthFilter, EthFilterAddress, EthFilterChanges, EthFilterTopic, EthFilterTopicEntry, EthFilterTopicInner, EthHeader, EthLog, EthReceipt, EthReceiptV0, EthReceiptV3, EthRichBlock, EthRichHeader, EthStorageProof, EthSubKind, EthSubParams, EthSubResult, EthSyncInfo, EthSyncStatus, EthTransaction, EthTransactionAction, EthTransactionCondition, EthTransactionRequest, EthTransactionSignature, EthTransactionStatus, EthWork, EthereumAccountId, EthereumAddress, EthereumLookupSource, EthereumSignature, LegacyTransaction, TransactionV0, TransactionV1, TransactionV2 } from '@polkadot/types/interfaces/eth';
+import type { EvmAccount, EvmCallInfo, EvmCallInfoV2, EvmCreateInfo, EvmCreateInfoV2, EvmLog, EvmVicinity, EvmWeightInfo, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed } from '@polkadot/types/interfaces/evm';
+import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV4, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics';
+import type { FungiblesAccessError } from '@polkadot/types/interfaces/fungibles';
+import type { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset';
+import type { ActiveGilt, ActiveGiltsTotal, ActiveIndex, GiltBid } from '@polkadot/types/interfaces/gilt';
+import type { AuthorityIndex, AuthorityList, AuthoritySet, AuthoritySetChange, AuthoritySetChanges, AuthorityWeight, DelayKind, DelayKindBest, EncodedFinalityProofs, ForkTreePendingChange, ForkTreePendingChangeNode, GrandpaCommit, GrandpaEquivocation, GrandpaEquivocationProof, GrandpaEquivocationValue, GrandpaJustification, GrandpaPrecommit, GrandpaPrevote, GrandpaSignedPrecommit, JustificationNotification, KeyOwnerProof, NextAuthority, PendingChange, PendingPause, PendingResume, Precommits, Prevotes, ReportedRoundStates, RoundState, SetId, StoredPendingChange, StoredState } from '@polkadot/types/interfaces/grandpa';
+import type { IdentityFields, IdentityInfo, IdentityInfoAdditional, IdentityInfoTo198, IdentityJudgement, RegistrarIndex, RegistrarInfo, Registration, RegistrationJudgement, RegistrationTo198 } from '@polkadot/types/interfaces/identity';
+import type { AuthIndex, AuthoritySignature, Heartbeat, HeartbeatTo244, OpaqueMultiaddr, OpaqueNetworkState, OpaquePeerId } from '@polkadot/types/interfaces/imOnline';
+import type { CallIndex, LotteryConfig } from '@polkadot/types/interfaces/lottery';
+import type { CustomMetadata15, CustomValueMetadata15, ErrorMetadataLatest, ErrorMetadataV10, ErrorMetadataV11, ErrorMetadataV12, ErrorMetadataV13, ErrorMetadataV14, ErrorMetadataV9, EventMetadataLatest, EventMetadataV10, EventMetadataV11, EventMetadataV12, EventMetadataV13, EventMetadataV14, EventMetadataV9, ExtrinsicMetadataLatest, ExtrinsicMetadataV11, ExtrinsicMetadataV12, ExtrinsicMetadataV13, ExtrinsicMetadataV14, ExtrinsicMetadataV15, FunctionArgumentMetadataLatest, FunctionArgumentMetadataV10, FunctionArgumentMetadataV11, FunctionArgumentMetadataV12, FunctionArgumentMetadataV13, FunctionArgumentMetadataV14, FunctionArgumentMetadataV9, FunctionMetadataLatest, FunctionMetadataV10, FunctionMetadataV11, FunctionMetadataV12, FunctionMetadataV13, FunctionMetadataV14, FunctionMetadataV9, MetadataAll, MetadataLatest, MetadataV10, MetadataV11, MetadataV12, MetadataV13, MetadataV14, MetadataV15, MetadataV9, ModuleConstantMetadataV10, ModuleConstantMetadataV11, ModuleConstantMetadataV12, ModuleConstantMetadataV13, ModuleConstantMetadataV9, ModuleMetadataV10, ModuleMetadataV11, ModuleMetadataV12, ModuleMetadataV13, ModuleMetadataV9, OpaqueMetadata, OuterEnums15, PalletCallMetadataLatest, PalletCallMetadataV14, PalletConstantMetadataLatest, PalletConstantMetadataV14, PalletErrorMetadataLatest, PalletErrorMetadataV14, PalletEventMetadataLatest, PalletEventMetadataV14, PalletMetadataLatest, PalletMetadataV14, PalletMetadataV15, PalletStorageMetadataLatest, PalletStorageMetadataV14, PortableType, PortableTypeV14, RuntimeApiMetadataLatest, RuntimeApiMetadataV15, RuntimeApiMethodMetadataV15, RuntimeApiMethodParamMetadataV15, SignedExtensionMetadataLatest, SignedExtensionMetadataV14, StorageEntryMetadataLatest, StorageEntryMetadataV10, StorageEntryMetadataV11, StorageEntryMetadataV12, StorageEntryMetadataV13, StorageEntryMetadataV14, StorageEntryMetadataV9, StorageEntryModifierLatest, StorageEntryModifierV10, StorageEntryModifierV11, StorageEntryModifierV12, StorageEntryModifierV13, StorageEntryModifierV14, StorageEntryModifierV9, StorageEntryTypeLatest, StorageEntryTypeV10, StorageEntryTypeV11, StorageEntryTypeV12, StorageEntryTypeV13, StorageEntryTypeV14, StorageEntryTypeV9, StorageHasher, StorageHasherV10, StorageHasherV11, StorageHasherV12, StorageHasherV13, StorageHasherV14, StorageHasherV9, StorageMetadataV10, StorageMetadataV11, StorageMetadataV12, StorageMetadataV13, StorageMetadataV9 } from '@polkadot/types/interfaces/metadata';
+import type { MmrBatchProof, MmrEncodableOpaqueLeaf, MmrError, MmrHash, MmrLeafBatchProof, MmrLeafIndex, MmrLeafProof, MmrNodeIndex, MmrProof } from '@polkadot/types/interfaces/mmr';
+import type { NftCollectionId, NftItemId } from '@polkadot/types/interfaces/nfts';
+import type { NpApiError, NpPoolId } from '@polkadot/types/interfaces/nompools';
+import type { StorageKind } from '@polkadot/types/interfaces/offchain';
+import type { DeferredOffenceOf, Kind, OffenceDetails, Offender, OpaqueTimeSlot, ReportIdOf, Reporter } from '@polkadot/types/interfaces/offences';
+import type { AbridgedCandidateReceipt, AbridgedHostConfiguration, AbridgedHrmpChannel, AssignmentId, AssignmentKind, AttestedCandidate, AuctionIndex, AuthorityDiscoveryId, AvailabilityBitfield, AvailabilityBitfieldRecord, BackedCandidate, Bidder, BufferedSessionChange, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateHash, CandidateInfo, CandidatePendingAvailability, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreAssignment, CoreIndex, CoreOccupied, CoreState, DisputeLocation, DisputeProof, DisputeResult, DisputeState, DisputeStatement, DisputeStatementSet, DisputesTimeSlot, DoubleVoteReport, DownwardMessage, ExecutorParam, ExecutorParams, ExecutorParamsHash, ExplicitDisputeStatement, GlobalValidationData, GlobalValidationSchedule, GroupIndex, GroupRotationInfo, HeadData, HostConfiguration, HrmpChannel, HrmpChannelId, HrmpOpenChannelRequest, InboundDownwardMessage, InboundHrmpMessage, InboundHrmpMessages, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, InvalidDisputeStatementKind, LeasePeriod, LeasePeriodOf, LocalValidationData, MessageIngestionType, MessageQueueChain, MessagingStateSnapshot, MessagingStateSnapshotEgressEntry, MultiDisputeStatementSet, NewBidder, OccupiedCore, OccupiedCoreAssumption, OldV1SessionInfo, OutboundHrmpMessage, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle, ParaPastCodeMeta, ParaScheduling, ParaValidatorIndex, ParachainDispatchOrigin, ParachainInherentData, ParachainProposal, ParachainsInherentData, ParathreadClaim, ParathreadClaimQueue, ParathreadEntry, PendingSlashes, PersistedValidationData, PvfCheckStatement, PvfExecTimeoutKind, PvfPrepTimeoutKind, QueuedParathread, RegisteredParachainInfo, RelayBlockNumber, RelayChainBlockNumber, RelayChainHash, RelayHash, Remark, ReplacementTimes, Retriable, ScheduledCore, Scheduling, ScrapedOnChainVotes, ServiceQuality, SessionInfo, SessionInfoValidatorGroup, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SigningContext, SlashingOffenceKind, SlotRange, SlotRange10, Statement, SubId, SystemInherentData, TransientValidationData, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidationData, ValidationDataType, ValidationFunctionParams, ValidatorSignature, ValidityAttestation, VecInboundHrmpMessage, WinnersData, WinnersData10, WinnersDataTuple, WinnersDataTuple10, WinningData, WinningData10, WinningDataEntry } from '@polkadot/types/interfaces/parachains';
+import type { FeeDetails, InclusionFee, RuntimeDispatchInfo, RuntimeDispatchInfoV1, RuntimeDispatchInfoV2 } from '@polkadot/types/interfaces/payment';
+import type { Approvals } from '@polkadot/types/interfaces/poll';
+import type { ProxyAnnouncement, ProxyDefinition, ProxyType } from '@polkadot/types/interfaces/proxy';
+import type { AccountStatus, AccountValidity } from '@polkadot/types/interfaces/purchase';
+import type { ActiveRecovery, RecoveryConfig } from '@polkadot/types/interfaces/recovery';
+import type { RpcMethods } from '@polkadot/types/interfaces/rpc';
+import type { AccountId, AccountId20, AccountId32, AccountId33, AccountIdOf, AccountIndex, Address, AssetId, Balance, BalanceOf, Block, BlockNumber, BlockNumberFor, BlockNumberOf, Call, CallHash, CallHashOf, ChangesTrieConfiguration, ChangesTrieSignal, CodecHash, Consensus, ConsensusEngineId, CrateVersion, Digest, DigestItem, EncodedJustification, ExtrinsicsWeight, Fixed128, Fixed64, FixedI128, FixedI64, FixedU128, FixedU64, H1024, H128, H160, H2048, H256, H32, H512, H64, Hash, Header, HeaderPartial, I32F32, Index, IndicesLookupSource, Justification, Justifications, KeyTypeId, KeyValue, LockIdentifier, LookupSource, LookupTarget, ModuleId, Moment, MultiAddress, MultiSigner, OpaqueCall, Origin, OriginCaller, PalletId, PalletVersion, PalletsOrigin, Pays, PerU16, Perbill, Percent, Permill, Perquintill, Phantom, PhantomData, PreRuntime, Releases, RuntimeCall, RuntimeDbWeight, RuntimeEvent, Seal, SealV0, SignedBlock, SignedBlockWithJustification, SignedBlockWithJustifications, Slot, SlotDuration, StorageData, StorageInfo, StorageProof, TransactionInfo, TransactionLongevity, TransactionPriority, TransactionStorageProof, TransactionTag, U32F32, ValidatorId, ValidatorIdOf, Weight, WeightMultiplier, WeightV0, WeightV1, WeightV2 } from '@polkadot/types/interfaces/runtime';
+import type { Si0Field, Si0LookupTypeId, Si0Path, Si0Type, Si0TypeDef, Si0TypeDefArray, Si0TypeDefBitSequence, Si0TypeDefCompact, Si0TypeDefComposite, Si0TypeDefPhantom, Si0TypeDefPrimitive, Si0TypeDefSequence, Si0TypeDefTuple, Si0TypeDefVariant, Si0TypeParameter, Si0Variant, Si1Field, Si1LookupTypeId, Si1Path, Si1Type, Si1TypeDef, Si1TypeDefArray, Si1TypeDefBitSequence, Si1TypeDefCompact, Si1TypeDefComposite, Si1TypeDefPrimitive, Si1TypeDefSequence, Si1TypeDefTuple, Si1TypeDefVariant, Si1TypeParameter, Si1Variant, SiField, SiLookupTypeId, SiPath, SiType, SiTypeDef, SiTypeDefArray, SiTypeDefBitSequence, SiTypeDefCompact, SiTypeDefComposite, SiTypeDefPrimitive, SiTypeDefSequence, SiTypeDefTuple, SiTypeDefVariant, SiTypeParameter, SiVariant } from '@polkadot/types/interfaces/scaleInfo';
+import type { Period, Priority, SchedulePeriod, SchedulePriority, Scheduled, ScheduledTo254, TaskAddress } from '@polkadot/types/interfaces/scheduler';
+import type { BeefyKey, FullIdentification, IdentificationTuple, Keys, MembershipProof, SessionIndex, SessionKeys1, SessionKeys10, SessionKeys10B, SessionKeys2, SessionKeys3, SessionKeys4, SessionKeys5, SessionKeys6, SessionKeys6B, SessionKeys7, SessionKeys7B, SessionKeys8, SessionKeys8B, SessionKeys9, SessionKeys9B, ValidatorCount } from '@polkadot/types/interfaces/session';
+import type { Bid, BidKind, SocietyJudgement, SocietyVote, StrikeCount, VouchingStatus } from '@polkadot/types/interfaces/society';
+import type { ActiveEraInfo, CompactAssignments, CompactAssignmentsTo257, CompactAssignmentsTo265, CompactAssignmentsWith16, CompactAssignmentsWith24, CompactScore, CompactScoreCompact, ElectionCompute, ElectionPhase, ElectionResult, ElectionScore, ElectionSize, ElectionStatus, EraIndex, EraPoints, EraRewardPoints, EraRewards, Exposure, ExtendedBalance, Forcing, IndividualExposure, KeyType, MomentOf, Nominations, NominatorIndex, NominatorIndexCompact, OffchainAccuracy, OffchainAccuracyCompact, PhragmenScore, Points, RawSolution, RawSolutionTo265, RawSolutionWith16, RawSolutionWith24, ReadySolution, RewardDestination, RewardPoint, RoundSnapshot, SeatHolder, SignedSubmission, SignedSubmissionOf, SignedSubmissionTo276, SlashJournalEntry, SlashingSpans, SlashingSpansTo204, SolutionOrSnapshotSize, SolutionSupport, SolutionSupports, SpanIndex, SpanRecord, StakingLedger, StakingLedgerTo223, StakingLedgerTo240, SubmissionIndicesOf, Supports, UnappliedSlash, UnappliedSlashOther, UnlockChunk, ValidatorIndex, ValidatorIndexCompact, ValidatorPrefs, ValidatorPrefsTo145, ValidatorPrefsTo196, ValidatorPrefsWithBlocked, ValidatorPrefsWithCommission, VoteWeight, Voter } from '@polkadot/types/interfaces/staking';
+import type { ApiId, BlockTrace, BlockTraceEvent, BlockTraceEventData, BlockTraceSpan, KeyValueOption, MigrationStatusResult, ReadProof, RuntimeVersion, RuntimeVersionApi, RuntimeVersionPartial, RuntimeVersionPre3, RuntimeVersionPre4, SpecVersion, StorageChangeSet, TraceBlockResponse, TraceError } from '@polkadot/types/interfaces/state';
+import type { WeightToFeeCoefficient } from '@polkadot/types/interfaces/support';
+import type { AccountInfo, AccountInfoWithDualRefCount, AccountInfoWithProviders, AccountInfoWithRefCount, AccountInfoWithRefCountU8, AccountInfoWithTripleRefCount, ApplyExtrinsicResult, ApplyExtrinsicResultPre6, ArithmeticError, BlockLength, BlockWeights, ChainProperties, ChainType, ConsumedWeight, DigestOf, DispatchClass, DispatchError, DispatchErrorModule, DispatchErrorModulePre6, DispatchErrorModuleU8, DispatchErrorModuleU8a, DispatchErrorPre6, DispatchErrorPre6First, DispatchErrorTo198, DispatchInfo, DispatchInfoTo190, DispatchInfoTo244, DispatchOutcome, DispatchOutcomePre6, DispatchResult, DispatchResultOf, DispatchResultTo198, Event, EventId, EventIndex, EventRecord, Health, InvalidTransaction, Key, LastRuntimeUpgradeInfo, NetworkState, NetworkStatePeerset, NetworkStatePeersetInfo, NodeRole, NotConnectedPeer, Peer, PeerEndpoint, PeerEndpointAddr, PeerInfo, PeerPing, PerDispatchClassU32, PerDispatchClassWeight, PerDispatchClassWeightsPerClass, Phase, RawOrigin, RefCount, RefCountTo259, SyncState, SystemOrigin, TokenError, TransactionValidityError, TransactionalError, UnknownTransaction, WeightPerClass } from '@polkadot/types/interfaces/system';
+import type { Bounty, BountyIndex, BountyStatus, BountyStatusActive, BountyStatusCuratorProposed, BountyStatusPendingPayout, OpenTip, OpenTipFinderTo225, OpenTipTip, OpenTipTo225, TreasuryProposal } from '@polkadot/types/interfaces/treasury';
+import type { Multiplier } from '@polkadot/types/interfaces/txpayment';
+import type { TransactionSource, TransactionValidity, ValidTransaction } from '@polkadot/types/interfaces/txqueue';
+import type { ClassDetails, ClassId, ClassMetadata, DepositBalance, DepositBalanceOf, DestroyWitness, InstanceDetails, InstanceId, InstanceMetadata } from '@polkadot/types/interfaces/uniques';
+import type { Multisig, Timepoint } from '@polkadot/types/interfaces/utility';
+import type { VestingInfo } from '@polkadot/types/interfaces/vesting';
+import type { AssetInstance, AssetInstanceV0, AssetInstanceV1, AssetInstanceV2, BodyId, BodyPart, DoubleEncodedCall, Fungibility, FungibilityV0, FungibilityV1, FungibilityV2, InboundStatus, InstructionV2, InteriorMultiLocation, Junction, JunctionV0, JunctionV1, JunctionV2, Junctions, JunctionsV1, JunctionsV2, MultiAsset, MultiAssetFilter, MultiAssetFilterV1, MultiAssetFilterV2, MultiAssetV0, MultiAssetV1, MultiAssetV2, MultiAssets, MultiAssetsV1, MultiAssetsV2, MultiLocation, MultiLocationV0, MultiLocationV1, MultiLocationV2, NetworkId, OriginKindV0, OriginKindV1, OriginKindV2, OutboundStatus, Outcome, QueryId, QueryStatus, QueueConfigData, Response, ResponseV0, ResponseV1, ResponseV2, ResponseV2Error, ResponseV2Result, VersionMigrationStage, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WeightLimitV2, WildFungibility, WildFungibilityV0, WildFungibilityV1, WildFungibilityV2, WildMultiAsset, WildMultiAssetV1, WildMultiAssetV2, Xcm, XcmAssetId, XcmError, XcmErrorV0, XcmErrorV1, XcmErrorV2, XcmOrder, XcmOrderV0, XcmOrderV1, XcmOrderV2, XcmOrigin, XcmOriginKind, XcmV0, XcmV1, XcmV2, XcmVersion, XcmpMessageFormat } from '@polkadot/types/interfaces/xcm';
+
+declare module '@polkadot/types/types/registry' {
+  interface InterfaceTypes {
+    AbridgedCandidateReceipt: AbridgedCandidateReceipt;
+    AbridgedHostConfiguration: AbridgedHostConfiguration;
+    AbridgedHrmpChannel: AbridgedHrmpChannel;
+    AccountData: AccountData;
+    AccountId: AccountId;
+    AccountId20: AccountId20;
+    AccountId32: AccountId32;
+    AccountId33: AccountId33;
+    AccountIdOf: AccountIdOf;
+    AccountIndex: AccountIndex;
+    AccountInfo: AccountInfo;
+    AccountInfoWithDualRefCount: AccountInfoWithDualRefCount;
+    AccountInfoWithProviders: AccountInfoWithProviders;
+    AccountInfoWithRefCount: AccountInfoWithRefCount;
+    AccountInfoWithRefCountU8: AccountInfoWithRefCountU8;
+    AccountInfoWithTripleRefCount: AccountInfoWithTripleRefCount;
+    AccountStatus: AccountStatus;
+    AccountValidity: AccountValidity;
+    AccountVote: AccountVote;
+    AccountVoteSplit: AccountVoteSplit;
+    AccountVoteStandard: AccountVoteStandard;
+    ActiveEraInfo: ActiveEraInfo;
+    ActiveGilt: ActiveGilt;
+    ActiveGiltsTotal: ActiveGiltsTotal;
+    ActiveIndex: ActiveIndex;
+    ActiveRecovery: ActiveRecovery;
+    Address: Address;
+    AliveContractInfo: AliveContractInfo;
+    AllowedSlots: AllowedSlots;
+    AnySignature: AnySignature;
+    ApiId: ApiId;
+    ApplyExtrinsicResult: ApplyExtrinsicResult;
+    ApplyExtrinsicResultPre6: ApplyExtrinsicResultPre6;
+    ApprovalFlag: ApprovalFlag;
+    Approvals: Approvals;
+    ArithmeticError: ArithmeticError;
+    AssetApproval: AssetApproval;
+    AssetApprovalKey: AssetApprovalKey;
+    AssetBalance: AssetBalance;
+    AssetDestroyWitness: AssetDestroyWitness;
+    AssetDetails: AssetDetails;
+    AssetId: AssetId;
+    AssetInstance: AssetInstance;
+    AssetInstanceV0: AssetInstanceV0;
+    AssetInstanceV1: AssetInstanceV1;
+    AssetInstanceV2: AssetInstanceV2;
+    AssetMetadata: AssetMetadata;
+    AssetOptions: AssetOptions;
+    AssignmentId: AssignmentId;
+    AssignmentKind: AssignmentKind;
+    AttestedCandidate: AttestedCandidate;
+    AuctionIndex: AuctionIndex;
+    AuthIndex: AuthIndex;
+    AuthorityDiscoveryId: AuthorityDiscoveryId;
+    AuthorityId: AuthorityId;
+    AuthorityIndex: AuthorityIndex;
+    AuthorityList: AuthorityList;
+    AuthoritySet: AuthoritySet;
+    AuthoritySetChange: AuthoritySetChange;
+    AuthoritySetChanges: AuthoritySetChanges;
+    AuthoritySignature: AuthoritySignature;
+    AuthorityWeight: AuthorityWeight;
+    AvailabilityBitfield: AvailabilityBitfield;
+    AvailabilityBitfieldRecord: AvailabilityBitfieldRecord;
+    BabeAuthorityWeight: BabeAuthorityWeight;
+    BabeBlockWeight: BabeBlockWeight;
+    BabeEpochConfiguration: BabeEpochConfiguration;
+    BabeEquivocationProof: BabeEquivocationProof;
+    BabeGenesisConfiguration: BabeGenesisConfiguration;
+    BabeGenesisConfigurationV1: BabeGenesisConfigurationV1;
+    BabeWeight: BabeWeight;
+    BackedCandidate: BackedCandidate;
+    Balance: Balance;
+    BalanceLock: BalanceLock;
+    BalanceLockTo212: BalanceLockTo212;
+    BalanceOf: BalanceOf;
+    BalanceStatus: BalanceStatus;
+    BeefyAuthoritySet: BeefyAuthoritySet;
+    BeefyCommitment: BeefyCommitment;
+    BeefyEquivocationProof: BeefyEquivocationProof;
+    BeefyId: BeefyId;
+    BeefyKey: BeefyKey;
+    BeefyNextAuthoritySet: BeefyNextAuthoritySet;
+    BeefyPayload: BeefyPayload;
+    BeefyPayloadId: BeefyPayloadId;
+    BeefySignedCommitment: BeefySignedCommitment;
+    BeefyVoteMessage: BeefyVoteMessage;
+    BenchmarkBatch: BenchmarkBatch;
+    BenchmarkConfig: BenchmarkConfig;
+    BenchmarkList: BenchmarkList;
+    BenchmarkMetadata: BenchmarkMetadata;
+    BenchmarkParameter: BenchmarkParameter;
+    BenchmarkResult: BenchmarkResult;
+    Bid: Bid;
+    Bidder: Bidder;
+    BidKind: BidKind;
+    BitVec: BitVec;
+    Block: Block;
+    BlockAttestations: BlockAttestations;
+    BlockHash: BlockHash;
+    BlockLength: BlockLength;
+    BlockNumber: BlockNumber;
+    BlockNumberFor: BlockNumberFor;
+    BlockNumberOf: BlockNumberOf;
+    BlockStats: BlockStats;
+    BlockTrace: BlockTrace;
+    BlockTraceEvent: BlockTraceEvent;
+    BlockTraceEventData: BlockTraceEventData;
+    BlockTraceSpan: BlockTraceSpan;
+    BlockV0: BlockV0;
+    BlockV1: BlockV1;
+    BlockV2: BlockV2;
+    BlockWeights: BlockWeights;
+    BodyId: BodyId;
+    BodyPart: BodyPart;
+    bool: bool;
+    Bool: Bool;
+    Bounty: Bounty;
+    BountyIndex: BountyIndex;
+    BountyStatus: BountyStatus;
+    BountyStatusActive: BountyStatusActive;
+    BountyStatusCuratorProposed: BountyStatusCuratorProposed;
+    BountyStatusPendingPayout: BountyStatusPendingPayout;
+    BridgedBlockHash: BridgedBlockHash;
+    BridgedBlockNumber: BridgedBlockNumber;
+    BridgedHeader: BridgedHeader;
+    BridgeMessageId: BridgeMessageId;
+    BufferedSessionChange: BufferedSessionChange;
+    Bytes: Bytes;
+    Call: Call;
+    CallHash: CallHash;
+    CallHashOf: CallHashOf;
+    CallIndex: CallIndex;
+    CallOrigin: CallOrigin;
+    CandidateCommitments: CandidateCommitments;
+    CandidateDescriptor: CandidateDescriptor;
+    CandidateEvent: CandidateEvent;
+    CandidateHash: CandidateHash;
+    CandidateInfo: CandidateInfo;
+    CandidatePendingAvailability: CandidatePendingAvailability;
+    CandidateReceipt: CandidateReceipt;
+    ChainId: ChainId;
+    ChainProperties: ChainProperties;
+    ChainType: ChainType;
+    ChangesTrieConfiguration: ChangesTrieConfiguration;
+    ChangesTrieSignal: ChangesTrieSignal;
+    CheckInherentsResult: CheckInherentsResult;
+    ClassDetails: ClassDetails;
+    ClassId: ClassId;
+    ClassMetadata: ClassMetadata;
+    CodecHash: CodecHash;
+    CodeHash: CodeHash;
+    CodeSource: CodeSource;
+    CodeUploadRequest: CodeUploadRequest;
+    CodeUploadResult: CodeUploadResult;
+    CodeUploadResultValue: CodeUploadResultValue;
+    CollationInfo: CollationInfo;
+    CollationInfoV1: CollationInfoV1;
+    CollatorId: CollatorId;
+    CollatorSignature: CollatorSignature;
+    CollectiveOrigin: CollectiveOrigin;
+    CommittedCandidateReceipt: CommittedCandidateReceipt;
+    CompactAssignments: CompactAssignments;
+    CompactAssignmentsTo257: CompactAssignmentsTo257;
+    CompactAssignmentsTo265: CompactAssignmentsTo265;
+    CompactAssignmentsWith16: CompactAssignmentsWith16;
+    CompactAssignmentsWith24: CompactAssignmentsWith24;
+    CompactScore: CompactScore;
+    CompactScoreCompact: CompactScoreCompact;
+    ConfigData: ConfigData;
+    Consensus: Consensus;
+    ConsensusEngineId: ConsensusEngineId;
+    ConsumedWeight: ConsumedWeight;
+    ContractCallFlags: ContractCallFlags;
+    ContractCallRequest: ContractCallRequest;
+    ContractConstructorSpecLatest: ContractConstructorSpecLatest;
+    ContractConstructorSpecV0: ContractConstructorSpecV0;
+    ContractConstructorSpecV1: ContractConstructorSpecV1;
+    ContractConstructorSpecV2: ContractConstructorSpecV2;
+    ContractConstructorSpecV3: ContractConstructorSpecV3;
+    ContractConstructorSpecV4: ContractConstructorSpecV4;
+    ContractContractSpecV0: ContractContractSpecV0;
+    ContractContractSpecV1: ContractContractSpecV1;
+    ContractContractSpecV2: ContractContractSpecV2;
+    ContractContractSpecV3: ContractContractSpecV3;
+    ContractContractSpecV4: ContractContractSpecV4;
+    ContractCryptoHasher: ContractCryptoHasher;
+    ContractDiscriminant: ContractDiscriminant;
+    ContractDisplayName: ContractDisplayName;
+    ContractEnvironmentV4: ContractEnvironmentV4;
+    ContractEventParamSpecLatest: ContractEventParamSpecLatest;
+    ContractEventParamSpecV0: ContractEventParamSpecV0;
+    ContractEventParamSpecV2: ContractEventParamSpecV2;
+    ContractEventSpecLatest: ContractEventSpecLatest;
+    ContractEventSpecV0: ContractEventSpecV0;
+    ContractEventSpecV1: ContractEventSpecV1;
+    ContractEventSpecV2: ContractEventSpecV2;
+    ContractExecResult: ContractExecResult;
+    ContractExecResultOk: ContractExecResultOk;
+    ContractExecResultResult: ContractExecResultResult;
+    ContractExecResultSuccessTo255: ContractExecResultSuccessTo255;
+    ContractExecResultSuccessTo260: ContractExecResultSuccessTo260;
+    ContractExecResultTo255: ContractExecResultTo255;
+    ContractExecResultTo260: ContractExecResultTo260;
+    ContractExecResultTo267: ContractExecResultTo267;
+    ContractExecResultU64: ContractExecResultU64;
+    ContractInfo: ContractInfo;
+    ContractInstantiateResult: ContractInstantiateResult;
+    ContractInstantiateResultTo267: ContractInstantiateResultTo267;
+    ContractInstantiateResultTo299: ContractInstantiateResultTo299;
+    ContractInstantiateResultU64: ContractInstantiateResultU64;
+    ContractLayoutArray: ContractLayoutArray;
+    ContractLayoutCell: ContractLayoutCell;
+    ContractLayoutEnum: ContractLayoutEnum;
+    ContractLayoutHash: ContractLayoutHash;
+    ContractLayoutHashingStrategy: ContractLayoutHashingStrategy;
+    ContractLayoutKey: ContractLayoutKey;
+    ContractLayoutStruct: ContractLayoutStruct;
+    ContractLayoutStructField: ContractLayoutStructField;
+    ContractMessageParamSpecLatest: ContractMessageParamSpecLatest;
+    ContractMessageParamSpecV0: ContractMessageParamSpecV0;
+    ContractMessageParamSpecV2: ContractMessageParamSpecV2;
+    ContractMessageSpecLatest: ContractMessageSpecLatest;
+    ContractMessageSpecV0: ContractMessageSpecV0;
+    ContractMessageSpecV1: ContractMessageSpecV1;
+    ContractMessageSpecV2: ContractMessageSpecV2;
+    ContractMessageSpecV3: ContractMessageSpecV3;
+    ContractMetadata: ContractMetadata;
+    ContractMetadataLatest: ContractMetadataLatest;
+    ContractMetadataV0: ContractMetadataV0;
+    ContractMetadataV1: ContractMetadataV1;
+    ContractMetadataV2: ContractMetadataV2;
+    ContractMetadataV3: ContractMetadataV3;
+    ContractMetadataV4: ContractMetadataV4;
+    ContractProject: ContractProject;
+    ContractProjectContract: ContractProjectContract;
+    ContractProjectInfo: ContractProjectInfo;
+    ContractProjectSource: ContractProjectSource;
+    ContractProjectV0: ContractProjectV0;
+    ContractReturnFlags: ContractReturnFlags;
+    ContractSelector: ContractSelector;
+    ContractStorageKey: ContractStorageKey;
+    ContractStorageLayout: ContractStorageLayout;
+    ContractTypeSpec: ContractTypeSpec;
+    Conviction: Conviction;
+    CoreAssignment: CoreAssignment;
+    CoreIndex: CoreIndex;
+    CoreOccupied: CoreOccupied;
+    CoreState: CoreState;
+    CrateVersion: CrateVersion;
+    CreatedBlock: CreatedBlock;
+    CumulusPalletDmpQueueCall: CumulusPalletDmpQueueCall;
+    CumulusPalletDmpQueueConfigData: CumulusPalletDmpQueueConfigData;
+    CumulusPalletDmpQueueError: CumulusPalletDmpQueueError;
+    CumulusPalletDmpQueueEvent: CumulusPalletDmpQueueEvent;
+    CumulusPalletDmpQueuePageIndexData: CumulusPalletDmpQueuePageIndexData;
+    CumulusPalletParachainSystemCall: CumulusPalletParachainSystemCall;
+    CumulusPalletParachainSystemCodeUpgradeAuthorization: CumulusPalletParachainSystemCodeUpgradeAuthorization;
+    CumulusPalletParachainSystemError: CumulusPalletParachainSystemError;
+    CumulusPalletParachainSystemEvent: CumulusPalletParachainSystemEvent;
+    CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot;
+    CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity;
+    CumulusPalletParachainSystemUnincludedSegmentAncestor: CumulusPalletParachainSystemUnincludedSegmentAncestor;
+    CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate;
+    CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: CumulusPalletParachainSystemUnincludedSegmentSegmentTracker;
+    CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
+    CumulusPalletXcmCall: CumulusPalletXcmCall;
+    CumulusPalletXcmError: CumulusPalletXcmError;
+    CumulusPalletXcmEvent: CumulusPalletXcmEvent;
+    CumulusPalletXcmOrigin: CumulusPalletXcmOrigin;
+    CumulusPalletXcmpQueueCall: CumulusPalletXcmpQueueCall;
+    CumulusPalletXcmpQueueError: CumulusPalletXcmpQueueError;
+    CumulusPalletXcmpQueueEvent: CumulusPalletXcmpQueueEvent;
+    CumulusPalletXcmpQueueInboundChannelDetails: CumulusPalletXcmpQueueInboundChannelDetails;
+    CumulusPalletXcmpQueueInboundState: CumulusPalletXcmpQueueInboundState;
+    CumulusPalletXcmpQueueOutboundChannelDetails: CumulusPalletXcmpQueueOutboundChannelDetails;
+    CumulusPalletXcmpQueueOutboundState: CumulusPalletXcmpQueueOutboundState;
+    CumulusPalletXcmpQueueQueueConfigData: CumulusPalletXcmpQueueQueueConfigData;
+    CumulusPrimitivesParachainInherentParachainInherentData: CumulusPrimitivesParachainInherentParachainInherentData;
+    CustomMetadata15: CustomMetadata15;
+    CustomValueMetadata15: CustomValueMetadata15;
+    Data: Data;
+    DeferredOffenceOf: DeferredOffenceOf;
+    DefunctVoter: DefunctVoter;
+    DelayKind: DelayKind;
+    DelayKindBest: DelayKindBest;
+    Delegations: Delegations;
+    DeletedContract: DeletedContract;
+    DeliveredMessages: DeliveredMessages;
+    DepositBalance: DepositBalance;
+    DepositBalanceOf: DepositBalanceOf;
+    DestroyWitness: DestroyWitness;
+    Digest: Digest;
+    DigestItem: DigestItem;
+    DigestOf: DigestOf;
+    DispatchClass: DispatchClass;
+    DispatchError: DispatchError;
+    DispatchErrorModule: DispatchErrorModule;
+    DispatchErrorModulePre6: DispatchErrorModulePre6;
+    DispatchErrorModuleU8: DispatchErrorModuleU8;
+    DispatchErrorModuleU8a: DispatchErrorModuleU8a;
+    DispatchErrorPre6: DispatchErrorPre6;
+    DispatchErrorPre6First: DispatchErrorPre6First;
+    DispatchErrorTo198: DispatchErrorTo198;
+    DispatchFeePayment: DispatchFeePayment;
+    DispatchInfo: DispatchInfo;
+    DispatchInfoTo190: DispatchInfoTo190;
+    DispatchInfoTo244: DispatchInfoTo244;
+    DispatchOutcome: DispatchOutcome;
+    DispatchOutcomePre6: DispatchOutcomePre6;
+    DispatchResult: DispatchResult;
+    DispatchResultOf: DispatchResultOf;
+    DispatchResultTo198: DispatchResultTo198;
+    DisputeLocation: DisputeLocation;
+    DisputeProof: DisputeProof;
+    DisputeResult: DisputeResult;
+    DisputeState: DisputeState;
+    DisputeStatement: DisputeStatement;
+    DisputeStatementSet: DisputeStatementSet;
+    DisputesTimeSlot: DisputesTimeSlot;
+    DoubleEncodedCall: DoubleEncodedCall;
+    DoubleVoteReport: DoubleVoteReport;
+    DownwardMessage: DownwardMessage;
+    EcdsaSignature: EcdsaSignature;
+    Ed25519Signature: Ed25519Signature;
+    EIP1559Transaction: EIP1559Transaction;
+    EIP2930Transaction: EIP2930Transaction;
+    ElectionCompute: ElectionCompute;
+    ElectionPhase: ElectionPhase;
+    ElectionResult: ElectionResult;
+    ElectionScore: ElectionScore;
+    ElectionSize: ElectionSize;
+    ElectionStatus: ElectionStatus;
+    EncodedFinalityProofs: EncodedFinalityProofs;
+    EncodedJustification: EncodedJustification;
+    Epoch: Epoch;
+    EpochAuthorship: EpochAuthorship;
+    Era: Era;
+    EraIndex: EraIndex;
+    EraPoints: EraPoints;
+    EraRewardPoints: EraRewardPoints;
+    EraRewards: EraRewards;
+    ErrorMetadataLatest: ErrorMetadataLatest;
+    ErrorMetadataV10: ErrorMetadataV10;
+    ErrorMetadataV11: ErrorMetadataV11;
+    ErrorMetadataV12: ErrorMetadataV12;
+    ErrorMetadataV13: ErrorMetadataV13;
+    ErrorMetadataV14: ErrorMetadataV14;
+    ErrorMetadataV9: ErrorMetadataV9;
+    EthAccessList: EthAccessList;
+    EthAccessListItem: EthAccessListItem;
+    EthAccount: EthAccount;
+    EthAddress: EthAddress;
+    EthBlock: EthBlock;
+    EthBloom: EthBloom;
+    EthbloomBloom: EthbloomBloom;
+    EthCallRequest: EthCallRequest;
+    EthereumAccountId: EthereumAccountId;
+    EthereumAddress: EthereumAddress;
+    EthereumBlock: EthereumBlock;
+    EthereumHeader: EthereumHeader;
+    EthereumLog: EthereumLog;
+    EthereumLookupSource: EthereumLookupSource;
+    EthereumReceiptEip658ReceiptData: EthereumReceiptEip658ReceiptData;
+    EthereumReceiptReceiptV3: EthereumReceiptReceiptV3;
+    EthereumSignature: EthereumSignature;
+    EthereumTransactionAccessListItem: EthereumTransactionAccessListItem;
+    EthereumTransactionEip1559Transaction: EthereumTransactionEip1559Transaction;
+    EthereumTransactionEip2930Transaction: EthereumTransactionEip2930Transaction;
+    EthereumTransactionLegacyTransaction: EthereumTransactionLegacyTransaction;
+    EthereumTransactionTransactionAction: EthereumTransactionTransactionAction;
+    EthereumTransactionTransactionSignature: EthereumTransactionTransactionSignature;
+    EthereumTransactionTransactionV2: EthereumTransactionTransactionV2;
+    EthereumTypesHashH64: EthereumTypesHashH64;
+    EthFeeHistory: EthFeeHistory;
+    EthFilter: EthFilter;
+    EthFilterAddress: EthFilterAddress;
+    EthFilterChanges: EthFilterChanges;
+    EthFilterTopic: EthFilterTopic;
+    EthFilterTopicEntry: EthFilterTopicEntry;
+    EthFilterTopicInner: EthFilterTopicInner;
+    EthHeader: EthHeader;
+    EthLog: EthLog;
+    EthReceipt: EthReceipt;
+    EthReceiptV0: EthReceiptV0;
+    EthReceiptV3: EthReceiptV3;
+    EthRichBlock: EthRichBlock;
+    EthRichHeader: EthRichHeader;
+    EthStorageProof: EthStorageProof;
+    EthSubKind: EthSubKind;
+    EthSubParams: EthSubParams;
+    EthSubResult: EthSubResult;
+    EthSyncInfo: EthSyncInfo;
+    EthSyncStatus: EthSyncStatus;
+    EthTransaction: EthTransaction;
+    EthTransactionAction: EthTransactionAction;
+    EthTransactionCondition: EthTransactionCondition;
+    EthTransactionRequest: EthTransactionRequest;
+    EthTransactionSignature: EthTransactionSignature;
+    EthTransactionStatus: EthTransactionStatus;
+    EthWork: EthWork;
+    Event: Event;
+    EventId: EventId;
+    EventIndex: EventIndex;
+    EventMetadataLatest: EventMetadataLatest;
+    EventMetadataV10: EventMetadataV10;
+    EventMetadataV11: EventMetadataV11;
+    EventMetadataV12: EventMetadataV12;
+    EventMetadataV13: EventMetadataV13;
+    EventMetadataV14: EventMetadataV14;
+    EventMetadataV9: EventMetadataV9;
+    EventRecord: EventRecord;
+    EvmAccount: EvmAccount;
+    EvmCallInfo: EvmCallInfo;
+    EvmCallInfoV2: EvmCallInfoV2;
+    EvmCoreErrorExitError: EvmCoreErrorExitError;
+    EvmCoreErrorExitFatal: EvmCoreErrorExitFatal;
+    EvmCoreErrorExitReason: EvmCoreErrorExitReason;
+    EvmCoreErrorExitRevert: EvmCoreErrorExitRevert;
+    EvmCoreErrorExitSucceed: EvmCoreErrorExitSucceed;
+    EvmCreateInfo: EvmCreateInfo;
+    EvmCreateInfoV2: EvmCreateInfoV2;
+    EvmLog: EvmLog;
+    EvmVicinity: EvmVicinity;
+    EvmWeightInfo: EvmWeightInfo;
+    ExecReturnValue: ExecReturnValue;
+    ExecutorParam: ExecutorParam;
+    ExecutorParams: ExecutorParams;
+    ExecutorParamsHash: ExecutorParamsHash;
+    ExitError: ExitError;
+    ExitFatal: ExitFatal;
+    ExitReason: ExitReason;
+    ExitRevert: ExitRevert;
+    ExitSucceed: ExitSucceed;
+    ExplicitDisputeStatement: ExplicitDisputeStatement;
+    Exposure: Exposure;
+    ExtendedBalance: ExtendedBalance;
+    Extrinsic: Extrinsic;
+    ExtrinsicEra: ExtrinsicEra;
+    ExtrinsicMetadataLatest: ExtrinsicMetadataLatest;
+    ExtrinsicMetadataV11: ExtrinsicMetadataV11;
+    ExtrinsicMetadataV12: ExtrinsicMetadataV12;
+    ExtrinsicMetadataV13: ExtrinsicMetadataV13;
+    ExtrinsicMetadataV14: ExtrinsicMetadataV14;
+    ExtrinsicMetadataV15: ExtrinsicMetadataV15;
+    ExtrinsicOrHash: ExtrinsicOrHash;
+    ExtrinsicPayload: ExtrinsicPayload;
+    ExtrinsicPayloadUnknown: ExtrinsicPayloadUnknown;
+    ExtrinsicPayloadV4: ExtrinsicPayloadV4;
+    ExtrinsicSignature: ExtrinsicSignature;
+    ExtrinsicSignatureV4: ExtrinsicSignatureV4;
+    ExtrinsicStatus: ExtrinsicStatus;
+    ExtrinsicsWeight: ExtrinsicsWeight;
+    ExtrinsicUnknown: ExtrinsicUnknown;
+    ExtrinsicV4: ExtrinsicV4;
+    f32: f32;
+    F32: F32;
+    f64: f64;
+    F64: F64;
+    FeeDetails: FeeDetails;
+    Fixed128: Fixed128;
+    Fixed64: Fixed64;
+    FixedI128: FixedI128;
+    FixedI64: FixedI64;
+    FixedU128: FixedU128;
+    FixedU64: FixedU64;
+    Forcing: Forcing;
+    ForkTreePendingChange: ForkTreePendingChange;
+    ForkTreePendingChangeNode: ForkTreePendingChangeNode;
+    FpRpcTransactionStatus: FpRpcTransactionStatus;
+    FrameSupportDispatchDispatchClass: FrameSupportDispatchDispatchClass;
+    FrameSupportDispatchDispatchInfo: FrameSupportDispatchDispatchInfo;
+    FrameSupportDispatchPays: FrameSupportDispatchPays;
+    FrameSupportDispatchPerDispatchClassU32: FrameSupportDispatchPerDispatchClassU32;
+    FrameSupportDispatchPerDispatchClassWeight: FrameSupportDispatchPerDispatchClassWeight;
+    FrameSupportDispatchPerDispatchClassWeightsPerClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
+    FrameSupportDispatchRawOrigin: FrameSupportDispatchRawOrigin;
+    FrameSupportPalletId: FrameSupportPalletId;
+    FrameSupportPreimagesBounded: FrameSupportPreimagesBounded;
+    FrameSupportScheduleDispatchTime: FrameSupportScheduleDispatchTime;
+    FrameSupportTokensMiscBalanceStatus: FrameSupportTokensMiscBalanceStatus;
+    FrameSystemAccountInfo: FrameSystemAccountInfo;
+    FrameSystemCall: FrameSystemCall;
+    FrameSystemError: FrameSystemError;
+    FrameSystemEvent: FrameSystemEvent;
+    FrameSystemEventRecord: FrameSystemEventRecord;
+    FrameSystemExtensionsCheckGenesis: FrameSystemExtensionsCheckGenesis;
+    FrameSystemExtensionsCheckNonce: FrameSystemExtensionsCheckNonce;
+    FrameSystemExtensionsCheckSpecVersion: FrameSystemExtensionsCheckSpecVersion;
+    FrameSystemExtensionsCheckTxVersion: FrameSystemExtensionsCheckTxVersion;
+    FrameSystemExtensionsCheckWeight: FrameSystemExtensionsCheckWeight;
+    FrameSystemLastRuntimeUpgradeInfo: FrameSystemLastRuntimeUpgradeInfo;
+    FrameSystemLimitsBlockLength: FrameSystemLimitsBlockLength;
+    FrameSystemLimitsBlockWeights: FrameSystemLimitsBlockWeights;
+    FrameSystemLimitsWeightsPerClass: FrameSystemLimitsWeightsPerClass;
+    FrameSystemPhase: FrameSystemPhase;
+    FullIdentification: FullIdentification;
+    FunctionArgumentMetadataLatest: FunctionArgumentMetadataLatest;
+    FunctionArgumentMetadataV10: FunctionArgumentMetadataV10;
+    FunctionArgumentMetadataV11: FunctionArgumentMetadataV11;
+    FunctionArgumentMetadataV12: FunctionArgumentMetadataV12;
+    FunctionArgumentMetadataV13: FunctionArgumentMetadataV13;
+    FunctionArgumentMetadataV14: FunctionArgumentMetadataV14;
+    FunctionArgumentMetadataV9: FunctionArgumentMetadataV9;
+    FunctionMetadataLatest: FunctionMetadataLatest;
+    FunctionMetadataV10: FunctionMetadataV10;
+    FunctionMetadataV11: FunctionMetadataV11;
+    FunctionMetadataV12: FunctionMetadataV12;
+    FunctionMetadataV13: FunctionMetadataV13;
+    FunctionMetadataV14: FunctionMetadataV14;
+    FunctionMetadataV9: FunctionMetadataV9;
+    FundIndex: FundIndex;
+    FundInfo: FundInfo;
+    Fungibility: Fungibility;
+    FungibilityV0: FungibilityV0;
+    FungibilityV1: FungibilityV1;
+    FungibilityV2: FungibilityV2;
+    FungiblesAccessError: FungiblesAccessError;
+    Gas: Gas;
+    GiltBid: GiltBid;
+    GlobalValidationData: GlobalValidationData;
+    GlobalValidationSchedule: GlobalValidationSchedule;
+    GrandpaCommit: GrandpaCommit;
+    GrandpaEquivocation: GrandpaEquivocation;
+    GrandpaEquivocationProof: GrandpaEquivocationProof;
+    GrandpaEquivocationValue: GrandpaEquivocationValue;
+    GrandpaJustification: GrandpaJustification;
+    GrandpaPrecommit: GrandpaPrecommit;
+    GrandpaPrevote: GrandpaPrevote;
+    GrandpaSignedPrecommit: GrandpaSignedPrecommit;
+    GroupIndex: GroupIndex;
+    GroupRotationInfo: GroupRotationInfo;
+    H1024: H1024;
+    H128: H128;
+    H160: H160;
+    H2048: H2048;
+    H256: H256;
+    H32: H32;
+    H512: H512;
+    H64: H64;
+    Hash: Hash;
+    HeadData: HeadData;
+    Header: Header;
+    HeaderPartial: HeaderPartial;
+    Health: Health;
+    Heartbeat: Heartbeat;
+    HeartbeatTo244: HeartbeatTo244;
+    HostConfiguration: HostConfiguration;
+    HostFnWeights: HostFnWeights;
+    HostFnWeightsTo264: HostFnWeightsTo264;
+    HrmpChannel: HrmpChannel;
+    HrmpChannelId: HrmpChannelId;
+    HrmpOpenChannelRequest: HrmpOpenChannelRequest;
+    i128: i128;
+    I128: I128;
+    i16: i16;
+    I16: I16;
+    i256: i256;
+    I256: I256;
+    i32: i32;
+    I32: I32;
+    I32F32: I32F32;
+    i64: i64;
+    I64: I64;
+    i8: i8;
+    I8: I8;
+    IdentificationTuple: IdentificationTuple;
+    IdentityFields: IdentityFields;
+    IdentityInfo: IdentityInfo;
+    IdentityInfoAdditional: IdentityInfoAdditional;
+    IdentityInfoTo198: IdentityInfoTo198;
+    IdentityJudgement: IdentityJudgement;
+    ImmortalEra: ImmortalEra;
+    ImportedAux: ImportedAux;
+    InboundDownwardMessage: InboundDownwardMessage;
+    InboundHrmpMessage: InboundHrmpMessage;
+    InboundHrmpMessages: InboundHrmpMessages;
+    InboundLaneData: InboundLaneData;
+    InboundRelayer: InboundRelayer;
+    InboundStatus: InboundStatus;
+    IncludedBlocks: IncludedBlocks;
+    InclusionFee: InclusionFee;
+    IncomingParachain: IncomingParachain;
+    IncomingParachainDeploy: IncomingParachainDeploy;
+    IncomingParachainFixed: IncomingParachainFixed;
+    Index: Index;
+    IndicesLookupSource: IndicesLookupSource;
+    IndividualExposure: IndividualExposure;
+    InherentData: InherentData;
+    InherentIdentifier: InherentIdentifier;
+    InitializationData: InitializationData;
+    InstanceDetails: InstanceDetails;
+    InstanceId: InstanceId;
+    InstanceMetadata: InstanceMetadata;
+    InstantiateRequest: InstantiateRequest;
+    InstantiateRequestV1: InstantiateRequestV1;
+    InstantiateRequestV2: InstantiateRequestV2;
+    InstantiateReturnValue: InstantiateReturnValue;
+    InstantiateReturnValueOk: InstantiateReturnValueOk;
+    InstantiateReturnValueTo267: InstantiateReturnValueTo267;
+    InstructionV2: InstructionV2;
+    InstructionWeights: InstructionWeights;
+    InteriorMultiLocation: InteriorMultiLocation;
+    InvalidDisputeStatementKind: InvalidDisputeStatementKind;
+    InvalidTransaction: InvalidTransaction;
+    isize: isize;
+    ISize: ISize;
+    Json: Json;
+    Junction: Junction;
+    Junctions: Junctions;
+    JunctionsV1: JunctionsV1;
+    JunctionsV2: JunctionsV2;
+    JunctionV0: JunctionV0;
+    JunctionV1: JunctionV1;
+    JunctionV2: JunctionV2;
+    Justification: Justification;
+    JustificationNotification: JustificationNotification;
+    Justifications: Justifications;
+    Key: Key;
+    KeyOwnerProof: KeyOwnerProof;
+    Keys: Keys;
+    KeyType: KeyType;
+    KeyTypeId: KeyTypeId;
+    KeyValue: KeyValue;
+    KeyValueOption: KeyValueOption;
+    Kind: Kind;
+    LaneId: LaneId;
+    LastContribution: LastContribution;
+    LastRuntimeUpgradeInfo: LastRuntimeUpgradeInfo;
+    LeasePeriod: LeasePeriod;
+    LeasePeriodOf: LeasePeriodOf;
+    LegacyTransaction: LegacyTransaction;
+    Limits: Limits;
+    LimitsTo264: LimitsTo264;
+    LocalValidationData: LocalValidationData;
+    LockIdentifier: LockIdentifier;
+    LookupSource: LookupSource;
+    LookupTarget: LookupTarget;
+    LotteryConfig: LotteryConfig;
+    MaybeRandomness: MaybeRandomness;
+    MaybeVrf: MaybeVrf;
+    MemberCount: MemberCount;
+    MembershipProof: MembershipProof;
+    MessageData: MessageData;
+    MessageId: MessageId;
+    MessageIngestionType: MessageIngestionType;
+    MessageKey: MessageKey;
+    MessageNonce: MessageNonce;
+    MessageQueueChain: MessageQueueChain;
+    MessagesDeliveryProofOf: MessagesDeliveryProofOf;
+    MessagesProofOf: MessagesProofOf;
+    MessagingStateSnapshot: MessagingStateSnapshot;
+    MessagingStateSnapshotEgressEntry: MessagingStateSnapshotEgressEntry;
+    MetadataAll: MetadataAll;
+    MetadataLatest: MetadataLatest;
+    MetadataV10: MetadataV10;
+    MetadataV11: MetadataV11;
+    MetadataV12: MetadataV12;
+    MetadataV13: MetadataV13;
+    MetadataV14: MetadataV14;
+    MetadataV15: MetadataV15;
+    MetadataV9: MetadataV9;
+    MigrationStatusResult: MigrationStatusResult;
+    MmrBatchProof: MmrBatchProof;
+    MmrEncodableOpaqueLeaf: MmrEncodableOpaqueLeaf;
+    MmrError: MmrError;
+    MmrHash: MmrHash;
+    MmrLeafBatchProof: MmrLeafBatchProof;
+    MmrLeafIndex: MmrLeafIndex;
+    MmrLeafProof: MmrLeafProof;
+    MmrNodeIndex: MmrNodeIndex;
+    MmrProof: MmrProof;
+    MmrRootHash: MmrRootHash;
+    ModuleConstantMetadataV10: ModuleConstantMetadataV10;
+    ModuleConstantMetadataV11: ModuleConstantMetadataV11;
+    ModuleConstantMetadataV12: ModuleConstantMetadataV12;
+    ModuleConstantMetadataV13: ModuleConstantMetadataV13;
+    ModuleConstantMetadataV9: ModuleConstantMetadataV9;
+    ModuleId: ModuleId;
+    ModuleMetadataV10: ModuleMetadataV10;
+    ModuleMetadataV11: ModuleMetadataV11;
+    ModuleMetadataV12: ModuleMetadataV12;
+    ModuleMetadataV13: ModuleMetadataV13;
+    ModuleMetadataV9: ModuleMetadataV9;
+    Moment: Moment;
+    MomentOf: MomentOf;
+    MoreAttestations: MoreAttestations;
+    MortalEra: MortalEra;
+    MultiAddress: MultiAddress;
+    MultiAsset: MultiAsset;
+    MultiAssetFilter: MultiAssetFilter;
+    MultiAssetFilterV1: MultiAssetFilterV1;
+    MultiAssetFilterV2: MultiAssetFilterV2;
+    MultiAssets: MultiAssets;
+    MultiAssetsV1: MultiAssetsV1;
+    MultiAssetsV2: MultiAssetsV2;
+    MultiAssetV0: MultiAssetV0;
+    MultiAssetV1: MultiAssetV1;
+    MultiAssetV2: MultiAssetV2;
+    MultiDisputeStatementSet: MultiDisputeStatementSet;
+    MultiLocation: MultiLocation;
+    MultiLocationV0: MultiLocationV0;
+    MultiLocationV1: MultiLocationV1;
+    MultiLocationV2: MultiLocationV2;
+    Multiplier: Multiplier;
+    Multisig: Multisig;
+    MultiSignature: MultiSignature;
+    MultiSigner: MultiSigner;
+    NetworkId: NetworkId;
+    NetworkState: NetworkState;
+    NetworkStatePeerset: NetworkStatePeerset;
+    NetworkStatePeersetInfo: NetworkStatePeersetInfo;
+    NewBidder: NewBidder;
+    NextAuthority: NextAuthority;
+    NextConfigDescriptor: NextConfigDescriptor;
+    NextConfigDescriptorV1: NextConfigDescriptorV1;
+    NftCollectionId: NftCollectionId;
+    NftItemId: NftItemId;
+    NodeRole: NodeRole;
+    Nominations: Nominations;
+    NominatorIndex: NominatorIndex;
+    NominatorIndexCompact: NominatorIndexCompact;
+    NotConnectedPeer: NotConnectedPeer;
+    NpApiError: NpApiError;
+    NpPoolId: NpPoolId;
+    Null: Null;
+    OccupiedCore: OccupiedCore;
+    OccupiedCoreAssumption: OccupiedCoreAssumption;
+    OffchainAccuracy: OffchainAccuracy;
+    OffchainAccuracyCompact: OffchainAccuracyCompact;
+    OffenceDetails: OffenceDetails;
+    Offender: Offender;
+    OldV1SessionInfo: OldV1SessionInfo;
+    OpalRuntimeOriginCaller: OpalRuntimeOriginCaller;
+    OpalRuntimeRuntime: OpalRuntimeRuntime;
+    OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls;
+    OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance;
+    OpalRuntimeRuntimeCommonSessionKeys: OpalRuntimeRuntimeCommonSessionKeys;
+    OpalRuntimeRuntimeHoldReason: OpalRuntimeRuntimeHoldReason;
+    OpaqueCall: OpaqueCall;
+    OpaqueKeyOwnershipProof: OpaqueKeyOwnershipProof;
+    OpaqueMetadata: OpaqueMetadata;
+    OpaqueMultiaddr: OpaqueMultiaddr;
+    OpaqueNetworkState: OpaqueNetworkState;
+    OpaquePeerId: OpaquePeerId;
+    OpaqueTimeSlot: OpaqueTimeSlot;
+    OpenTip: OpenTip;
+    OpenTipFinderTo225: OpenTipFinderTo225;
+    OpenTipTip: OpenTipTip;
+    OpenTipTo225: OpenTipTo225;
+    OperatingMode: OperatingMode;
+    OptionBool: OptionBool;
+    Origin: Origin;
+    OriginCaller: OriginCaller;
+    OriginKindV0: OriginKindV0;
+    OriginKindV1: OriginKindV1;
+    OriginKindV2: OriginKindV2;
+    OrmlTokensAccountData: OrmlTokensAccountData;
+    OrmlTokensBalanceLock: OrmlTokensBalanceLock;
+    OrmlTokensModuleCall: OrmlTokensModuleCall;
+    OrmlTokensModuleError: OrmlTokensModuleError;
+    OrmlTokensModuleEvent: OrmlTokensModuleEvent;
+    OrmlTokensReserveData: OrmlTokensReserveData;
+    OrmlVestingModuleCall: OrmlVestingModuleCall;
+    OrmlVestingModuleError: OrmlVestingModuleError;
+    OrmlVestingModuleEvent: OrmlVestingModuleEvent;
+    OrmlVestingVestingSchedule: OrmlVestingVestingSchedule;
+    OrmlXtokensModuleCall: OrmlXtokensModuleCall;
+    OrmlXtokensModuleError: OrmlXtokensModuleError;
+    OrmlXtokensModuleEvent: OrmlXtokensModuleEvent;
+    OutboundHrmpMessage: OutboundHrmpMessage;
+    OutboundLaneData: OutboundLaneData;
+    OutboundMessageFee: OutboundMessageFee;
+    OutboundPayload: OutboundPayload;
+    OutboundStatus: OutboundStatus;
+    Outcome: Outcome;
+    OuterEnums15: OuterEnums15;
+    OverweightIndex: OverweightIndex;
+    Owner: Owner;
+    PageCounter: PageCounter;
+    PageIndexData: PageIndexData;
+    PalletAppPromotionCall: PalletAppPromotionCall;
+    PalletAppPromotionError: PalletAppPromotionError;
+    PalletAppPromotionEvent: PalletAppPromotionEvent;
+    PalletBalancesAccountData: PalletBalancesAccountData;
+    PalletBalancesBalanceLock: PalletBalancesBalanceLock;
+    PalletBalancesCall: PalletBalancesCall;
+    PalletBalancesError: PalletBalancesError;
+    PalletBalancesEvent: PalletBalancesEvent;
+    PalletBalancesIdAmount: PalletBalancesIdAmount;
+    PalletBalancesReasons: PalletBalancesReasons;
+    PalletBalancesReserveData: PalletBalancesReserveData;
+    PalletCallMetadataLatest: PalletCallMetadataLatest;
+    PalletCallMetadataV14: PalletCallMetadataV14;
+    PalletCollatorSelectionCall: PalletCollatorSelectionCall;
+    PalletCollatorSelectionError: PalletCollatorSelectionError;
+    PalletCollatorSelectionEvent: PalletCollatorSelectionEvent;
+    PalletCollatorSelectionHoldReason: PalletCollatorSelectionHoldReason;
+    PalletCollectiveCall: PalletCollectiveCall;
+    PalletCollectiveError: PalletCollectiveError;
+    PalletCollectiveEvent: PalletCollectiveEvent;
+    PalletCollectiveRawOrigin: PalletCollectiveRawOrigin;
+    PalletCollectiveVotes: PalletCollectiveVotes;
+    PalletCommonError: PalletCommonError;
+    PalletCommonEvent: PalletCommonEvent;
+    PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration;
+    PalletConfigurationCall: PalletConfigurationCall;
+    PalletConfigurationError: PalletConfigurationError;
+    PalletConfigurationEvent: PalletConfigurationEvent;
+    PalletConstantMetadataLatest: PalletConstantMetadataLatest;
+    PalletConstantMetadataV14: PalletConstantMetadataV14;
+    PalletDemocracyCall: PalletDemocracyCall;
+    PalletDemocracyConviction: PalletDemocracyConviction;
+    PalletDemocracyDelegations: PalletDemocracyDelegations;
+    PalletDemocracyError: PalletDemocracyError;
+    PalletDemocracyEvent: PalletDemocracyEvent;
+    PalletDemocracyMetadataOwner: PalletDemocracyMetadataOwner;
+    PalletDemocracyReferendumInfo: PalletDemocracyReferendumInfo;
+    PalletDemocracyReferendumStatus: PalletDemocracyReferendumStatus;
+    PalletDemocracyTally: PalletDemocracyTally;
+    PalletDemocracyVoteAccountVote: PalletDemocracyVoteAccountVote;
+    PalletDemocracyVotePriorLock: PalletDemocracyVotePriorLock;
+    PalletDemocracyVoteThreshold: PalletDemocracyVoteThreshold;
+    PalletDemocracyVoteVoting: PalletDemocracyVoteVoting;
+    PalletErrorMetadataLatest: PalletErrorMetadataLatest;
+    PalletErrorMetadataV14: PalletErrorMetadataV14;
+    PalletEthereumCall: PalletEthereumCall;
+    PalletEthereumError: PalletEthereumError;
+    PalletEthereumEvent: PalletEthereumEvent;
+    PalletEthereumFakeTransactionFinalizer: PalletEthereumFakeTransactionFinalizer;
+    PalletEthereumRawOrigin: PalletEthereumRawOrigin;
+    PalletEventMetadataLatest: PalletEventMetadataLatest;
+    PalletEventMetadataV14: PalletEventMetadataV14;
+    PalletEvmAccountBasicCrossAccountIdRepr: PalletEvmAccountBasicCrossAccountIdRepr;
+    PalletEvmCall: PalletEvmCall;
+    PalletEvmCodeMetadata: PalletEvmCodeMetadata;
+    PalletEvmCoderSubstrateError: PalletEvmCoderSubstrateError;
+    PalletEvmContractHelpersCall: PalletEvmContractHelpersCall;
+    PalletEvmContractHelpersError: PalletEvmContractHelpersError;
+    PalletEvmContractHelpersEvent: PalletEvmContractHelpersEvent;
+    PalletEvmContractHelpersSponsoringModeT: PalletEvmContractHelpersSponsoringModeT;
+    PalletEvmError: PalletEvmError;
+    PalletEvmEvent: PalletEvmEvent;
+    PalletEvmMigrationCall: PalletEvmMigrationCall;
+    PalletEvmMigrationError: PalletEvmMigrationError;
+    PalletEvmMigrationEvent: PalletEvmMigrationEvent;
+    PalletForeignAssetsAssetId: PalletForeignAssetsAssetId;
+    PalletForeignAssetsModuleAssetMetadata: PalletForeignAssetsModuleAssetMetadata;
+    PalletForeignAssetsModuleCall: PalletForeignAssetsModuleCall;
+    PalletForeignAssetsModuleError: PalletForeignAssetsModuleError;
+    PalletForeignAssetsModuleEvent: PalletForeignAssetsModuleEvent;
+    PalletForeignAssetsNativeCurrency: PalletForeignAssetsNativeCurrency;
+    PalletFungibleError: PalletFungibleError;
+    PalletGovOriginsOrigin: PalletGovOriginsOrigin;
+    PalletId: PalletId;
+    PalletIdentityBitFlags: PalletIdentityBitFlags;
+    PalletIdentityCall: PalletIdentityCall;
+    PalletIdentityError: PalletIdentityError;
+    PalletIdentityEvent: PalletIdentityEvent;
+    PalletIdentityIdentityField: PalletIdentityIdentityField;
+    PalletIdentityIdentityInfo: PalletIdentityIdentityInfo;
+    PalletIdentityJudgement: PalletIdentityJudgement;
+    PalletIdentityRegistrarInfo: PalletIdentityRegistrarInfo;
+    PalletIdentityRegistration: PalletIdentityRegistration;
+    PalletInflationCall: PalletInflationCall;
+    PalletMaintenanceCall: PalletMaintenanceCall;
+    PalletMaintenanceError: PalletMaintenanceError;
+    PalletMaintenanceEvent: PalletMaintenanceEvent;
+    PalletMembershipCall: PalletMembershipCall;
+    PalletMembershipError: PalletMembershipError;
+    PalletMembershipEvent: PalletMembershipEvent;
+    PalletMetadataLatest: PalletMetadataLatest;
+    PalletMetadataV14: PalletMetadataV14;
+    PalletMetadataV15: PalletMetadataV15;
+    PalletNonfungibleError: PalletNonfungibleError;
+    PalletNonfungibleItemData: PalletNonfungibleItemData;
+    PalletPreimageCall: PalletPreimageCall;
+    PalletPreimageError: PalletPreimageError;
+    PalletPreimageEvent: PalletPreimageEvent;
+    PalletPreimageRequestStatus: PalletPreimageRequestStatus;
+    PalletRankedCollectiveCall: PalletRankedCollectiveCall;
+    PalletRankedCollectiveError: PalletRankedCollectiveError;
+    PalletRankedCollectiveEvent: PalletRankedCollectiveEvent;
+    PalletRankedCollectiveMemberRecord: PalletRankedCollectiveMemberRecord;
+    PalletRankedCollectiveTally: PalletRankedCollectiveTally;
+    PalletRankedCollectiveVoteRecord: PalletRankedCollectiveVoteRecord;
+    PalletReferendaCall: PalletReferendaCall;
+    PalletReferendaCurve: PalletReferendaCurve;
+    PalletReferendaDecidingStatus: PalletReferendaDecidingStatus;
+    PalletReferendaDeposit: PalletReferendaDeposit;
+    PalletReferendaError: PalletReferendaError;
+    PalletReferendaEvent: PalletReferendaEvent;
+    PalletReferendaReferendumInfo: PalletReferendaReferendumInfo;
+    PalletReferendaReferendumStatus: PalletReferendaReferendumStatus;
+    PalletReferendaTrackInfo: PalletReferendaTrackInfo;
+    PalletRefungibleError: PalletRefungibleError;
+    PalletSchedulerCall: PalletSchedulerCall;
+    PalletSchedulerError: PalletSchedulerError;
+    PalletSchedulerEvent: PalletSchedulerEvent;
+    PalletSchedulerScheduled: PalletSchedulerScheduled;
+    PalletSessionCall: PalletSessionCall;
+    PalletSessionError: PalletSessionError;
+    PalletSessionEvent: PalletSessionEvent;
+    PalletsOrigin: PalletsOrigin;
+    PalletStateTrieMigrationCall: PalletStateTrieMigrationCall;
+    PalletStateTrieMigrationError: PalletStateTrieMigrationError;
+    PalletStateTrieMigrationEvent: PalletStateTrieMigrationEvent;
+    PalletStateTrieMigrationMigrationCompute: PalletStateTrieMigrationMigrationCompute;
+    PalletStateTrieMigrationMigrationLimits: PalletStateTrieMigrationMigrationLimits;
+    PalletStateTrieMigrationMigrationTask: PalletStateTrieMigrationMigrationTask;
+    PalletStateTrieMigrationProgress: PalletStateTrieMigrationProgress;
+    PalletStorageMetadataLatest: PalletStorageMetadataLatest;
+    PalletStorageMetadataV14: PalletStorageMetadataV14;
+    PalletStructureCall: PalletStructureCall;
+    PalletStructureError: PalletStructureError;
+    PalletStructureEvent: PalletStructureEvent;
+    PalletSudoCall: PalletSudoCall;
+    PalletSudoError: PalletSudoError;
+    PalletSudoEvent: PalletSudoEvent;
+    PalletTemplateTransactionPaymentChargeTransactionPayment: PalletTemplateTransactionPaymentChargeTransactionPayment;
+    PalletTestUtilsCall: PalletTestUtilsCall;
+    PalletTestUtilsError: PalletTestUtilsError;
+    PalletTestUtilsEvent: PalletTestUtilsEvent;
+    PalletTimestampCall: PalletTimestampCall;
+    PalletTransactionPaymentEvent: PalletTransactionPaymentEvent;
+    PalletTransactionPaymentReleases: PalletTransactionPaymentReleases;
+    PalletTreasuryCall: PalletTreasuryCall;
+    PalletTreasuryError: PalletTreasuryError;
+    PalletTreasuryEvent: PalletTreasuryEvent;
+    PalletTreasuryProposal: PalletTreasuryProposal;
+    PalletUniqueCall: PalletUniqueCall;
+    PalletUniqueError: PalletUniqueError;
+    PalletUtilityCall: PalletUtilityCall;
+    PalletUtilityError: PalletUtilityError;
+    PalletUtilityEvent: PalletUtilityEvent;
+    PalletVersion: PalletVersion;
+    PalletXcmCall: PalletXcmCall;
+    PalletXcmError: PalletXcmError;
+    PalletXcmEvent: PalletXcmEvent;
+    PalletXcmOrigin: PalletXcmOrigin;
+    PalletXcmQueryStatus: PalletXcmQueryStatus;
+    PalletXcmRemoteLockedFungibleRecord: PalletXcmRemoteLockedFungibleRecord;
+    PalletXcmVersionMigrationStage: PalletXcmVersionMigrationStage;
+    ParachainDispatchOrigin: ParachainDispatchOrigin;
+    ParachainInfoCall: ParachainInfoCall;
+    ParachainInherentData: ParachainInherentData;
+    ParachainProposal: ParachainProposal;
+    ParachainsInherentData: ParachainsInherentData;
+    ParaGenesisArgs: ParaGenesisArgs;
+    ParaId: ParaId;
+    ParaInfo: ParaInfo;
+    ParaLifecycle: ParaLifecycle;
+    Parameter: Parameter;
+    ParaPastCodeMeta: ParaPastCodeMeta;
+    ParaScheduling: ParaScheduling;
+    ParathreadClaim: ParathreadClaim;
+    ParathreadClaimQueue: ParathreadClaimQueue;
+    ParathreadEntry: ParathreadEntry;
+    ParaValidatorIndex: ParaValidatorIndex;
+    Pays: Pays;
+    Peer: Peer;
+    PeerEndpoint: PeerEndpoint;
+    PeerEndpointAddr: PeerEndpointAddr;
+    PeerInfo: PeerInfo;
+    PeerPing: PeerPing;
+    PendingChange: PendingChange;
+    PendingPause: PendingPause;
+    PendingResume: PendingResume;
+    PendingSlashes: PendingSlashes;
+    Perbill: Perbill;
+    Percent: Percent;
+    PerDispatchClassU32: PerDispatchClassU32;
+    PerDispatchClassWeight: PerDispatchClassWeight;
+    PerDispatchClassWeightsPerClass: PerDispatchClassWeightsPerClass;
+    Period: Period;
+    Permill: Permill;
+    PermissionLatest: PermissionLatest;
+    PermissionsV1: PermissionsV1;
+    PermissionVersions: PermissionVersions;
+    Perquintill: Perquintill;
+    PersistedValidationData: PersistedValidationData;
+    PerU16: PerU16;
+    Phantom: Phantom;
+    PhantomData: PhantomData;
+    PhantomTypeUpDataStructs: PhantomTypeUpDataStructs;
+    Phase: Phase;
+    PhragmenScore: PhragmenScore;
+    Points: Points;
+    PolkadotCorePrimitivesInboundDownwardMessage: PolkadotCorePrimitivesInboundDownwardMessage;
+    PolkadotCorePrimitivesInboundHrmpMessage: PolkadotCorePrimitivesInboundHrmpMessage;
+    PolkadotCorePrimitivesOutboundHrmpMessage: PolkadotCorePrimitivesOutboundHrmpMessage;
+    PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat;
+    PolkadotPrimitivesV5AbridgedHostConfiguration: PolkadotPrimitivesV5AbridgedHostConfiguration;
+    PolkadotPrimitivesV5AbridgedHrmpChannel: PolkadotPrimitivesV5AbridgedHrmpChannel;
+    PolkadotPrimitivesV5PersistedValidationData: PolkadotPrimitivesV5PersistedValidationData;
+    PolkadotPrimitivesV5UpgradeGoAhead: PolkadotPrimitivesV5UpgradeGoAhead;
+    PolkadotPrimitivesV5UpgradeRestriction: PolkadotPrimitivesV5UpgradeRestriction;
+    PolkadotPrimitivesVstagingAsyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams;
+    PortableType: PortableType;
+    PortableTypeV14: PortableTypeV14;
+    Precommits: Precommits;
+    PrefabWasmModule: PrefabWasmModule;
+    PrefixedStorageKey: PrefixedStorageKey;
+    PreimageStatus: PreimageStatus;
+    PreimageStatusAvailable: PreimageStatusAvailable;
+    PreRuntime: PreRuntime;
+    Prevotes: Prevotes;
+    Priority: Priority;
+    PriorLock: PriorLock;
+    PropIndex: PropIndex;
+    Proposal: Proposal;
+    ProposalIndex: ProposalIndex;
+    ProxyAnnouncement: ProxyAnnouncement;
+    ProxyDefinition: ProxyDefinition;
+    ProxyState: ProxyState;
+    ProxyType: ProxyType;
+    PvfCheckStatement: PvfCheckStatement;
+    PvfExecTimeoutKind: PvfExecTimeoutKind;
+    PvfPrepTimeoutKind: PvfPrepTimeoutKind;
+    QueryId: QueryId;
+    QueryStatus: QueryStatus;
+    QueueConfigData: QueueConfigData;
+    QueuedParathread: QueuedParathread;
+    Randomness: Randomness;
+    Raw: Raw;
+    RawAuraPreDigest: RawAuraPreDigest;
+    RawBabePreDigest: RawBabePreDigest;
+    RawBabePreDigestCompat: RawBabePreDigestCompat;
+    RawBabePreDigestPrimary: RawBabePreDigestPrimary;
+    RawBabePreDigestPrimaryTo159: RawBabePreDigestPrimaryTo159;
+    RawBabePreDigestSecondaryPlain: RawBabePreDigestSecondaryPlain;
+    RawBabePreDigestSecondaryTo159: RawBabePreDigestSecondaryTo159;
+    RawBabePreDigestSecondaryVRF: RawBabePreDigestSecondaryVRF;
+    RawBabePreDigestTo159: RawBabePreDigestTo159;
+    RawOrigin: RawOrigin;
+    RawSolution: RawSolution;
+    RawSolutionTo265: RawSolutionTo265;
+    RawSolutionWith16: RawSolutionWith16;
+    RawSolutionWith24: RawSolutionWith24;
+    RawVRFOutput: RawVRFOutput;
+    ReadProof: ReadProof;
+    ReadySolution: ReadySolution;
+    Reasons: Reasons;
+    RecoveryConfig: RecoveryConfig;
+    RefCount: RefCount;
+    RefCountTo259: RefCountTo259;
+    ReferendumIndex: ReferendumIndex;
+    ReferendumInfo: ReferendumInfo;
+    ReferendumInfoFinished: ReferendumInfoFinished;
+    ReferendumInfoTo239: ReferendumInfoTo239;
+    ReferendumStatus: ReferendumStatus;
+    RegisteredParachainInfo: RegisteredParachainInfo;
+    RegistrarIndex: RegistrarIndex;
+    RegistrarInfo: RegistrarInfo;
+    Registration: Registration;
+    RegistrationJudgement: RegistrationJudgement;
+    RegistrationTo198: RegistrationTo198;
+    RelayBlockNumber: RelayBlockNumber;
+    RelayChainBlockNumber: RelayChainBlockNumber;
+    RelayChainHash: RelayChainHash;
+    RelayerId: RelayerId;
+    RelayHash: RelayHash;
+    Releases: Releases;
+    Remark: Remark;
+    Renouncing: Renouncing;
+    RentProjection: RentProjection;
+    ReplacementTimes: ReplacementTimes;
+    ReportedRoundStates: ReportedRoundStates;
+    Reporter: Reporter;
+    ReportIdOf: ReportIdOf;
+    ReserveData: ReserveData;
+    ReserveIdentifier: ReserveIdentifier;
+    Response: Response;
+    ResponseV0: ResponseV0;
+    ResponseV1: ResponseV1;
+    ResponseV2: ResponseV2;
+    ResponseV2Error: ResponseV2Error;
+    ResponseV2Result: ResponseV2Result;
+    Retriable: Retriable;
+    RewardDestination: RewardDestination;
+    RewardPoint: RewardPoint;
+    RoundSnapshot: RoundSnapshot;
+    RoundState: RoundState;
+    RpcMethods: RpcMethods;
+    RuntimeApiMetadataLatest: RuntimeApiMetadataLatest;
+    RuntimeApiMetadataV15: RuntimeApiMetadataV15;
+    RuntimeApiMethodMetadataV15: RuntimeApiMethodMetadataV15;
+    RuntimeApiMethodParamMetadataV15: RuntimeApiMethodParamMetadataV15;
+    RuntimeCall: RuntimeCall;
+    RuntimeDbWeight: RuntimeDbWeight;
+    RuntimeDispatchInfo: RuntimeDispatchInfo;
+    RuntimeDispatchInfoV1: RuntimeDispatchInfoV1;
+    RuntimeDispatchInfoV2: RuntimeDispatchInfoV2;
+    RuntimeEvent: RuntimeEvent;
+    RuntimeVersion: RuntimeVersion;
+    RuntimeVersionApi: RuntimeVersionApi;
+    RuntimeVersionPartial: RuntimeVersionPartial;
+    RuntimeVersionPre3: RuntimeVersionPre3;
+    RuntimeVersionPre4: RuntimeVersionPre4;
+    Schedule: Schedule;
+    Scheduled: Scheduled;
+    ScheduledCore: ScheduledCore;
+    ScheduledTo254: ScheduledTo254;
+    SchedulePeriod: SchedulePeriod;
+    SchedulePriority: SchedulePriority;
+    ScheduleTo212: ScheduleTo212;
+    ScheduleTo258: ScheduleTo258;
+    ScheduleTo264: ScheduleTo264;
+    Scheduling: Scheduling;
+    ScrapedOnChainVotes: ScrapedOnChainVotes;
+    Seal: Seal;
+    SealV0: SealV0;
+    SeatHolder: SeatHolder;
+    SeedOf: SeedOf;
+    ServiceQuality: ServiceQuality;
+    SessionIndex: SessionIndex;
+    SessionInfo: SessionInfo;
+    SessionInfoValidatorGroup: SessionInfoValidatorGroup;
+    SessionKeys1: SessionKeys1;
+    SessionKeys10: SessionKeys10;
+    SessionKeys10B: SessionKeys10B;
+    SessionKeys2: SessionKeys2;
+    SessionKeys3: SessionKeys3;
+    SessionKeys4: SessionKeys4;
+    SessionKeys5: SessionKeys5;
+    SessionKeys6: SessionKeys6;
+    SessionKeys6B: SessionKeys6B;
+    SessionKeys7: SessionKeys7;
+    SessionKeys7B: SessionKeys7B;
+    SessionKeys8: SessionKeys8;
+    SessionKeys8B: SessionKeys8B;
+    SessionKeys9: SessionKeys9;
+    SessionKeys9B: SessionKeys9B;
+    SetId: SetId;
+    SetIndex: SetIndex;
+    Si0Field: Si0Field;
+    Si0LookupTypeId: Si0LookupTypeId;
+    Si0Path: Si0Path;
+    Si0Type: Si0Type;
+    Si0TypeDef: Si0TypeDef;
+    Si0TypeDefArray: Si0TypeDefArray;
+    Si0TypeDefBitSequence: Si0TypeDefBitSequence;
+    Si0TypeDefCompact: Si0TypeDefCompact;
+    Si0TypeDefComposite: Si0TypeDefComposite;
+    Si0TypeDefPhantom: Si0TypeDefPhantom;
+    Si0TypeDefPrimitive: Si0TypeDefPrimitive;
+    Si0TypeDefSequence: Si0TypeDefSequence;
+    Si0TypeDefTuple: Si0TypeDefTuple;
+    Si0TypeDefVariant: Si0TypeDefVariant;
+    Si0TypeParameter: Si0TypeParameter;
+    Si0Variant: Si0Variant;
+    Si1Field: Si1Field;
+    Si1LookupTypeId: Si1LookupTypeId;
+    Si1Path: Si1Path;
+    Si1Type: Si1Type;
+    Si1TypeDef: Si1TypeDef;
+    Si1TypeDefArray: Si1TypeDefArray;
+    Si1TypeDefBitSequence: Si1TypeDefBitSequence;
+    Si1TypeDefCompact: Si1TypeDefCompact;
+    Si1TypeDefComposite: Si1TypeDefComposite;
+    Si1TypeDefPrimitive: Si1TypeDefPrimitive;
+    Si1TypeDefSequence: Si1TypeDefSequence;
+    Si1TypeDefTuple: Si1TypeDefTuple;
+    Si1TypeDefVariant: Si1TypeDefVariant;
+    Si1TypeParameter: Si1TypeParameter;
+    Si1Variant: Si1Variant;
+    SiField: SiField;
+    Signature: Signature;
+    SignedAvailabilityBitfield: SignedAvailabilityBitfield;
+    SignedAvailabilityBitfields: SignedAvailabilityBitfields;
+    SignedBlock: SignedBlock;
+    SignedBlockWithJustification: SignedBlockWithJustification;
+    SignedBlockWithJustifications: SignedBlockWithJustifications;
+    SignedExtensionMetadataLatest: SignedExtensionMetadataLatest;
+    SignedExtensionMetadataV14: SignedExtensionMetadataV14;
+    SignedSubmission: SignedSubmission;
+    SignedSubmissionOf: SignedSubmissionOf;
+    SignedSubmissionTo276: SignedSubmissionTo276;
+    SignerPayload: SignerPayload;
+    SigningContext: SigningContext;
+    SiLookupTypeId: SiLookupTypeId;
+    SiPath: SiPath;
+    SiType: SiType;
+    SiTypeDef: SiTypeDef;
+    SiTypeDefArray: SiTypeDefArray;
+    SiTypeDefBitSequence: SiTypeDefBitSequence;
+    SiTypeDefCompact: SiTypeDefCompact;
+    SiTypeDefComposite: SiTypeDefComposite;
+    SiTypeDefPrimitive: SiTypeDefPrimitive;
+    SiTypeDefSequence: SiTypeDefSequence;
+    SiTypeDefTuple: SiTypeDefTuple;
+    SiTypeDefVariant: SiTypeDefVariant;
+    SiTypeParameter: SiTypeParameter;
+    SiVariant: SiVariant;
+    SlashingOffenceKind: SlashingOffenceKind;
+    SlashingSpans: SlashingSpans;
+    SlashingSpansTo204: SlashingSpansTo204;
+    SlashJournalEntry: SlashJournalEntry;
+    Slot: Slot;
+    SlotDuration: SlotDuration;
+    SlotNumber: SlotNumber;
+    SlotRange: SlotRange;
+    SlotRange10: SlotRange10;
+    SocietyJudgement: SocietyJudgement;
+    SocietyVote: SocietyVote;
+    SolutionOrSnapshotSize: SolutionOrSnapshotSize;
+    SolutionSupport: SolutionSupport;
+    SolutionSupports: SolutionSupports;
+    SpanIndex: SpanIndex;
+    SpanRecord: SpanRecord;
+    SpArithmeticArithmeticError: SpArithmeticArithmeticError;
+    SpConsensusAuraSr25519AppSr25519Public: SpConsensusAuraSr25519AppSr25519Public;
+    SpCoreCryptoKeyTypeId: SpCoreCryptoKeyTypeId;
+    SpCoreEcdsaSignature: SpCoreEcdsaSignature;
+    SpCoreEd25519Signature: SpCoreEd25519Signature;
+    SpCoreSr25519Public: SpCoreSr25519Public;
+    SpCoreSr25519Signature: SpCoreSr25519Signature;
+    SpCoreVoid: SpCoreVoid;
+    SpecVersion: SpecVersion;
+    SpRuntimeDigest: SpRuntimeDigest;
+    SpRuntimeDigestDigestItem: SpRuntimeDigestDigestItem;
+    SpRuntimeDispatchError: SpRuntimeDispatchError;
+    SpRuntimeModuleError: SpRuntimeModuleError;
+    SpRuntimeMultiSignature: SpRuntimeMultiSignature;
+    SpRuntimeTokenError: SpRuntimeTokenError;
+    SpRuntimeTransactionalError: SpRuntimeTransactionalError;
+    SpRuntimeTransactionValidityInvalidTransaction: SpRuntimeTransactionValidityInvalidTransaction;
+    SpRuntimeTransactionValidityTransactionValidityError: SpRuntimeTransactionValidityTransactionValidityError;
+    SpRuntimeTransactionValidityUnknownTransaction: SpRuntimeTransactionValidityUnknownTransaction;
+    SpTrieStorageProof: SpTrieStorageProof;
+    SpVersionRuntimeVersion: SpVersionRuntimeVersion;
+    SpWeightsRuntimeDbWeight: SpWeightsRuntimeDbWeight;
+    SpWeightsWeightV2Weight: SpWeightsWeightV2Weight;
+    Sr25519Signature: Sr25519Signature;
+    StagingXcmDoubleEncoded: StagingXcmDoubleEncoded;
+    StagingXcmV2BodyId: StagingXcmV2BodyId;
+    StagingXcmV2BodyPart: StagingXcmV2BodyPart;
+    StagingXcmV2Instruction: StagingXcmV2Instruction;
+    StagingXcmV2Junction: StagingXcmV2Junction;
+    StagingXcmV2MultiAsset: StagingXcmV2MultiAsset;
+    StagingXcmV2MultiassetAssetId: StagingXcmV2MultiassetAssetId;
+    StagingXcmV2MultiassetAssetInstance: StagingXcmV2MultiassetAssetInstance;
+    StagingXcmV2MultiassetFungibility: StagingXcmV2MultiassetFungibility;
+    StagingXcmV2MultiassetMultiAssetFilter: StagingXcmV2MultiassetMultiAssetFilter;
+    StagingXcmV2MultiassetMultiAssets: StagingXcmV2MultiassetMultiAssets;
+    StagingXcmV2MultiassetWildFungibility: StagingXcmV2MultiassetWildFungibility;
+    StagingXcmV2MultiassetWildMultiAsset: StagingXcmV2MultiassetWildMultiAsset;
+    StagingXcmV2MultiLocation: StagingXcmV2MultiLocation;
+    StagingXcmV2MultilocationJunctions: StagingXcmV2MultilocationJunctions;
+    StagingXcmV2NetworkId: StagingXcmV2NetworkId;
+    StagingXcmV2OriginKind: StagingXcmV2OriginKind;
+    StagingXcmV2Response: StagingXcmV2Response;
+    StagingXcmV2TraitsError: StagingXcmV2TraitsError;
+    StagingXcmV2WeightLimit: StagingXcmV2WeightLimit;
+    StagingXcmV2Xcm: StagingXcmV2Xcm;
+    StagingXcmV3Instruction: StagingXcmV3Instruction;
+    StagingXcmV3Junction: StagingXcmV3Junction;
+    StagingXcmV3JunctionBodyId: StagingXcmV3JunctionBodyId;
+    StagingXcmV3JunctionBodyPart: StagingXcmV3JunctionBodyPart;
+    StagingXcmV3JunctionNetworkId: StagingXcmV3JunctionNetworkId;
+    StagingXcmV3Junctions: StagingXcmV3Junctions;
+    StagingXcmV3MaybeErrorCode: StagingXcmV3MaybeErrorCode;
+    StagingXcmV3MultiAsset: StagingXcmV3MultiAsset;
+    StagingXcmV3MultiassetAssetId: StagingXcmV3MultiassetAssetId;
+    StagingXcmV3MultiassetAssetInstance: StagingXcmV3MultiassetAssetInstance;
+    StagingXcmV3MultiassetFungibility: StagingXcmV3MultiassetFungibility;
+    StagingXcmV3MultiassetMultiAssetFilter: StagingXcmV3MultiassetMultiAssetFilter;
+    StagingXcmV3MultiassetMultiAssets: StagingXcmV3MultiassetMultiAssets;
+    StagingXcmV3MultiassetWildFungibility: StagingXcmV3MultiassetWildFungibility;
+    StagingXcmV3MultiassetWildMultiAsset: StagingXcmV3MultiassetWildMultiAsset;
+    StagingXcmV3MultiLocation: StagingXcmV3MultiLocation;
+    StagingXcmV3PalletInfo: StagingXcmV3PalletInfo;
+    StagingXcmV3QueryResponseInfo: StagingXcmV3QueryResponseInfo;
+    StagingXcmV3Response: StagingXcmV3Response;
+    StagingXcmV3TraitsError: StagingXcmV3TraitsError;
+    StagingXcmV3TraitsOutcome: StagingXcmV3TraitsOutcome;
+    StagingXcmV3WeightLimit: StagingXcmV3WeightLimit;
+    StagingXcmV3Xcm: StagingXcmV3Xcm;
+    StagingXcmVersionedAssetId: StagingXcmVersionedAssetId;
+    StagingXcmVersionedMultiAsset: StagingXcmVersionedMultiAsset;
+    StagingXcmVersionedMultiAssets: StagingXcmVersionedMultiAssets;
+    StagingXcmVersionedMultiLocation: StagingXcmVersionedMultiLocation;
+    StagingXcmVersionedResponse: StagingXcmVersionedResponse;
+    StagingXcmVersionedXcm: StagingXcmVersionedXcm;
+    StakingLedger: StakingLedger;
+    StakingLedgerTo223: StakingLedgerTo223;
+    StakingLedgerTo240: StakingLedgerTo240;
+    Statement: Statement;
+    StatementKind: StatementKind;
+    StorageChangeSet: StorageChangeSet;
+    StorageData: StorageData;
+    StorageDeposit: StorageDeposit;
+    StorageEntryMetadataLatest: StorageEntryMetadataLatest;
+    StorageEntryMetadataV10: StorageEntryMetadataV10;
+    StorageEntryMetadataV11: StorageEntryMetadataV11;
+    StorageEntryMetadataV12: StorageEntryMetadataV12;
+    StorageEntryMetadataV13: StorageEntryMetadataV13;
+    StorageEntryMetadataV14: StorageEntryMetadataV14;
+    StorageEntryMetadataV9: StorageEntryMetadataV9;
+    StorageEntryModifierLatest: StorageEntryModifierLatest;
+    StorageEntryModifierV10: StorageEntryModifierV10;
+    StorageEntryModifierV11: StorageEntryModifierV11;
+    StorageEntryModifierV12: StorageEntryModifierV12;
+    StorageEntryModifierV13: StorageEntryModifierV13;
+    StorageEntryModifierV14: StorageEntryModifierV14;
+    StorageEntryModifierV9: StorageEntryModifierV9;
+    StorageEntryTypeLatest: StorageEntryTypeLatest;
+    StorageEntryTypeV10: StorageEntryTypeV10;
+    StorageEntryTypeV11: StorageEntryTypeV11;
+    StorageEntryTypeV12: StorageEntryTypeV12;
+    StorageEntryTypeV13: StorageEntryTypeV13;
+    StorageEntryTypeV14: StorageEntryTypeV14;
+    StorageEntryTypeV9: StorageEntryTypeV9;
+    StorageHasher: StorageHasher;
+    StorageHasherV10: StorageHasherV10;
+    StorageHasherV11: StorageHasherV11;
+    StorageHasherV12: StorageHasherV12;
+    StorageHasherV13: StorageHasherV13;
+    StorageHasherV14: StorageHasherV14;
+    StorageHasherV9: StorageHasherV9;
+    StorageInfo: StorageInfo;
+    StorageKey: StorageKey;
+    StorageKind: StorageKind;
+    StorageMetadataV10: StorageMetadataV10;
+    StorageMetadataV11: StorageMetadataV11;
+    StorageMetadataV12: StorageMetadataV12;
+    StorageMetadataV13: StorageMetadataV13;
+    StorageMetadataV9: StorageMetadataV9;
+    StorageProof: StorageProof;
+    StoredPendingChange: StoredPendingChange;
+    StoredState: StoredState;
+    StrikeCount: StrikeCount;
+    SubId: SubId;
+    SubmissionIndicesOf: SubmissionIndicesOf;
+    Supports: Supports;
+    SyncState: SyncState;
+    SystemInherentData: SystemInherentData;
+    SystemOrigin: SystemOrigin;
+    Tally: Tally;
+    TaskAddress: TaskAddress;
+    TAssetBalance: TAssetBalance;
+    TAssetDepositBalance: TAssetDepositBalance;
+    Text: Text;
+    Timepoint: Timepoint;
+    TokenError: TokenError;
+    TombstoneContractInfo: TombstoneContractInfo;
+    TraceBlockResponse: TraceBlockResponse;
+    TraceError: TraceError;
+    TransactionalError: TransactionalError;
+    TransactionInfo: TransactionInfo;
+    TransactionLongevity: TransactionLongevity;
+    TransactionPriority: TransactionPriority;
+    TransactionSource: TransactionSource;
+    TransactionStorageProof: TransactionStorageProof;
+    TransactionTag: TransactionTag;
+    TransactionV0: TransactionV0;
+    TransactionV1: TransactionV1;
+    TransactionV2: TransactionV2;
+    TransactionValidity: TransactionValidity;
+    TransactionValidityError: TransactionValidityError;
+    TransientValidationData: TransientValidationData;
+    TreasuryProposal: TreasuryProposal;
+    TrieId: TrieId;
+    TrieIndex: TrieIndex;
+    Type: Type;
+    u128: u128;
+    U128: U128;
+    u16: u16;
+    U16: U16;
+    u256: u256;
+    U256: U256;
+    u32: u32;
+    U32: U32;
+    U32F32: U32F32;
+    u64: u64;
+    U64: U64;
+    u8: u8;
+    U8: U8;
+    UnappliedSlash: UnappliedSlash;
+    UnappliedSlashOther: UnappliedSlashOther;
+    UncleEntryItem: UncleEntryItem;
+    UnknownTransaction: UnknownTransaction;
+    UnlockChunk: UnlockChunk;
+    UnrewardedRelayer: UnrewardedRelayer;
+    UnrewardedRelayersState: UnrewardedRelayersState;
+    UpDataStructsAccessMode: UpDataStructsAccessMode;
+    UpDataStructsCollection: UpDataStructsCollection;
+    UpDataStructsCollectionLimits: UpDataStructsCollectionLimits;
+    UpDataStructsCollectionMode: UpDataStructsCollectionMode;
+    UpDataStructsCollectionPermissions: UpDataStructsCollectionPermissions;
+    UpDataStructsCollectionStats: UpDataStructsCollectionStats;
+    UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData;
+    UpDataStructsCreateFungibleData: UpDataStructsCreateFungibleData;
+    UpDataStructsCreateItemData: UpDataStructsCreateItemData;
+    UpDataStructsCreateItemExData: UpDataStructsCreateItemExData;
+    UpDataStructsCreateNftData: UpDataStructsCreateNftData;
+    UpDataStructsCreateNftExData: UpDataStructsCreateNftExData;
+    UpDataStructsCreateReFungibleData: UpDataStructsCreateReFungibleData;
+    UpDataStructsCreateRefungibleExMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners;
+    UpDataStructsCreateRefungibleExSingleOwner: UpDataStructsCreateRefungibleExSingleOwner;
+    UpDataStructsNestingPermissions: UpDataStructsNestingPermissions;
+    UpDataStructsOwnerRestrictedSet: UpDataStructsOwnerRestrictedSet;
+    UpDataStructsProperties: UpDataStructsProperties;
+    UpDataStructsPropertiesMapBoundedVec: UpDataStructsPropertiesMapBoundedVec;
+    UpDataStructsPropertiesMapPropertyPermission: UpDataStructsPropertiesMapPropertyPermission;
+    UpDataStructsProperty: UpDataStructsProperty;
+    UpDataStructsPropertyKeyPermission: UpDataStructsPropertyKeyPermission;
+    UpDataStructsPropertyPermission: UpDataStructsPropertyPermission;
+    UpDataStructsPropertyScope: UpDataStructsPropertyScope;
+    UpDataStructsRpcCollection: UpDataStructsRpcCollection;
+    UpDataStructsRpcCollectionFlags: UpDataStructsRpcCollectionFlags;
+    UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit;
+    UpDataStructsSponsorshipStateAccountId32: UpDataStructsSponsorshipStateAccountId32;
+    UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: UpDataStructsSponsorshipStateBasicCrossAccountIdRepr;
+    UpDataStructsTokenChild: UpDataStructsTokenChild;
+    UpDataStructsTokenData: UpDataStructsTokenData;
+    UpgradeGoAhead: UpgradeGoAhead;
+    UpgradeRestriction: UpgradeRestriction;
+    UpPovEstimateRpcPovInfo: UpPovEstimateRpcPovInfo;
+    UpPovEstimateRpcTrieKeyValue: UpPovEstimateRpcTrieKeyValue;
+    UpwardMessage: UpwardMessage;
+    usize: usize;
+    USize: USize;
+    ValidationCode: ValidationCode;
+    ValidationCodeHash: ValidationCodeHash;
+    ValidationData: ValidationData;
+    ValidationDataType: ValidationDataType;
+    ValidationFunctionParams: ValidationFunctionParams;
+    ValidatorCount: ValidatorCount;
+    ValidatorId: ValidatorId;
+    ValidatorIdOf: ValidatorIdOf;
+    ValidatorIndex: ValidatorIndex;
+    ValidatorIndexCompact: ValidatorIndexCompact;
+    ValidatorPrefs: ValidatorPrefs;
+    ValidatorPrefsTo145: ValidatorPrefsTo145;
+    ValidatorPrefsTo196: ValidatorPrefsTo196;
+    ValidatorPrefsWithBlocked: ValidatorPrefsWithBlocked;
+    ValidatorPrefsWithCommission: ValidatorPrefsWithCommission;
+    ValidatorSet: ValidatorSet;
+    ValidatorSetId: ValidatorSetId;
+    ValidatorSignature: ValidatorSignature;
+    ValidDisputeStatementKind: ValidDisputeStatementKind;
+    ValidityAttestation: ValidityAttestation;
+    ValidTransaction: ValidTransaction;
+    VecInboundHrmpMessage: VecInboundHrmpMessage;
+    VersionedMultiAsset: VersionedMultiAsset;
+    VersionedMultiAssets: VersionedMultiAssets;
+    VersionedMultiLocation: VersionedMultiLocation;
+    VersionedResponse: VersionedResponse;
+    VersionedXcm: VersionedXcm;
+    VersionMigrationStage: VersionMigrationStage;
+    VestingInfo: VestingInfo;
+    VestingSchedule: VestingSchedule;
+    Vote: Vote;
+    VoteIndex: VoteIndex;
+    Voter: Voter;
+    VoterInfo: VoterInfo;
+    Votes: Votes;
+    VotesTo230: VotesTo230;
+    VoteThreshold: VoteThreshold;
+    VoteWeight: VoteWeight;
+    Voting: Voting;
+    VotingDelegating: VotingDelegating;
+    VotingDirect: VotingDirect;
+    VotingDirectVote: VotingDirectVote;
+    VouchingStatus: VouchingStatus;
+    VrfData: VrfData;
+    VrfOutput: VrfOutput;
+    VrfProof: VrfProof;
+    Weight: Weight;
+    WeightLimitV2: WeightLimitV2;
+    WeightMultiplier: WeightMultiplier;
+    WeightPerClass: WeightPerClass;
+    WeightToFeeCoefficient: WeightToFeeCoefficient;
+    WeightV0: WeightV0;
+    WeightV1: WeightV1;
+    WeightV2: WeightV2;
+    WildFungibility: WildFungibility;
+    WildFungibilityV0: WildFungibilityV0;
+    WildFungibilityV1: WildFungibilityV1;
+    WildFungibilityV2: WildFungibilityV2;
+    WildMultiAsset: WildMultiAsset;
+    WildMultiAssetV1: WildMultiAssetV1;
+    WildMultiAssetV2: WildMultiAssetV2;
+    WinnersData: WinnersData;
+    WinnersData10: WinnersData10;
+    WinnersDataTuple: WinnersDataTuple;
+    WinnersDataTuple10: WinnersDataTuple10;
+    WinningData: WinningData;
+    WinningData10: WinningData10;
+    WinningDataEntry: WinningDataEntry;
+    WithdrawReasons: WithdrawReasons;
+    Xcm: Xcm;
+    XcmAssetId: XcmAssetId;
+    XcmError: XcmError;
+    XcmErrorV0: XcmErrorV0;
+    XcmErrorV1: XcmErrorV1;
+    XcmErrorV2: XcmErrorV2;
+    XcmOrder: XcmOrder;
+    XcmOrderV0: XcmOrderV0;
+    XcmOrderV1: XcmOrderV1;
+    XcmOrderV2: XcmOrderV2;
+    XcmOrigin: XcmOrigin;
+    XcmOriginKind: XcmOriginKind;
+    XcmpMessageFormat: XcmpMessageFormat;
+    XcmV0: XcmV0;
+    XcmV1: XcmV1;
+    XcmV2: XcmV2;
+    XcmVersion: XcmVersion;
+  } // InterfaceTypes
+} // declare module
addedjs-packages/types/default/definitions.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/default/definitions.ts
@@ -0,0 +1,6 @@
+import types from '../lookup.js';
+
+export default {
+  types,
+  rpc: {},
+};
addedjs-packages/types/default/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/default/index.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export * from './types.js';
addedjs-packages/types/default/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/default/types.ts
@@ -0,0 +1,5537 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+import type { Data } from '@polkadot/types';
+import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
+import type { ITuple } from '@polkadot/types-codec/types';
+import type { Vote } from '@polkadot/types/interfaces/elections';
+import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime';
+import type { Event } from '@polkadot/types/interfaces/system';
+
+/** @name CumulusPalletDmpQueueCall */
+export interface CumulusPalletDmpQueueCall extends Enum {
+  readonly isServiceOverweight: boolean;
+  readonly asServiceOverweight: {
+    readonly index: u64;
+    readonly weightLimit: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly type: 'ServiceOverweight';
+}
+
+/** @name CumulusPalletDmpQueueConfigData */
+export interface CumulusPalletDmpQueueConfigData extends Struct {
+  readonly maxIndividual: SpWeightsWeightV2Weight;
+}
+
+/** @name CumulusPalletDmpQueueError */
+export interface CumulusPalletDmpQueueError extends Enum {
+  readonly isUnknown: boolean;
+  readonly isOverLimit: boolean;
+  readonly type: 'Unknown' | 'OverLimit';
+}
+
+/** @name CumulusPalletDmpQueueEvent */
+export interface CumulusPalletDmpQueueEvent extends Enum {
+  readonly isInvalidFormat: boolean;
+  readonly asInvalidFormat: {
+    readonly messageHash: U8aFixed;
+  } & Struct;
+  readonly isUnsupportedVersion: boolean;
+  readonly asUnsupportedVersion: {
+    readonly messageHash: U8aFixed;
+  } & Struct;
+  readonly isExecutedDownward: boolean;
+  readonly asExecutedDownward: {
+    readonly messageHash: U8aFixed;
+    readonly messageId: U8aFixed;
+    readonly outcome: StagingXcmV3TraitsOutcome;
+  } & Struct;
+  readonly isWeightExhausted: boolean;
+  readonly asWeightExhausted: {
+    readonly messageHash: U8aFixed;
+    readonly messageId: U8aFixed;
+    readonly remainingWeight: SpWeightsWeightV2Weight;
+    readonly requiredWeight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isOverweightEnqueued: boolean;
+  readonly asOverweightEnqueued: {
+    readonly messageHash: U8aFixed;
+    readonly messageId: U8aFixed;
+    readonly overweightIndex: u64;
+    readonly requiredWeight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isOverweightServiced: boolean;
+  readonly asOverweightServiced: {
+    readonly overweightIndex: u64;
+    readonly weightUsed: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isMaxMessagesExhausted: boolean;
+  readonly asMaxMessagesExhausted: {
+    readonly messageHash: U8aFixed;
+  } & Struct;
+  readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted';
+}
+
+/** @name CumulusPalletDmpQueuePageIndexData */
+export interface CumulusPalletDmpQueuePageIndexData extends Struct {
+  readonly beginUsed: u32;
+  readonly endUsed: u32;
+  readonly overweightCount: u64;
+}
+
+/** @name CumulusPalletParachainSystemCall */
+export interface CumulusPalletParachainSystemCall extends Enum {
+  readonly isSetValidationData: boolean;
+  readonly asSetValidationData: {
+    readonly data: CumulusPrimitivesParachainInherentParachainInherentData;
+  } & Struct;
+  readonly isSudoSendUpwardMessage: boolean;
+  readonly asSudoSendUpwardMessage: {
+    readonly message: Bytes;
+  } & Struct;
+  readonly isAuthorizeUpgrade: boolean;
+  readonly asAuthorizeUpgrade: {
+    readonly codeHash: H256;
+    readonly checkVersion: bool;
+  } & Struct;
+  readonly isEnactAuthorizedUpgrade: boolean;
+  readonly asEnactAuthorizedUpgrade: {
+    readonly code: Bytes;
+  } & Struct;
+  readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade';
+}
+
+/** @name CumulusPalletParachainSystemCodeUpgradeAuthorization */
+export interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct {
+  readonly codeHash: H256;
+  readonly checkVersion: bool;
+}
+
+/** @name CumulusPalletParachainSystemError */
+export interface CumulusPalletParachainSystemError extends Enum {
+  readonly isOverlappingUpgrades: boolean;
+  readonly isProhibitedByPolkadot: boolean;
+  readonly isTooBig: boolean;
+  readonly isValidationDataNotAvailable: boolean;
+  readonly isHostConfigurationNotAvailable: boolean;
+  readonly isNotScheduled: boolean;
+  readonly isNothingAuthorized: boolean;
+  readonly isUnauthorized: boolean;
+  readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized';
+}
+
+/** @name CumulusPalletParachainSystemEvent */
+export interface CumulusPalletParachainSystemEvent extends Enum {
+  readonly isValidationFunctionStored: boolean;
+  readonly isValidationFunctionApplied: boolean;
+  readonly asValidationFunctionApplied: {
+    readonly relayChainBlockNum: u32;
+  } & Struct;
+  readonly isValidationFunctionDiscarded: boolean;
+  readonly isUpgradeAuthorized: boolean;
+  readonly asUpgradeAuthorized: {
+    readonly codeHash: H256;
+  } & Struct;
+  readonly isDownwardMessagesReceived: boolean;
+  readonly asDownwardMessagesReceived: {
+    readonly count: u32;
+  } & Struct;
+  readonly isDownwardMessagesProcessed: boolean;
+  readonly asDownwardMessagesProcessed: {
+    readonly weightUsed: SpWeightsWeightV2Weight;
+    readonly dmqHead: H256;
+  } & Struct;
+  readonly isUpwardMessageSent: boolean;
+  readonly asUpwardMessageSent: {
+    readonly messageHash: Option<U8aFixed>;
+  } & Struct;
+  readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent';
+}
+
+/** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot */
+export interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct {
+  readonly dmqMqcHead: H256;
+  readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity;
+  readonly ingressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
+  readonly egressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
+}
+
+/** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity */
+export interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct {
+  readonly remainingCount: u32;
+  readonly remainingSize: u32;
+}
+
+/** @name CumulusPalletParachainSystemUnincludedSegmentAncestor */
+export interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct {
+  readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
+  readonly paraHeadHash: Option<H256>;
+  readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
+}
+
+/** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate */
+export interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct {
+  readonly msgCount: u32;
+  readonly totalBytes: u32;
+}
+
+/** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker */
+export interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct {
+  readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
+  readonly hrmpWatermark: Option<u32>;
+  readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
+}
+
+/** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth */
+export interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct {
+  readonly umpMsgCount: u32;
+  readonly umpTotalBytes: u32;
+  readonly hrmpOutgoing: BTreeMap<u32, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate>;
+}
+
+/** @name CumulusPalletXcmCall */
+export interface CumulusPalletXcmCall extends Null {}
+
+/** @name CumulusPalletXcmError */
+export interface CumulusPalletXcmError extends Null {}
+
+/** @name CumulusPalletXcmEvent */
+export interface CumulusPalletXcmEvent extends Enum {
+  readonly isInvalidFormat: boolean;
+  readonly asInvalidFormat: U8aFixed;
+  readonly isUnsupportedVersion: boolean;
+  readonly asUnsupportedVersion: U8aFixed;
+  readonly isExecutedDownward: boolean;
+  readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>;
+  readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward';
+}
+
+/** @name CumulusPalletXcmOrigin */
+export interface CumulusPalletXcmOrigin extends Enum {
+  readonly isRelay: boolean;
+  readonly isSiblingParachain: boolean;
+  readonly asSiblingParachain: u32;
+  readonly type: 'Relay' | 'SiblingParachain';
+}
+
+/** @name CumulusPalletXcmpQueueCall */
+export interface CumulusPalletXcmpQueueCall extends Enum {
+  readonly isServiceOverweight: boolean;
+  readonly asServiceOverweight: {
+    readonly index: u64;
+    readonly weightLimit: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isSuspendXcmExecution: boolean;
+  readonly isResumeXcmExecution: boolean;
+  readonly isUpdateSuspendThreshold: boolean;
+  readonly asUpdateSuspendThreshold: {
+    readonly new_: u32;
+  } & Struct;
+  readonly isUpdateDropThreshold: boolean;
+  readonly asUpdateDropThreshold: {
+    readonly new_: u32;
+  } & Struct;
+  readonly isUpdateResumeThreshold: boolean;
+  readonly asUpdateResumeThreshold: {
+    readonly new_: u32;
+  } & Struct;
+  readonly isUpdateThresholdWeight: boolean;
+  readonly asUpdateThresholdWeight: {
+    readonly new_: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isUpdateWeightRestrictDecay: boolean;
+  readonly asUpdateWeightRestrictDecay: {
+    readonly new_: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isUpdateXcmpMaxIndividualWeight: boolean;
+  readonly asUpdateXcmpMaxIndividualWeight: {
+    readonly new_: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight';
+}
+
+/** @name CumulusPalletXcmpQueueError */
+export interface CumulusPalletXcmpQueueError extends Enum {
+  readonly isFailedToSend: boolean;
+  readonly isBadXcmOrigin: boolean;
+  readonly isBadXcm: boolean;
+  readonly isBadOverweightIndex: boolean;
+  readonly isWeightOverLimit: boolean;
+  readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
+}
+
+/** @name CumulusPalletXcmpQueueEvent */
+export interface CumulusPalletXcmpQueueEvent extends Enum {
+  readonly isSuccess: boolean;
+  readonly asSuccess: {
+    readonly messageHash: U8aFixed;
+    readonly messageId: U8aFixed;
+    readonly weight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isFail: boolean;
+  readonly asFail: {
+    readonly messageHash: U8aFixed;
+    readonly messageId: U8aFixed;
+    readonly error: StagingXcmV3TraitsError;
+    readonly weight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isBadVersion: boolean;
+  readonly asBadVersion: {
+    readonly messageHash: U8aFixed;
+  } & Struct;
+  readonly isBadFormat: boolean;
+  readonly asBadFormat: {
+    readonly messageHash: U8aFixed;
+  } & Struct;
+  readonly isXcmpMessageSent: boolean;
+  readonly asXcmpMessageSent: {
+    readonly messageHash: U8aFixed;
+  } & Struct;
+  readonly isOverweightEnqueued: boolean;
+  readonly asOverweightEnqueued: {
+    readonly sender: u32;
+    readonly sentAt: u32;
+    readonly index: u64;
+    readonly required: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isOverweightServiced: boolean;
+  readonly asOverweightServiced: {
+    readonly index: u64;
+    readonly used: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced';
+}
+
+/** @name CumulusPalletXcmpQueueInboundChannelDetails */
+export interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
+  readonly sender: u32;
+  readonly state: CumulusPalletXcmpQueueInboundState;
+  readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat]>>;
+}
+
+/** @name CumulusPalletXcmpQueueInboundState */
+export interface CumulusPalletXcmpQueueInboundState extends Enum {
+  readonly isOk: boolean;
+  readonly isSuspended: boolean;
+  readonly type: 'Ok' | 'Suspended';
+}
+
+/** @name CumulusPalletXcmpQueueOutboundChannelDetails */
+export interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
+  readonly recipient: u32;
+  readonly state: CumulusPalletXcmpQueueOutboundState;
+  readonly signalsExist: bool;
+  readonly firstIndex: u16;
+  readonly lastIndex: u16;
+}
+
+/** @name CumulusPalletXcmpQueueOutboundState */
+export interface CumulusPalletXcmpQueueOutboundState extends Enum {
+  readonly isOk: boolean;
+  readonly isSuspended: boolean;
+  readonly type: 'Ok' | 'Suspended';
+}
+
+/** @name CumulusPalletXcmpQueueQueueConfigData */
+export interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
+  readonly suspendThreshold: u32;
+  readonly dropThreshold: u32;
+  readonly resumeThreshold: u32;
+  readonly thresholdWeight: SpWeightsWeightV2Weight;
+  readonly weightRestrictDecay: SpWeightsWeightV2Weight;
+  readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight;
+}
+
+/** @name CumulusPrimitivesParachainInherentParachainInherentData */
+export interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct {
+  readonly validationData: PolkadotPrimitivesV5PersistedValidationData;
+  readonly relayChainState: SpTrieStorageProof;
+  readonly downwardMessages: Vec<PolkadotCorePrimitivesInboundDownwardMessage>;
+  readonly horizontalMessages: BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>;
+}
+
+/** @name EthbloomBloom */
+export interface EthbloomBloom extends U8aFixed {}
+
+/** @name EthereumBlock */
+export interface EthereumBlock extends Struct {
+  readonly header: EthereumHeader;
+  readonly transactions: Vec<EthereumTransactionTransactionV2>;
+  readonly ommers: Vec<EthereumHeader>;
+}
+
+/** @name EthereumHeader */
+export interface EthereumHeader extends Struct {
+  readonly parentHash: H256;
+  readonly ommersHash: H256;
+  readonly beneficiary: H160;
+  readonly stateRoot: H256;
+  readonly transactionsRoot: H256;
+  readonly receiptsRoot: H256;
+  readonly logsBloom: EthbloomBloom;
+  readonly difficulty: U256;
+  readonly number: U256;
+  readonly gasLimit: U256;
+  readonly gasUsed: U256;
+  readonly timestamp: u64;
+  readonly extraData: Bytes;
+  readonly mixHash: H256;
+  readonly nonce: EthereumTypesHashH64;
+}
+
+/** @name EthereumLog */
+export interface EthereumLog extends Struct {
+  readonly address: H160;
+  readonly topics: Vec<H256>;
+  readonly data: Bytes;
+}
+
+/** @name EthereumReceiptEip658ReceiptData */
+export interface EthereumReceiptEip658ReceiptData extends Struct {
+  readonly statusCode: u8;
+  readonly usedGas: U256;
+  readonly logsBloom: EthbloomBloom;
+  readonly logs: Vec<EthereumLog>;
+}
+
+/** @name EthereumReceiptReceiptV3 */
+export interface EthereumReceiptReceiptV3 extends Enum {
+  readonly isLegacy: boolean;
+  readonly asLegacy: EthereumReceiptEip658ReceiptData;
+  readonly isEip2930: boolean;
+  readonly asEip2930: EthereumReceiptEip658ReceiptData;
+  readonly isEip1559: boolean;
+  readonly asEip1559: EthereumReceiptEip658ReceiptData;
+  readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
+}
+
+/** @name EthereumTransactionAccessListItem */
+export interface EthereumTransactionAccessListItem extends Struct {
+  readonly address: H160;
+  readonly storageKeys: Vec<H256>;
+}
+
+/** @name EthereumTransactionEip1559Transaction */
+export interface EthereumTransactionEip1559Transaction extends Struct {
+  readonly chainId: u64;
+  readonly nonce: U256;
+  readonly maxPriorityFeePerGas: U256;
+  readonly maxFeePerGas: U256;
+  readonly gasLimit: U256;
+  readonly action: EthereumTransactionTransactionAction;
+  readonly value: U256;
+  readonly input: Bytes;
+  readonly accessList: Vec<EthereumTransactionAccessListItem>;
+  readonly oddYParity: bool;
+  readonly r: H256;
+  readonly s: H256;
+}
+
+/** @name EthereumTransactionEip2930Transaction */
+export interface EthereumTransactionEip2930Transaction extends Struct {
+  readonly chainId: u64;
+  readonly nonce: U256;
+  readonly gasPrice: U256;
+  readonly gasLimit: U256;
+  readonly action: EthereumTransactionTransactionAction;
+  readonly value: U256;
+  readonly input: Bytes;
+  readonly accessList: Vec<EthereumTransactionAccessListItem>;
+  readonly oddYParity: bool;
+  readonly r: H256;
+  readonly s: H256;
+}
+
+/** @name EthereumTransactionLegacyTransaction */
+export interface EthereumTransactionLegacyTransaction extends Struct {
+  readonly nonce: U256;
+  readonly gasPrice: U256;
+  readonly gasLimit: U256;
+  readonly action: EthereumTransactionTransactionAction;
+  readonly value: U256;
+  readonly input: Bytes;
+  readonly signature: EthereumTransactionTransactionSignature;
+}
+
+/** @name EthereumTransactionTransactionAction */
+export interface EthereumTransactionTransactionAction extends Enum {
+  readonly isCall: boolean;
+  readonly asCall: H160;
+  readonly isCreate: boolean;
+  readonly type: 'Call' | 'Create';
+}
+
+/** @name EthereumTransactionTransactionSignature */
+export interface EthereumTransactionTransactionSignature extends Struct {
+  readonly v: u64;
+  readonly r: H256;
+  readonly s: H256;
+}
+
+/** @name EthereumTransactionTransactionV2 */
+export interface EthereumTransactionTransactionV2 extends Enum {
+  readonly isLegacy: boolean;
+  readonly asLegacy: EthereumTransactionLegacyTransaction;
+  readonly isEip2930: boolean;
+  readonly asEip2930: EthereumTransactionEip2930Transaction;
+  readonly isEip1559: boolean;
+  readonly asEip1559: EthereumTransactionEip1559Transaction;
+  readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
+}
+
+/** @name EthereumTypesHashH64 */
+export interface EthereumTypesHashH64 extends U8aFixed {}
+
+/** @name EvmCoreErrorExitError */
+export interface EvmCoreErrorExitError extends Enum {
+  readonly isStackUnderflow: boolean;
+  readonly isStackOverflow: boolean;
+  readonly isInvalidJump: boolean;
+  readonly isInvalidRange: boolean;
+  readonly isDesignatedInvalid: boolean;
+  readonly isCallTooDeep: boolean;
+  readonly isCreateCollision: boolean;
+  readonly isCreateContractLimit: boolean;
+  readonly isOutOfOffset: boolean;
+  readonly isOutOfGas: boolean;
+  readonly isOutOfFund: boolean;
+  readonly isPcUnderflow: boolean;
+  readonly isCreateEmpty: boolean;
+  readonly isOther: boolean;
+  readonly asOther: Text;
+  readonly isMaxNonce: boolean;
+  readonly isInvalidCode: boolean;
+  readonly asInvalidCode: u8;
+  readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode';
+}
+
+/** @name EvmCoreErrorExitFatal */
+export interface EvmCoreErrorExitFatal extends Enum {
+  readonly isNotSupported: boolean;
+  readonly isUnhandledInterrupt: boolean;
+  readonly isCallErrorAsFatal: boolean;
+  readonly asCallErrorAsFatal: EvmCoreErrorExitError;
+  readonly isOther: boolean;
+  readonly asOther: Text;
+  readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other';
+}
+
+/** @name EvmCoreErrorExitReason */
+export interface EvmCoreErrorExitReason extends Enum {
+  readonly isSucceed: boolean;
+  readonly asSucceed: EvmCoreErrorExitSucceed;
+  readonly isError: boolean;
+  readonly asError: EvmCoreErrorExitError;
+  readonly isRevert: boolean;
+  readonly asRevert: EvmCoreErrorExitRevert;
+  readonly isFatal: boolean;
+  readonly asFatal: EvmCoreErrorExitFatal;
+  readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal';
+}
+
+/** @name EvmCoreErrorExitRevert */
+export interface EvmCoreErrorExitRevert extends Enum {
+  readonly isReverted: boolean;
+  readonly type: 'Reverted';
+}
+
+/** @name EvmCoreErrorExitSucceed */
+export interface EvmCoreErrorExitSucceed extends Enum {
+  readonly isStopped: boolean;
+  readonly isReturned: boolean;
+  readonly isSuicided: boolean;
+  readonly type: 'Stopped' | 'Returned' | 'Suicided';
+}
+
+/** @name FpRpcTransactionStatus */
+export interface FpRpcTransactionStatus extends Struct {
+  readonly transactionHash: H256;
+  readonly transactionIndex: u32;
+  readonly from: H160;
+  readonly to: Option<H160>;
+  readonly contractAddress: Option<H160>;
+  readonly logs: Vec<EthereumLog>;
+  readonly logsBloom: EthbloomBloom;
+}
+
+/** @name FrameSupportDispatchDispatchClass */
+export interface FrameSupportDispatchDispatchClass extends Enum {
+  readonly isNormal: boolean;
+  readonly isOperational: boolean;
+  readonly isMandatory: boolean;
+  readonly type: 'Normal' | 'Operational' | 'Mandatory';
+}
+
+/** @name FrameSupportDispatchDispatchInfo */
+export interface FrameSupportDispatchDispatchInfo extends Struct {
+  readonly weight: SpWeightsWeightV2Weight;
+  readonly class: FrameSupportDispatchDispatchClass;
+  readonly paysFee: FrameSupportDispatchPays;
+}
+
+/** @name FrameSupportDispatchPays */
+export interface FrameSupportDispatchPays extends Enum {
+  readonly isYes: boolean;
+  readonly isNo: boolean;
+  readonly type: 'Yes' | 'No';
+}
+
+/** @name FrameSupportDispatchPerDispatchClassU32 */
+export interface FrameSupportDispatchPerDispatchClassU32 extends Struct {
+  readonly normal: u32;
+  readonly operational: u32;
+  readonly mandatory: u32;
+}
+
+/** @name FrameSupportDispatchPerDispatchClassWeight */
+export interface FrameSupportDispatchPerDispatchClassWeight extends Struct {
+  readonly normal: SpWeightsWeightV2Weight;
+  readonly operational: SpWeightsWeightV2Weight;
+  readonly mandatory: SpWeightsWeightV2Weight;
+}
+
+/** @name FrameSupportDispatchPerDispatchClassWeightsPerClass */
+export interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct {
+  readonly normal: FrameSystemLimitsWeightsPerClass;
+  readonly operational: FrameSystemLimitsWeightsPerClass;
+  readonly mandatory: FrameSystemLimitsWeightsPerClass;
+}
+
+/** @name FrameSupportDispatchRawOrigin */
+export interface FrameSupportDispatchRawOrigin extends Enum {
+  readonly isRoot: boolean;
+  readonly isSigned: boolean;
+  readonly asSigned: AccountId32;
+  readonly isNone: boolean;
+  readonly type: 'Root' | 'Signed' | 'None';
+}
+
+/** @name FrameSupportPalletId */
+export interface FrameSupportPalletId extends U8aFixed {}
+
+/** @name FrameSupportPreimagesBounded */
+export interface FrameSupportPreimagesBounded extends Enum {
+  readonly isLegacy: boolean;
+  readonly asLegacy: {
+    readonly hash_: H256;
+  } & Struct;
+  readonly isInline: boolean;
+  readonly asInline: Bytes;
+  readonly isLookup: boolean;
+  readonly asLookup: {
+    readonly hash_: H256;
+    readonly len: u32;
+  } & Struct;
+  readonly type: 'Legacy' | 'Inline' | 'Lookup';
+}
+
+/** @name FrameSupportScheduleDispatchTime */
+export interface FrameSupportScheduleDispatchTime extends Enum {
+  readonly isAt: boolean;
+  readonly asAt: u32;
+  readonly isAfter: boolean;
+  readonly asAfter: u32;
+  readonly type: 'At' | 'After';
+}
+
+/** @name FrameSupportTokensMiscBalanceStatus */
+export interface FrameSupportTokensMiscBalanceStatus extends Enum {
+  readonly isFree: boolean;
+  readonly isReserved: boolean;
+  readonly type: 'Free' | 'Reserved';
+}
+
+/** @name FrameSystemAccountInfo */
+export interface FrameSystemAccountInfo extends Struct {
+  readonly nonce: u32;
+  readonly consumers: u32;
+  readonly providers: u32;
+  readonly sufficients: u32;
+  readonly data: PalletBalancesAccountData;
+}
+
+/** @name FrameSystemCall */
+export interface FrameSystemCall extends Enum {
+  readonly isRemark: boolean;
+  readonly asRemark: {
+    readonly remark: Bytes;
+  } & Struct;
+  readonly isSetHeapPages: boolean;
+  readonly asSetHeapPages: {
+    readonly pages: u64;
+  } & Struct;
+  readonly isSetCode: boolean;
+  readonly asSetCode: {
+    readonly code: Bytes;
+  } & Struct;
+  readonly isSetCodeWithoutChecks: boolean;
+  readonly asSetCodeWithoutChecks: {
+    readonly code: Bytes;
+  } & Struct;
+  readonly isSetStorage: boolean;
+  readonly asSetStorage: {
+    readonly items: Vec<ITuple<[Bytes, Bytes]>>;
+  } & Struct;
+  readonly isKillStorage: boolean;
+  readonly asKillStorage: {
+    readonly keys_: Vec<Bytes>;
+  } & Struct;
+  readonly isKillPrefix: boolean;
+  readonly asKillPrefix: {
+    readonly prefix: Bytes;
+    readonly subkeys: u32;
+  } & Struct;
+  readonly isRemarkWithEvent: boolean;
+  readonly asRemarkWithEvent: {
+    readonly remark: Bytes;
+  } & Struct;
+  readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent';
+}
+
+/** @name FrameSystemError */
+export interface FrameSystemError extends Enum {
+  readonly isInvalidSpecName: boolean;
+  readonly isSpecVersionNeedsToIncrease: boolean;
+  readonly isFailedToExtractRuntimeVersion: boolean;
+  readonly isNonDefaultComposite: boolean;
+  readonly isNonZeroRefCount: boolean;
+  readonly isCallFiltered: boolean;
+  readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered';
+}
+
+/** @name FrameSystemEvent */
+export interface FrameSystemEvent extends Enum {
+  readonly isExtrinsicSuccess: boolean;
+  readonly asExtrinsicSuccess: {
+    readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
+  } & Struct;
+  readonly isExtrinsicFailed: boolean;
+  readonly asExtrinsicFailed: {
+    readonly dispatchError: SpRuntimeDispatchError;
+    readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
+  } & Struct;
+  readonly isCodeUpdated: boolean;
+  readonly isNewAccount: boolean;
+  readonly asNewAccount: {
+    readonly account: AccountId32;
+  } & Struct;
+  readonly isKilledAccount: boolean;
+  readonly asKilledAccount: {
+    readonly account: AccountId32;
+  } & Struct;
+  readonly isRemarked: boolean;
+  readonly asRemarked: {
+    readonly sender: AccountId32;
+    readonly hash_: H256;
+  } & Struct;
+  readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked';
+}
+
+/** @name FrameSystemEventRecord */
+export interface FrameSystemEventRecord extends Struct {
+  readonly phase: FrameSystemPhase;
+  readonly event: Event;
+  readonly topics: Vec<H256>;
+}
+
+/** @name FrameSystemExtensionsCheckGenesis */
+export interface FrameSystemExtensionsCheckGenesis extends Null {}
+
+/** @name FrameSystemExtensionsCheckNonce */
+export interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
+
+/** @name FrameSystemExtensionsCheckSpecVersion */
+export interface FrameSystemExtensionsCheckSpecVersion extends Null {}
+
+/** @name FrameSystemExtensionsCheckTxVersion */
+export interface FrameSystemExtensionsCheckTxVersion extends Null {}
+
+/** @name FrameSystemExtensionsCheckWeight */
+export interface FrameSystemExtensionsCheckWeight extends Null {}
+
+/** @name FrameSystemLastRuntimeUpgradeInfo */
+export interface FrameSystemLastRuntimeUpgradeInfo extends Struct {
+  readonly specVersion: Compact<u32>;
+  readonly specName: Text;
+}
+
+/** @name FrameSystemLimitsBlockLength */
+export interface FrameSystemLimitsBlockLength extends Struct {
+  readonly max: FrameSupportDispatchPerDispatchClassU32;
+}
+
+/** @name FrameSystemLimitsBlockWeights */
+export interface FrameSystemLimitsBlockWeights extends Struct {
+  readonly baseBlock: SpWeightsWeightV2Weight;
+  readonly maxBlock: SpWeightsWeightV2Weight;
+  readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
+}
+
+/** @name FrameSystemLimitsWeightsPerClass */
+export interface FrameSystemLimitsWeightsPerClass extends Struct {
+  readonly baseExtrinsic: SpWeightsWeightV2Weight;
+  readonly maxExtrinsic: Option<SpWeightsWeightV2Weight>;
+  readonly maxTotal: Option<SpWeightsWeightV2Weight>;
+  readonly reserved: Option<SpWeightsWeightV2Weight>;
+}
+
+/** @name FrameSystemPhase */
+export interface FrameSystemPhase extends Enum {
+  readonly isApplyExtrinsic: boolean;
+  readonly asApplyExtrinsic: u32;
+  readonly isFinalization: boolean;
+  readonly isInitialization: boolean;
+  readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization';
+}
+
+/** @name OpalRuntimeOriginCaller */
+export interface OpalRuntimeOriginCaller extends Enum {
+  readonly isSystem: boolean;
+  readonly asSystem: FrameSupportDispatchRawOrigin;
+  readonly isVoid: boolean;
+  readonly asVoid: SpCoreVoid;
+  readonly isCouncil: boolean;
+  readonly asCouncil: PalletCollectiveRawOrigin;
+  readonly isTechnicalCommittee: boolean;
+  readonly asTechnicalCommittee: PalletCollectiveRawOrigin;
+  readonly isPolkadotXcm: boolean;
+  readonly asPolkadotXcm: PalletXcmOrigin;
+  readonly isCumulusXcm: boolean;
+  readonly asCumulusXcm: CumulusPalletXcmOrigin;
+  readonly isOrigins: boolean;
+  readonly asOrigins: PalletGovOriginsOrigin;
+  readonly isEthereum: boolean;
+  readonly asEthereum: PalletEthereumRawOrigin;
+  readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum';
+}
+
+/** @name OpalRuntimeRuntime */
+export interface OpalRuntimeRuntime extends Null {}
+
+/** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls */
+export interface OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls extends Null {}
+
+/** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance */
+export interface OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance extends Null {}
+
+/** @name OpalRuntimeRuntimeCommonSessionKeys */
+export interface OpalRuntimeRuntimeCommonSessionKeys extends Struct {
+  readonly aura: SpConsensusAuraSr25519AppSr25519Public;
+}
+
+/** @name OpalRuntimeRuntimeHoldReason */
+export interface OpalRuntimeRuntimeHoldReason extends Enum {
+  readonly isCollatorSelection: boolean;
+  readonly asCollatorSelection: PalletCollatorSelectionHoldReason;
+  readonly type: 'CollatorSelection';
+}
+
+/** @name OrmlTokensAccountData */
+export interface OrmlTokensAccountData extends Struct {
+  readonly free: u128;
+  readonly reserved: u128;
+  readonly frozen: u128;
+}
+
+/** @name OrmlTokensBalanceLock */
+export interface OrmlTokensBalanceLock extends Struct {
+  readonly id: U8aFixed;
+  readonly amount: u128;
+}
+
+/** @name OrmlTokensModuleCall */
+export interface OrmlTokensModuleCall extends Enum {
+  readonly isTransfer: boolean;
+  readonly asTransfer: {
+    readonly dest: MultiAddress;
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: Compact<u128>;
+  } & Struct;
+  readonly isTransferAll: boolean;
+  readonly asTransferAll: {
+    readonly dest: MultiAddress;
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly keepAlive: bool;
+  } & Struct;
+  readonly isTransferKeepAlive: boolean;
+  readonly asTransferKeepAlive: {
+    readonly dest: MultiAddress;
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: Compact<u128>;
+  } & Struct;
+  readonly isForceTransfer: boolean;
+  readonly asForceTransfer: {
+    readonly source: MultiAddress;
+    readonly dest: MultiAddress;
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: Compact<u128>;
+  } & Struct;
+  readonly isSetBalance: boolean;
+  readonly asSetBalance: {
+    readonly who: MultiAddress;
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly newFree: Compact<u128>;
+    readonly newReserved: Compact<u128>;
+  } & Struct;
+  readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance';
+}
+
+/** @name OrmlTokensModuleError */
+export interface OrmlTokensModuleError extends Enum {
+  readonly isBalanceTooLow: boolean;
+  readonly isAmountIntoBalanceFailed: boolean;
+  readonly isLiquidityRestrictions: boolean;
+  readonly isMaxLocksExceeded: boolean;
+  readonly isKeepAlive: boolean;
+  readonly isExistentialDeposit: boolean;
+  readonly isDeadAccount: boolean;
+  readonly isTooManyReserves: boolean;
+  readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves';
+}
+
+/** @name OrmlTokensModuleEvent */
+export interface OrmlTokensModuleEvent extends Enum {
+  readonly isEndowed: boolean;
+  readonly asEndowed: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isDustLost: boolean;
+  readonly asDustLost: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isTransfer: boolean;
+  readonly asTransfer: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly from: AccountId32;
+    readonly to: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isReserved: boolean;
+  readonly asReserved: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isUnreserved: boolean;
+  readonly asUnreserved: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isReserveRepatriated: boolean;
+  readonly asReserveRepatriated: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly from: AccountId32;
+    readonly to: AccountId32;
+    readonly amount: u128;
+    readonly status: FrameSupportTokensMiscBalanceStatus;
+  } & Struct;
+  readonly isBalanceSet: boolean;
+  readonly asBalanceSet: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly free: u128;
+    readonly reserved: u128;
+  } & Struct;
+  readonly isTotalIssuanceSet: boolean;
+  readonly asTotalIssuanceSet: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: u128;
+  } & Struct;
+  readonly isWithdrawn: boolean;
+  readonly asWithdrawn: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isSlashed: boolean;
+  readonly asSlashed: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly freeAmount: u128;
+    readonly reservedAmount: u128;
+  } & Struct;
+  readonly isDeposited: boolean;
+  readonly asDeposited: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isLockSet: boolean;
+  readonly asLockSet: {
+    readonly lockId: U8aFixed;
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isLockRemoved: boolean;
+  readonly asLockRemoved: {
+    readonly lockId: U8aFixed;
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+  } & Struct;
+  readonly isLocked: boolean;
+  readonly asLocked: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isUnlocked: boolean;
+  readonly asUnlocked: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isIssued: boolean;
+  readonly asIssued: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: u128;
+  } & Struct;
+  readonly isRescinded: boolean;
+  readonly asRescinded: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: u128;
+  } & Struct;
+  readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded';
+}
+
+/** @name OrmlTokensReserveData */
+export interface OrmlTokensReserveData extends Struct {
+  readonly id: Null;
+  readonly amount: u128;
+}
+
+/** @name OrmlVestingModuleCall */
+export interface OrmlVestingModuleCall extends Enum {
+  readonly isClaim: boolean;
+  readonly isVestedTransfer: boolean;
+  readonly asVestedTransfer: {
+    readonly dest: MultiAddress;
+    readonly schedule: OrmlVestingVestingSchedule;
+  } & Struct;
+  readonly isUpdateVestingSchedules: boolean;
+  readonly asUpdateVestingSchedules: {
+    readonly who: MultiAddress;
+    readonly vestingSchedules: Vec<OrmlVestingVestingSchedule>;
+  } & Struct;
+  readonly isClaimFor: boolean;
+  readonly asClaimFor: {
+    readonly dest: MultiAddress;
+  } & Struct;
+  readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor';
+}
+
+/** @name OrmlVestingModuleError */
+export interface OrmlVestingModuleError extends Enum {
+  readonly isZeroVestingPeriod: boolean;
+  readonly isZeroVestingPeriodCount: boolean;
+  readonly isInsufficientBalanceToLock: boolean;
+  readonly isTooManyVestingSchedules: boolean;
+  readonly isAmountLow: boolean;
+  readonly isMaxVestingSchedulesExceeded: boolean;
+  readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
+}
+
+/** @name OrmlVestingModuleEvent */
+export interface OrmlVestingModuleEvent extends Enum {
+  readonly isVestingScheduleAdded: boolean;
+  readonly asVestingScheduleAdded: {
+    readonly from: AccountId32;
+    readonly to: AccountId32;
+    readonly vestingSchedule: OrmlVestingVestingSchedule;
+  } & Struct;
+  readonly isClaimed: boolean;
+  readonly asClaimed: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isVestingSchedulesUpdated: boolean;
+  readonly asVestingSchedulesUpdated: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated';
+}
+
+/** @name OrmlVestingVestingSchedule */
+export interface OrmlVestingVestingSchedule extends Struct {
+  readonly start: u32;
+  readonly period: u32;
+  readonly periodCount: u32;
+  readonly perPeriod: Compact<u128>;
+}
+
+/** @name OrmlXtokensModuleCall */
+export interface OrmlXtokensModuleCall extends Enum {
+  readonly isTransfer: boolean;
+  readonly asTransfer: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: u128;
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly destWeightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isTransferMultiasset: boolean;
+  readonly asTransferMultiasset: {
+    readonly asset: StagingXcmVersionedMultiAsset;
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly destWeightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isTransferWithFee: boolean;
+  readonly asTransferWithFee: {
+    readonly currencyId: PalletForeignAssetsAssetId;
+    readonly amount: u128;
+    readonly fee: u128;
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly destWeightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isTransferMultiassetWithFee: boolean;
+  readonly asTransferMultiassetWithFee: {
+    readonly asset: StagingXcmVersionedMultiAsset;
+    readonly fee: StagingXcmVersionedMultiAsset;
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly destWeightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isTransferMulticurrencies: boolean;
+  readonly asTransferMulticurrencies: {
+    readonly currencies: Vec<ITuple<[PalletForeignAssetsAssetId, u128]>>;
+    readonly feeItem: u32;
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly destWeightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isTransferMultiassets: boolean;
+  readonly asTransferMultiassets: {
+    readonly assets: StagingXcmVersionedMultiAssets;
+    readonly feeItem: u32;
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly destWeightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets';
+}
+
+/** @name OrmlXtokensModuleError */
+export interface OrmlXtokensModuleError extends Enum {
+  readonly isAssetHasNoReserve: boolean;
+  readonly isNotCrossChainTransfer: boolean;
+  readonly isInvalidDest: boolean;
+  readonly isNotCrossChainTransferableCurrency: boolean;
+  readonly isUnweighableMessage: boolean;
+  readonly isXcmExecutionFailed: boolean;
+  readonly isCannotReanchor: boolean;
+  readonly isInvalidAncestry: boolean;
+  readonly isInvalidAsset: boolean;
+  readonly isDestinationNotInvertible: boolean;
+  readonly isBadVersion: boolean;
+  readonly isDistinctReserveForAssetAndFee: boolean;
+  readonly isZeroFee: boolean;
+  readonly isZeroAmount: boolean;
+  readonly isTooManyAssetsBeingSent: boolean;
+  readonly isAssetIndexNonExistent: boolean;
+  readonly isFeeNotEnough: boolean;
+  readonly isNotSupportedMultiLocation: boolean;
+  readonly isMinXcmFeeNotDefined: boolean;
+  readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined';
+}
+
+/** @name OrmlXtokensModuleEvent */
+export interface OrmlXtokensModuleEvent extends Enum {
+  readonly isTransferredMultiAssets: boolean;
+  readonly asTransferredMultiAssets: {
+    readonly sender: AccountId32;
+    readonly assets: StagingXcmV3MultiassetMultiAssets;
+    readonly fee: StagingXcmV3MultiAsset;
+    readonly dest: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly type: 'TransferredMultiAssets';
+}
+
+/** @name PalletAppPromotionCall */
+export interface PalletAppPromotionCall extends Enum {
+  readonly isSetAdminAddress: boolean;
+  readonly asSetAdminAddress: {
+    readonly admin: PalletEvmAccountBasicCrossAccountIdRepr;
+  } & Struct;
+  readonly isStake: boolean;
+  readonly asStake: {
+    readonly amount: u128;
+  } & Struct;
+  readonly isUnstakeAll: boolean;
+  readonly isSponsorCollection: boolean;
+  readonly asSponsorCollection: {
+    readonly collectionId: u32;
+  } & Struct;
+  readonly isStopSponsoringCollection: boolean;
+  readonly asStopSponsoringCollection: {
+    readonly collectionId: u32;
+  } & Struct;
+  readonly isSponsorContract: boolean;
+  readonly asSponsorContract: {
+    readonly contractId: H160;
+  } & Struct;
+  readonly isStopSponsoringContract: boolean;
+  readonly asStopSponsoringContract: {
+    readonly contractId: H160;
+  } & Struct;
+  readonly isPayoutStakers: boolean;
+  readonly asPayoutStakers: {
+    readonly stakersNumber: Option<u8>;
+  } & Struct;
+  readonly isUnstakePartial: boolean;
+  readonly asUnstakePartial: {
+    readonly amount: u128;
+  } & Struct;
+  readonly isForceUnstake: boolean;
+  readonly asForceUnstake: {
+    readonly pendingBlocks: Vec<u32>;
+  } & Struct;
+  readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake';
+}
+
+/** @name PalletAppPromotionError */
+export interface PalletAppPromotionError extends Enum {
+  readonly isAdminNotSet: boolean;
+  readonly isNoPermission: boolean;
+  readonly isNotSufficientFunds: boolean;
+  readonly isPendingForBlockOverflow: boolean;
+  readonly isSponsorNotSet: boolean;
+  readonly isInsufficientStakedBalance: boolean;
+  readonly isInconsistencyState: boolean;
+  readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState';
+}
+
+/** @name PalletAppPromotionEvent */
+export interface PalletAppPromotionEvent extends Enum {
+  readonly isStakingRecalculation: boolean;
+  readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
+  readonly isStake: boolean;
+  readonly asStake: ITuple<[AccountId32, u128]>;
+  readonly isUnstake: boolean;
+  readonly asUnstake: ITuple<[AccountId32, u128]>;
+  readonly isSetAdmin: boolean;
+  readonly asSetAdmin: AccountId32;
+  readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin';
+}
+
+/** @name PalletBalancesAccountData */
+export interface PalletBalancesAccountData extends Struct {
+  readonly free: u128;
+  readonly reserved: u128;
+  readonly frozen: u128;
+  readonly flags: u128;
+}
+
+/** @name PalletBalancesBalanceLock */
+export interface PalletBalancesBalanceLock extends Struct {
+  readonly id: U8aFixed;
+  readonly amount: u128;
+  readonly reasons: PalletBalancesReasons;
+}
+
+/** @name PalletBalancesCall */
+export interface PalletBalancesCall extends Enum {
+  readonly isTransferAllowDeath: boolean;
+  readonly asTransferAllowDeath: {
+    readonly dest: MultiAddress;
+    readonly value: Compact<u128>;
+  } & Struct;
+  readonly isSetBalanceDeprecated: boolean;
+  readonly asSetBalanceDeprecated: {
+    readonly who: MultiAddress;
+    readonly newFree: Compact<u128>;
+    readonly oldReserved: Compact<u128>;
+  } & Struct;
+  readonly isForceTransfer: boolean;
+  readonly asForceTransfer: {
+    readonly source: MultiAddress;
+    readonly dest: MultiAddress;
+    readonly value: Compact<u128>;
+  } & Struct;
+  readonly isTransferKeepAlive: boolean;
+  readonly asTransferKeepAlive: {
+    readonly dest: MultiAddress;
+    readonly value: Compact<u128>;
+  } & Struct;
+  readonly isTransferAll: boolean;
+  readonly asTransferAll: {
+    readonly dest: MultiAddress;
+    readonly keepAlive: bool;
+  } & Struct;
+  readonly isForceUnreserve: boolean;
+  readonly asForceUnreserve: {
+    readonly who: MultiAddress;
+    readonly amount: u128;
+  } & Struct;
+  readonly isUpgradeAccounts: boolean;
+  readonly asUpgradeAccounts: {
+    readonly who: Vec<AccountId32>;
+  } & Struct;
+  readonly isTransfer: boolean;
+  readonly asTransfer: {
+    readonly dest: MultiAddress;
+    readonly value: Compact<u128>;
+  } & Struct;
+  readonly isForceSetBalance: boolean;
+  readonly asForceSetBalance: {
+    readonly who: MultiAddress;
+    readonly newFree: Compact<u128>;
+  } & Struct;
+  readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance';
+}
+
+/** @name PalletBalancesError */
+export interface PalletBalancesError extends Enum {
+  readonly isVestingBalance: boolean;
+  readonly isLiquidityRestrictions: boolean;
+  readonly isInsufficientBalance: boolean;
+  readonly isExistentialDeposit: boolean;
+  readonly isExpendability: boolean;
+  readonly isExistingVestingSchedule: boolean;
+  readonly isDeadAccount: boolean;
+  readonly isTooManyReserves: boolean;
+  readonly isTooManyHolds: boolean;
+  readonly isTooManyFreezes: boolean;
+  readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes';
+}
+
+/** @name PalletBalancesEvent */
+export interface PalletBalancesEvent extends Enum {
+  readonly isEndowed: boolean;
+  readonly asEndowed: {
+    readonly account: AccountId32;
+    readonly freeBalance: u128;
+  } & Struct;
+  readonly isDustLost: boolean;
+  readonly asDustLost: {
+    readonly account: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isTransfer: boolean;
+  readonly asTransfer: {
+    readonly from: AccountId32;
+    readonly to: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isBalanceSet: boolean;
+  readonly asBalanceSet: {
+    readonly who: AccountId32;
+    readonly free: u128;
+  } & Struct;
+  readonly isReserved: boolean;
+  readonly asReserved: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isUnreserved: boolean;
+  readonly asUnreserved: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isReserveRepatriated: boolean;
+  readonly asReserveRepatriated: {
+    readonly from: AccountId32;
+    readonly to: AccountId32;
+    readonly amount: u128;
+    readonly destinationStatus: FrameSupportTokensMiscBalanceStatus;
+  } & Struct;
+  readonly isDeposit: boolean;
+  readonly asDeposit: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isWithdraw: boolean;
+  readonly asWithdraw: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isSlashed: boolean;
+  readonly asSlashed: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isMinted: boolean;
+  readonly asMinted: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isBurned: boolean;
+  readonly asBurned: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isSuspended: boolean;
+  readonly asSuspended: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isRestored: boolean;
+  readonly asRestored: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isUpgraded: boolean;
+  readonly asUpgraded: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly isIssued: boolean;
+  readonly asIssued: {
+    readonly amount: u128;
+  } & Struct;
+  readonly isRescinded: boolean;
+  readonly asRescinded: {
+    readonly amount: u128;
+  } & Struct;
+  readonly isLocked: boolean;
+  readonly asLocked: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isUnlocked: boolean;
+  readonly asUnlocked: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isFrozen: boolean;
+  readonly asFrozen: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isThawed: boolean;
+  readonly asThawed: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed';
+}
+
+/** @name PalletBalancesIdAmount */
+export interface PalletBalancesIdAmount extends Struct {
+  readonly id: U8aFixed;
+  readonly amount: u128;
+}
+
+/** @name PalletBalancesReasons */
+export interface PalletBalancesReasons extends Enum {
+  readonly isFee: boolean;
+  readonly isMisc: boolean;
+  readonly isAll: boolean;
+  readonly type: 'Fee' | 'Misc' | 'All';
+}
+
+/** @name PalletBalancesReserveData */
+export interface PalletBalancesReserveData extends Struct {
+  readonly id: U8aFixed;
+  readonly amount: u128;
+}
+
+/** @name PalletCollatorSelectionCall */
+export interface PalletCollatorSelectionCall extends Enum {
+  readonly isAddInvulnerable: boolean;
+  readonly asAddInvulnerable: {
+    readonly new_: AccountId32;
+  } & Struct;
+  readonly isRemoveInvulnerable: boolean;
+  readonly asRemoveInvulnerable: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly isGetLicense: boolean;
+  readonly isOnboard: boolean;
+  readonly isOffboard: boolean;
+  readonly isReleaseLicense: boolean;
+  readonly isForceReleaseLicense: boolean;
+  readonly asForceReleaseLicense: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense';
+}
+
+/** @name PalletCollatorSelectionError */
+export interface PalletCollatorSelectionError extends Enum {
+  readonly isTooManyCandidates: boolean;
+  readonly isUnknown: boolean;
+  readonly isPermission: boolean;
+  readonly isAlreadyHoldingLicense: boolean;
+  readonly isNoLicense: boolean;
+  readonly isAlreadyCandidate: boolean;
+  readonly isNotCandidate: boolean;
+  readonly isTooManyInvulnerables: boolean;
+  readonly isTooFewInvulnerables: boolean;
+  readonly isAlreadyInvulnerable: boolean;
+  readonly isNotInvulnerable: boolean;
+  readonly isNoAssociatedValidatorId: boolean;
+  readonly isValidatorNotRegistered: boolean;
+  readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered';
+}
+
+/** @name PalletCollatorSelectionEvent */
+export interface PalletCollatorSelectionEvent extends Enum {
+  readonly isInvulnerableAdded: boolean;
+  readonly asInvulnerableAdded: {
+    readonly invulnerable: AccountId32;
+  } & Struct;
+  readonly isInvulnerableRemoved: boolean;
+  readonly asInvulnerableRemoved: {
+    readonly invulnerable: AccountId32;
+  } & Struct;
+  readonly isLicenseObtained: boolean;
+  readonly asLicenseObtained: {
+    readonly accountId: AccountId32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isLicenseReleased: boolean;
+  readonly asLicenseReleased: {
+    readonly accountId: AccountId32;
+    readonly depositReturned: u128;
+  } & Struct;
+  readonly isCandidateAdded: boolean;
+  readonly asCandidateAdded: {
+    readonly accountId: AccountId32;
+  } & Struct;
+  readonly isCandidateRemoved: boolean;
+  readonly asCandidateRemoved: {
+    readonly accountId: AccountId32;
+  } & Struct;
+  readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved';
+}
+
+/** @name PalletCollatorSelectionHoldReason */
+export interface PalletCollatorSelectionHoldReason extends Enum {
+  readonly isLicenseBond: boolean;
+  readonly type: 'LicenseBond';
+}
+
+/** @name PalletCollectiveCall */
+export interface PalletCollectiveCall extends Enum {
+  readonly isSetMembers: boolean;
+  readonly asSetMembers: {
+    readonly newMembers: Vec<AccountId32>;
+    readonly prime: Option<AccountId32>;
+    readonly oldCount: u32;
+  } & Struct;
+  readonly isExecute: boolean;
+  readonly asExecute: {
+    readonly proposal: Call;
+    readonly lengthBound: Compact<u32>;
+  } & Struct;
+  readonly isPropose: boolean;
+  readonly asPropose: {
+    readonly threshold: Compact<u32>;
+    readonly proposal: Call;
+    readonly lengthBound: Compact<u32>;
+  } & Struct;
+  readonly isVote: boolean;
+  readonly asVote: {
+    readonly proposal: H256;
+    readonly index: Compact<u32>;
+    readonly approve: bool;
+  } & Struct;
+  readonly isDisapproveProposal: boolean;
+  readonly asDisapproveProposal: {
+    readonly proposalHash: H256;
+  } & Struct;
+  readonly isClose: boolean;
+  readonly asClose: {
+    readonly proposalHash: H256;
+    readonly index: Compact<u32>;
+    readonly proposalWeightBound: SpWeightsWeightV2Weight;
+    readonly lengthBound: Compact<u32>;
+  } & Struct;
+  readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close';
+}
+
+/** @name PalletCollectiveError */
+export interface PalletCollectiveError extends Enum {
+  readonly isNotMember: boolean;
+  readonly isDuplicateProposal: boolean;
+  readonly isProposalMissing: boolean;
+  readonly isWrongIndex: boolean;
+  readonly isDuplicateVote: boolean;
+  readonly isAlreadyInitialized: boolean;
+  readonly isTooEarly: boolean;
+  readonly isTooManyProposals: boolean;
+  readonly isWrongProposalWeight: boolean;
+  readonly isWrongProposalLength: boolean;
+  readonly isPrimeAccountNotMember: boolean;
+  readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember';
+}
+
+/** @name PalletCollectiveEvent */
+export interface PalletCollectiveEvent extends Enum {
+  readonly isProposed: boolean;
+  readonly asProposed: {
+    readonly account: AccountId32;
+    readonly proposalIndex: u32;
+    readonly proposalHash: H256;
+    readonly threshold: u32;
+  } & Struct;
+  readonly isVoted: boolean;
+  readonly asVoted: {
+    readonly account: AccountId32;
+    readonly proposalHash: H256;
+    readonly voted: bool;
+    readonly yes: u32;
+    readonly no: u32;
+  } & Struct;
+  readonly isApproved: boolean;
+  readonly asApproved: {
+    readonly proposalHash: H256;
+  } & Struct;
+  readonly isDisapproved: boolean;
+  readonly asDisapproved: {
+    readonly proposalHash: H256;
+  } & Struct;
+  readonly isExecuted: boolean;
+  readonly asExecuted: {
+    readonly proposalHash: H256;
+    readonly result: Result<Null, SpRuntimeDispatchError>;
+  } & Struct;
+  readonly isMemberExecuted: boolean;
+  readonly asMemberExecuted: {
+    readonly proposalHash: H256;
+    readonly result: Result<Null, SpRuntimeDispatchError>;
+  } & Struct;
+  readonly isClosed: boolean;
+  readonly asClosed: {
+    readonly proposalHash: H256;
+    readonly yes: u32;
+    readonly no: u32;
+  } & Struct;
+  readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed';
+}
+
+/** @name PalletCollectiveRawOrigin */
+export interface PalletCollectiveRawOrigin extends Enum {
+  readonly isMembers: boolean;
+  readonly asMembers: ITuple<[u32, u32]>;
+  readonly isMember: boolean;
+  readonly asMember: AccountId32;
+  readonly isPhantom: boolean;
+  readonly type: 'Members' | 'Member' | 'Phantom';
+}
+
+/** @name PalletCollectiveVotes */
+export interface PalletCollectiveVotes extends Struct {
+  readonly index: u32;
+  readonly threshold: u32;
+  readonly ayes: Vec<AccountId32>;
+  readonly nays: Vec<AccountId32>;
+  readonly end: u32;
+}
+
+/** @name PalletCommonError */
+export interface PalletCommonError extends Enum {
+  readonly isCollectionNotFound: boolean;
+  readonly isMustBeTokenOwner: boolean;
+  readonly isNoPermission: boolean;
+  readonly isCantDestroyNotEmptyCollection: boolean;
+  readonly isPublicMintingNotAllowed: boolean;
+  readonly isAddressNotInAllowlist: boolean;
+  readonly isCollectionNameLimitExceeded: boolean;
+  readonly isCollectionDescriptionLimitExceeded: boolean;
+  readonly isCollectionTokenPrefixLimitExceeded: boolean;
+  readonly isTotalCollectionsLimitExceeded: boolean;
+  readonly isCollectionAdminCountExceeded: boolean;
+  readonly isCollectionLimitBoundsExceeded: boolean;
+  readonly isOwnerPermissionsCantBeReverted: boolean;
+  readonly isTransferNotAllowed: boolean;
+  readonly isAccountTokenLimitExceeded: boolean;
+  readonly isCollectionTokenLimitExceeded: boolean;
+  readonly isMetadataFlagFrozen: boolean;
+  readonly isTokenNotFound: boolean;
+  readonly isTokenValueTooLow: boolean;
+  readonly isApprovedValueTooLow: boolean;
+  readonly isCantApproveMoreThanOwned: boolean;
+  readonly isAddressIsNotEthMirror: boolean;
+  readonly isAddressIsZero: boolean;
+  readonly isUnsupportedOperation: boolean;
+  readonly isNotSufficientFounds: boolean;
+  readonly isUserIsNotAllowedToNest: boolean;
+  readonly isSourceCollectionIsNotAllowedToNest: boolean;
+  readonly isCollectionFieldSizeExceeded: boolean;
+  readonly isNoSpaceForProperty: boolean;
+  readonly isPropertyLimitReached: boolean;
+  readonly isPropertyKeyIsTooLong: boolean;
+  readonly isInvalidCharacterInPropertyKey: boolean;
+  readonly isEmptyPropertyKey: boolean;
+  readonly isCollectionIsExternal: boolean;
+  readonly isCollectionIsInternal: boolean;
+  readonly isConfirmSponsorshipFail: boolean;
+  readonly isUserIsNotCollectionAdmin: boolean;
+  readonly isFungibleItemsHaveNoId: boolean;
+  readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId';
+}
+
+/** @name PalletCommonEvent */
+export interface PalletCommonEvent extends Enum {
+  readonly isCollectionCreated: boolean;
+  readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>;
+  readonly isCollectionDestroyed: boolean;
+  readonly asCollectionDestroyed: u32;
+  readonly isItemCreated: boolean;
+  readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+  readonly isItemDestroyed: boolean;
+  readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+  readonly isTransfer: boolean;
+  readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+  readonly isApproved: boolean;
+  readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+  readonly isApprovedForAll: boolean;
+  readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
+  readonly isCollectionPropertySet: boolean;
+  readonly asCollectionPropertySet: ITuple<[u32, Bytes]>;
+  readonly isCollectionPropertyDeleted: boolean;
+  readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>;
+  readonly isTokenPropertySet: boolean;
+  readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>;
+  readonly isTokenPropertyDeleted: boolean;
+  readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>;
+  readonly isPropertyPermissionSet: boolean;
+  readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>;
+  readonly isAllowListAddressAdded: boolean;
+  readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+  readonly isAllowListAddressRemoved: boolean;
+  readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+  readonly isCollectionAdminAdded: boolean;
+  readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+  readonly isCollectionAdminRemoved: boolean;
+  readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+  readonly isCollectionLimitSet: boolean;
+  readonly asCollectionLimitSet: u32;
+  readonly isCollectionOwnerChanged: boolean;
+  readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>;
+  readonly isCollectionPermissionSet: boolean;
+  readonly asCollectionPermissionSet: u32;
+  readonly isCollectionSponsorSet: boolean;
+  readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>;
+  readonly isSponsorshipConfirmed: boolean;
+  readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>;
+  readonly isCollectionSponsorRemoved: boolean;
+  readonly asCollectionSponsorRemoved: u32;
+  readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved';
+}
+
+/** @name PalletConfigurationAppPromotionConfiguration */
+export interface PalletConfigurationAppPromotionConfiguration extends Struct {
+  readonly recalculationInterval: Option<u32>;
+  readonly pendingInterval: Option<u32>;
+  readonly intervalIncome: Option<Perbill>;
+  readonly maxStakersPerCalculation: Option<u8>;
+}
+
+/** @name PalletConfigurationCall */
+export interface PalletConfigurationCall extends Enum {
+  readonly isSetWeightToFeeCoefficientOverride: boolean;
+  readonly asSetWeightToFeeCoefficientOverride: {
+    readonly coeff: Option<u64>;
+  } & Struct;
+  readonly isSetMinGasPriceOverride: boolean;
+  readonly asSetMinGasPriceOverride: {
+    readonly coeff: Option<u64>;
+  } & Struct;
+  readonly isSetAppPromotionConfigurationOverride: boolean;
+  readonly asSetAppPromotionConfigurationOverride: {
+    readonly configuration: PalletConfigurationAppPromotionConfiguration;
+  } & Struct;
+  readonly isSetCollatorSelectionDesiredCollators: boolean;
+  readonly asSetCollatorSelectionDesiredCollators: {
+    readonly max: Option<u32>;
+  } & Struct;
+  readonly isSetCollatorSelectionLicenseBond: boolean;
+  readonly asSetCollatorSelectionLicenseBond: {
+    readonly amount: Option<u128>;
+  } & Struct;
+  readonly isSetCollatorSelectionKickThreshold: boolean;
+  readonly asSetCollatorSelectionKickThreshold: {
+    readonly threshold: Option<u32>;
+  } & Struct;
+  readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold';
+}
+
+/** @name PalletConfigurationError */
+export interface PalletConfigurationError extends Enum {
+  readonly isInconsistentConfiguration: boolean;
+  readonly type: 'InconsistentConfiguration';
+}
+
+/** @name PalletConfigurationEvent */
+export interface PalletConfigurationEvent extends Enum {
+  readonly isNewDesiredCollators: boolean;
+  readonly asNewDesiredCollators: {
+    readonly desiredCollators: Option<u32>;
+  } & Struct;
+  readonly isNewCollatorLicenseBond: boolean;
+  readonly asNewCollatorLicenseBond: {
+    readonly bondCost: Option<u128>;
+  } & Struct;
+  readonly isNewCollatorKickThreshold: boolean;
+  readonly asNewCollatorKickThreshold: {
+    readonly lengthInBlocks: Option<u32>;
+  } & Struct;
+  readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold';
+}
+
+/** @name PalletDemocracyCall */
+export interface PalletDemocracyCall extends Enum {
+  readonly isPropose: boolean;
+  readonly asPropose: {
+    readonly proposal: FrameSupportPreimagesBounded;
+    readonly value: Compact<u128>;
+  } & Struct;
+  readonly isSecond: boolean;
+  readonly asSecond: {
+    readonly proposal: Compact<u32>;
+  } & Struct;
+  readonly isVote: boolean;
+  readonly asVote: {
+    readonly refIndex: Compact<u32>;
+    readonly vote: PalletDemocracyVoteAccountVote;
+  } & Struct;
+  readonly isEmergencyCancel: boolean;
+  readonly asEmergencyCancel: {
+    readonly refIndex: u32;
+  } & Struct;
+  readonly isExternalPropose: boolean;
+  readonly asExternalPropose: {
+    readonly proposal: FrameSupportPreimagesBounded;
+  } & Struct;
+  readonly isExternalProposeMajority: boolean;
+  readonly asExternalProposeMajority: {
+    readonly proposal: FrameSupportPreimagesBounded;
+  } & Struct;
+  readonly isExternalProposeDefault: boolean;
+  readonly asExternalProposeDefault: {
+    readonly proposal: FrameSupportPreimagesBounded;
+  } & Struct;
+  readonly isFastTrack: boolean;
+  readonly asFastTrack: {
+    readonly proposalHash: H256;
+    readonly votingPeriod: u32;
+    readonly delay: u32;
+  } & Struct;
+  readonly isVetoExternal: boolean;
+  readonly asVetoExternal: {
+    readonly proposalHash: H256;
+  } & Struct;
+  readonly isCancelReferendum: boolean;
+  readonly asCancelReferendum: {
+    readonly refIndex: Compact<u32>;
+  } & Struct;
+  readonly isDelegate: boolean;
+  readonly asDelegate: {
+    readonly to: MultiAddress;
+    readonly conviction: PalletDemocracyConviction;
+    readonly balance: u128;
+  } & Struct;
+  readonly isUndelegate: boolean;
+  readonly isClearPublicProposals: boolean;
+  readonly isUnlock: boolean;
+  readonly asUnlock: {
+    readonly target: MultiAddress;
+  } & Struct;
+  readonly isRemoveVote: boolean;
+  readonly asRemoveVote: {
+    readonly index: u32;
+  } & Struct;
+  readonly isRemoveOtherVote: boolean;
+  readonly asRemoveOtherVote: {
+    readonly target: MultiAddress;
+    readonly index: u32;
+  } & Struct;
+  readonly isBlacklist: boolean;
+  readonly asBlacklist: {
+    readonly proposalHash: H256;
+    readonly maybeRefIndex: Option<u32>;
+  } & Struct;
+  readonly isCancelProposal: boolean;
+  readonly asCancelProposal: {
+    readonly propIndex: Compact<u32>;
+  } & Struct;
+  readonly isSetMetadata: boolean;
+  readonly asSetMetadata: {
+    readonly owner: PalletDemocracyMetadataOwner;
+    readonly maybeHash: Option<H256>;
+  } & Struct;
+  readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata';
+}
+
+/** @name PalletDemocracyConviction */
+export interface PalletDemocracyConviction extends Enum {
+  readonly isNone: boolean;
+  readonly isLocked1x: boolean;
+  readonly isLocked2x: boolean;
+  readonly isLocked3x: boolean;
+  readonly isLocked4x: boolean;
+  readonly isLocked5x: boolean;
+  readonly isLocked6x: boolean;
+  readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x';
+}
+
+/** @name PalletDemocracyDelegations */
+export interface PalletDemocracyDelegations extends Struct {
+  readonly votes: u128;
+  readonly capital: u128;
+}
+
+/** @name PalletDemocracyError */
+export interface PalletDemocracyError extends Enum {
+  readonly isValueLow: boolean;
+  readonly isProposalMissing: boolean;
+  readonly isAlreadyCanceled: boolean;
+  readonly isDuplicateProposal: boolean;
+  readonly isProposalBlacklisted: boolean;
+  readonly isNotSimpleMajority: boolean;
+  readonly isInvalidHash: boolean;
+  readonly isNoProposal: boolean;
+  readonly isAlreadyVetoed: boolean;
+  readonly isReferendumInvalid: boolean;
+  readonly isNoneWaiting: boolean;
+  readonly isNotVoter: boolean;
+  readonly isNoPermission: boolean;
+  readonly isAlreadyDelegating: boolean;
+  readonly isInsufficientFunds: boolean;
+  readonly isNotDelegating: boolean;
+  readonly isVotesExist: boolean;
+  readonly isInstantNotAllowed: boolean;
+  readonly isNonsense: boolean;
+  readonly isWrongUpperBound: boolean;
+  readonly isMaxVotesReached: boolean;
+  readonly isTooMany: boolean;
+  readonly isVotingPeriodLow: boolean;
+  readonly isPreimageNotExist: boolean;
+  readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist';
+}
+
+/** @name PalletDemocracyEvent */
+export interface PalletDemocracyEvent extends Enum {
+  readonly isProposed: boolean;
+  readonly asProposed: {
+    readonly proposalIndex: u32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isTabled: boolean;
+  readonly asTabled: {
+    readonly proposalIndex: u32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isExternalTabled: boolean;
+  readonly isStarted: boolean;
+  readonly asStarted: {
+    readonly refIndex: u32;
+    readonly threshold: PalletDemocracyVoteThreshold;
+  } & Struct;
+  readonly isPassed: boolean;
+  readonly asPassed: {
+    readonly refIndex: u32;
+  } & Struct;
+  readonly isNotPassed: boolean;
+  readonly asNotPassed: {
+    readonly refIndex: u32;
+  } & Struct;
+  readonly isCancelled: boolean;
+  readonly asCancelled: {
+    readonly refIndex: u32;
+  } & Struct;
+  readonly isDelegated: boolean;
+  readonly asDelegated: {
+    readonly who: AccountId32;
+    readonly target: AccountId32;
+  } & Struct;
+  readonly isUndelegated: boolean;
+  readonly asUndelegated: {
+    readonly account: AccountId32;
+  } & Struct;
+  readonly isVetoed: boolean;
+  readonly asVetoed: {
+    readonly who: AccountId32;
+    readonly proposalHash: H256;
+    readonly until: u32;
+  } & Struct;
+  readonly isBlacklisted: boolean;
+  readonly asBlacklisted: {
+    readonly proposalHash: H256;
+  } & Struct;
+  readonly isVoted: boolean;
+  readonly asVoted: {
+    readonly voter: AccountId32;
+    readonly refIndex: u32;
+    readonly vote: PalletDemocracyVoteAccountVote;
+  } & Struct;
+  readonly isSeconded: boolean;
+  readonly asSeconded: {
+    readonly seconder: AccountId32;
+    readonly propIndex: u32;
+  } & Struct;
+  readonly isProposalCanceled: boolean;
+  readonly asProposalCanceled: {
+    readonly propIndex: u32;
+  } & Struct;
+  readonly isMetadataSet: boolean;
+  readonly asMetadataSet: {
+    readonly owner: PalletDemocracyMetadataOwner;
+    readonly hash_: H256;
+  } & Struct;
+  readonly isMetadataCleared: boolean;
+  readonly asMetadataCleared: {
+    readonly owner: PalletDemocracyMetadataOwner;
+    readonly hash_: H256;
+  } & Struct;
+  readonly isMetadataTransferred: boolean;
+  readonly asMetadataTransferred: {
+    readonly prevOwner: PalletDemocracyMetadataOwner;
+    readonly owner: PalletDemocracyMetadataOwner;
+    readonly hash_: H256;
+  } & Struct;
+  readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred';
+}
+
+/** @name PalletDemocracyMetadataOwner */
+export interface PalletDemocracyMetadataOwner extends Enum {
+  readonly isExternal: boolean;
+  readonly isProposal: boolean;
+  readonly asProposal: u32;
+  readonly isReferendum: boolean;
+  readonly asReferendum: u32;
+  readonly type: 'External' | 'Proposal' | 'Referendum';
+}
+
+/** @name PalletDemocracyReferendumInfo */
+export interface PalletDemocracyReferendumInfo extends Enum {
+  readonly isOngoing: boolean;
+  readonly asOngoing: PalletDemocracyReferendumStatus;
+  readonly isFinished: boolean;
+  readonly asFinished: {
+    readonly approved: bool;
+    readonly end: u32;
+  } & Struct;
+  readonly type: 'Ongoing' | 'Finished';
+}
+
+/** @name PalletDemocracyReferendumStatus */
+export interface PalletDemocracyReferendumStatus extends Struct {
+  readonly end: u32;
+  readonly proposal: FrameSupportPreimagesBounded;
+  readonly threshold: PalletDemocracyVoteThreshold;
+  readonly delay: u32;
+  readonly tally: PalletDemocracyTally;
+}
+
+/** @name PalletDemocracyTally */
+export interface PalletDemocracyTally extends Struct {
+  readonly ayes: u128;
+  readonly nays: u128;
+  readonly turnout: u128;
+}
+
+/** @name PalletDemocracyVoteAccountVote */
+export interface PalletDemocracyVoteAccountVote extends Enum {
+  readonly isStandard: boolean;
+  readonly asStandard: {
+    readonly vote: Vote;
+    readonly balance: u128;
+  } & Struct;
+  readonly isSplit: boolean;
+  readonly asSplit: {
+    readonly aye: u128;
+    readonly nay: u128;
+  } & Struct;
+  readonly type: 'Standard' | 'Split';
+}
+
+/** @name PalletDemocracyVotePriorLock */
+export interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {}
+
+/** @name PalletDemocracyVoteThreshold */
+export interface PalletDemocracyVoteThreshold extends Enum {
+  readonly isSuperMajorityApprove: boolean;
+  readonly isSuperMajorityAgainst: boolean;
+  readonly isSimpleMajority: boolean;
+  readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority';
+}
+
+/** @name PalletDemocracyVoteVoting */
+export interface PalletDemocracyVoteVoting extends Enum {
+  readonly isDirect: boolean;
+  readonly asDirect: {
+    readonly votes: Vec<ITuple<[u32, PalletDemocracyVoteAccountVote]>>;
+    readonly delegations: PalletDemocracyDelegations;
+    readonly prior: PalletDemocracyVotePriorLock;
+  } & Struct;
+  readonly isDelegating: boolean;
+  readonly asDelegating: {
+    readonly balance: u128;
+    readonly target: AccountId32;
+    readonly conviction: PalletDemocracyConviction;
+    readonly delegations: PalletDemocracyDelegations;
+    readonly prior: PalletDemocracyVotePriorLock;
+  } & Struct;
+  readonly type: 'Direct' | 'Delegating';
+}
+
+/** @name PalletEthereumCall */
+export interface PalletEthereumCall extends Enum {
+  readonly isTransact: boolean;
+  readonly asTransact: {
+    readonly transaction: EthereumTransactionTransactionV2;
+  } & Struct;
+  readonly type: 'Transact';
+}
+
+/** @name PalletEthereumError */
+export interface PalletEthereumError extends Enum {
+  readonly isInvalidSignature: boolean;
+  readonly isPreLogExists: boolean;
+  readonly type: 'InvalidSignature' | 'PreLogExists';
+}
+
+/** @name PalletEthereumEvent */
+export interface PalletEthereumEvent extends Enum {
+  readonly isExecuted: boolean;
+  readonly asExecuted: {
+    readonly from: H160;
+    readonly to: H160;
+    readonly transactionHash: H256;
+    readonly exitReason: EvmCoreErrorExitReason;
+    readonly extraData: Bytes;
+  } & Struct;
+  readonly type: 'Executed';
+}
+
+/** @name PalletEthereumFakeTransactionFinalizer */
+export interface PalletEthereumFakeTransactionFinalizer extends Null {}
+
+/** @name PalletEthereumRawOrigin */
+export interface PalletEthereumRawOrigin extends Enum {
+  readonly isEthereumTransaction: boolean;
+  readonly asEthereumTransaction: H160;
+  readonly type: 'EthereumTransaction';
+}
+
+/** @name PalletEvmAccountBasicCrossAccountIdRepr */
+export interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum {
+  readonly isSubstrate: boolean;
+  readonly asSubstrate: AccountId32;
+  readonly isEthereum: boolean;
+  readonly asEthereum: H160;
+  readonly type: 'Substrate' | 'Ethereum';
+}
+
+/** @name PalletEvmCall */
+export interface PalletEvmCall extends Enum {
+  readonly isWithdraw: boolean;
+  readonly asWithdraw: {
+    readonly address: H160;
+    readonly value: u128;
+  } & Struct;
+  readonly isCall: boolean;
+  readonly asCall: {
+    readonly source: H160;
+    readonly target: H160;
+    readonly input: Bytes;
+    readonly value: U256;
+    readonly gasLimit: u64;
+    readonly maxFeePerGas: U256;
+    readonly maxPriorityFeePerGas: Option<U256>;
+    readonly nonce: Option<U256>;
+    readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
+  } & Struct;
+  readonly isCreate: boolean;
+  readonly asCreate: {
+    readonly source: H160;
+    readonly init: Bytes;
+    readonly value: U256;
+    readonly gasLimit: u64;
+    readonly maxFeePerGas: U256;
+    readonly maxPriorityFeePerGas: Option<U256>;
+    readonly nonce: Option<U256>;
+    readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
+  } & Struct;
+  readonly isCreate2: boolean;
+  readonly asCreate2: {
+    readonly source: H160;
+    readonly init: Bytes;
+    readonly salt: H256;
+    readonly value: U256;
+    readonly gasLimit: u64;
+    readonly maxFeePerGas: U256;
+    readonly maxPriorityFeePerGas: Option<U256>;
+    readonly nonce: Option<U256>;
+    readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
+  } & Struct;
+  readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
+}
+
+/** @name PalletEvmCodeMetadata */
+export interface PalletEvmCodeMetadata extends Struct {
+  readonly size_: u64;
+  readonly hash_: H256;
+}
+
+/** @name PalletEvmCoderSubstrateError */
+export interface PalletEvmCoderSubstrateError extends Enum {
+  readonly isOutOfGas: boolean;
+  readonly isOutOfFund: boolean;
+  readonly type: 'OutOfGas' | 'OutOfFund';
+}
+
+/** @name PalletEvmContractHelpersCall */
+export interface PalletEvmContractHelpersCall extends Enum {
+  readonly isMigrateFromSelfSponsoring: boolean;
+  readonly asMigrateFromSelfSponsoring: {
+    readonly addresses: Vec<H160>;
+  } & Struct;
+  readonly type: 'MigrateFromSelfSponsoring';
+}
+
+/** @name PalletEvmContractHelpersError */
+export interface PalletEvmContractHelpersError extends Enum {
+  readonly isNoPermission: boolean;
+  readonly isNoPendingSponsor: boolean;
+  readonly isTooManyMethodsHaveSponsoredLimit: boolean;
+  readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit';
+}
+
+/** @name PalletEvmContractHelpersEvent */
+export interface PalletEvmContractHelpersEvent extends Enum {
+  readonly isContractSponsorSet: boolean;
+  readonly asContractSponsorSet: ITuple<[H160, AccountId32]>;
+  readonly isContractSponsorshipConfirmed: boolean;
+  readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>;
+  readonly isContractSponsorRemoved: boolean;
+  readonly asContractSponsorRemoved: H160;
+  readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved';
+}
+
+/** @name PalletEvmContractHelpersSponsoringModeT */
+export interface PalletEvmContractHelpersSponsoringModeT extends Enum {
+  readonly isDisabled: boolean;
+  readonly isAllowlisted: boolean;
+  readonly isGenerous: boolean;
+  readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
+}
+
+/** @name PalletEvmError */
+export interface PalletEvmError extends Enum {
+  readonly isBalanceLow: boolean;
+  readonly isFeeOverflow: boolean;
+  readonly isPaymentOverflow: boolean;
+  readonly isWithdrawFailed: boolean;
+  readonly isGasPriceTooLow: boolean;
+  readonly isInvalidNonce: boolean;
+  readonly isGasLimitTooLow: boolean;
+  readonly isGasLimitTooHigh: boolean;
+  readonly isUndefined: boolean;
+  readonly isReentrancy: boolean;
+  readonly isTransactionMustComeFromEOA: boolean;
+  readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA';
+}
+
+/** @name PalletEvmEvent */
+export interface PalletEvmEvent extends Enum {
+  readonly isLog: boolean;
+  readonly asLog: {
+    readonly log: EthereumLog;
+  } & Struct;
+  readonly isCreated: boolean;
+  readonly asCreated: {
+    readonly address: H160;
+  } & Struct;
+  readonly isCreatedFailed: boolean;
+  readonly asCreatedFailed: {
+    readonly address: H160;
+  } & Struct;
+  readonly isExecuted: boolean;
+  readonly asExecuted: {
+    readonly address: H160;
+  } & Struct;
+  readonly isExecutedFailed: boolean;
+  readonly asExecutedFailed: {
+    readonly address: H160;
+  } & Struct;
+  readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed';
+}
+
+/** @name PalletEvmMigrationCall */
+export interface PalletEvmMigrationCall extends Enum {
+  readonly isBegin: boolean;
+  readonly asBegin: {
+    readonly address: H160;
+  } & Struct;
+  readonly isSetData: boolean;
+  readonly asSetData: {
+    readonly address: H160;
+    readonly data: Vec<ITuple<[H256, H256]>>;
+  } & Struct;
+  readonly isFinish: boolean;
+  readonly asFinish: {
+    readonly address: H160;
+    readonly code: Bytes;
+  } & Struct;
+  readonly isInsertEthLogs: boolean;
+  readonly asInsertEthLogs: {
+    readonly logs: Vec<EthereumLog>;
+  } & Struct;
+  readonly isInsertEvents: boolean;
+  readonly asInsertEvents: {
+    readonly events: Vec<Bytes>;
+  } & Struct;
+  readonly isRemoveRmrkData: boolean;
+  readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData';
+}
+
+/** @name PalletEvmMigrationError */
+export interface PalletEvmMigrationError extends Enum {
+  readonly isAccountNotEmpty: boolean;
+  readonly isAccountIsNotMigrating: boolean;
+  readonly isBadEvent: boolean;
+  readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent';
+}
+
+/** @name PalletEvmMigrationEvent */
+export interface PalletEvmMigrationEvent extends Enum {
+  readonly isTestEvent: boolean;
+  readonly type: 'TestEvent';
+}
+
+/** @name PalletForeignAssetsAssetId */
+export interface PalletForeignAssetsAssetId extends Enum {
+  readonly isForeignAssetId: boolean;
+  readonly asForeignAssetId: u32;
+  readonly isNativeAssetId: boolean;
+  readonly asNativeAssetId: PalletForeignAssetsNativeCurrency;
+  readonly type: 'ForeignAssetId' | 'NativeAssetId';
+}
+
+/** @name PalletForeignAssetsModuleAssetMetadata */
+export interface PalletForeignAssetsModuleAssetMetadata extends Struct {
+  readonly name: Bytes;
+  readonly symbol: Bytes;
+  readonly decimals: u8;
+  readonly minimalBalance: u128;
+}
+
+/** @name PalletForeignAssetsModuleCall */
+export interface PalletForeignAssetsModuleCall extends Enum {
+  readonly isRegisterForeignAsset: boolean;
+  readonly asRegisterForeignAsset: {
+    readonly owner: AccountId32;
+    readonly location: StagingXcmVersionedMultiLocation;
+    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+  } & Struct;
+  readonly isUpdateForeignAsset: boolean;
+  readonly asUpdateForeignAsset: {
+    readonly foreignAssetId: u32;
+    readonly location: StagingXcmVersionedMultiLocation;
+    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+  } & Struct;
+  readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset';
+}
+
+/** @name PalletForeignAssetsModuleError */
+export interface PalletForeignAssetsModuleError extends Enum {
+  readonly isBadLocation: boolean;
+  readonly isMultiLocationExisted: boolean;
+  readonly isAssetIdNotExists: boolean;
+  readonly isAssetIdExisted: boolean;
+  readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted';
+}
+
+/** @name PalletForeignAssetsModuleEvent */
+export interface PalletForeignAssetsModuleEvent extends Enum {
+  readonly isForeignAssetRegistered: boolean;
+  readonly asForeignAssetRegistered: {
+    readonly assetId: u32;
+    readonly assetAddress: StagingXcmV3MultiLocation;
+    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+  } & Struct;
+  readonly isForeignAssetUpdated: boolean;
+  readonly asForeignAssetUpdated: {
+    readonly assetId: u32;
+    readonly assetAddress: StagingXcmV3MultiLocation;
+    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+  } & Struct;
+  readonly isAssetRegistered: boolean;
+  readonly asAssetRegistered: {
+    readonly assetId: PalletForeignAssetsAssetId;
+    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+  } & Struct;
+  readonly isAssetUpdated: boolean;
+  readonly asAssetUpdated: {
+    readonly assetId: PalletForeignAssetsAssetId;
+    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+  } & Struct;
+  readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated';
+}
+
+/** @name PalletForeignAssetsNativeCurrency */
+export interface PalletForeignAssetsNativeCurrency extends Enum {
+  readonly isHere: boolean;
+  readonly isParent: boolean;
+  readonly type: 'Here' | 'Parent';
+}
+
+/** @name PalletFungibleError */
+export interface PalletFungibleError extends Enum {
+  readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
+  readonly isFungibleItemsDontHaveData: boolean;
+  readonly isFungibleDisallowsNesting: boolean;
+  readonly isSettingPropertiesNotAllowed: boolean;
+  readonly isSettingAllowanceForAllNotAllowed: boolean;
+  readonly isFungibleTokensAreAlwaysValid: boolean;
+  readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid';
+}
+
+/** @name PalletGovOriginsOrigin */
+export interface PalletGovOriginsOrigin extends Enum {
+  readonly isFellowshipProposition: boolean;
+  readonly type: 'FellowshipProposition';
+}
+
+/** @name PalletIdentityBitFlags */
+export interface PalletIdentityBitFlags extends Struct {
+  readonly _bitLength: 64;
+  readonly Display: 1;
+  readonly Legal: 2;
+  readonly Web: 4;
+  readonly Riot: 8;
+  readonly Email: 16;
+  readonly PgpFingerprint: 32;
+  readonly Image: 64;
+  readonly Twitter: 128;
+}
+
+/** @name PalletIdentityCall */
+export interface PalletIdentityCall extends Enum {
+  readonly isAddRegistrar: boolean;
+  readonly asAddRegistrar: {
+    readonly account: MultiAddress;
+  } & Struct;
+  readonly isSetIdentity: boolean;
+  readonly asSetIdentity: {
+    readonly info: PalletIdentityIdentityInfo;
+  } & Struct;
+  readonly isSetSubs: boolean;
+  readonly asSetSubs: {
+    readonly subs: Vec<ITuple<[AccountId32, Data]>>;
+  } & Struct;
+  readonly isClearIdentity: boolean;
+  readonly isRequestJudgement: boolean;
+  readonly asRequestJudgement: {
+    readonly regIndex: Compact<u32>;
+    readonly maxFee: Compact<u128>;
+  } & Struct;
+  readonly isCancelRequest: boolean;
+  readonly asCancelRequest: {
+    readonly regIndex: u32;
+  } & Struct;
+  readonly isSetFee: boolean;
+  readonly asSetFee: {
+    readonly index: Compact<u32>;
+    readonly fee: Compact<u128>;
+  } & Struct;
+  readonly isSetAccountId: boolean;
+  readonly asSetAccountId: {
+    readonly index: Compact<u32>;
+    readonly new_: MultiAddress;
+  } & Struct;
+  readonly isSetFields: boolean;
+  readonly asSetFields: {
+    readonly index: Compact<u32>;
+    readonly fields: PalletIdentityBitFlags;
+  } & Struct;
+  readonly isProvideJudgement: boolean;
+  readonly asProvideJudgement: {
+    readonly regIndex: Compact<u32>;
+    readonly target: MultiAddress;
+    readonly judgement: PalletIdentityJudgement;
+    readonly identity: H256;
+  } & Struct;
+  readonly isKillIdentity: boolean;
+  readonly asKillIdentity: {
+    readonly target: MultiAddress;
+  } & Struct;
+  readonly isAddSub: boolean;
+  readonly asAddSub: {
+    readonly sub: MultiAddress;
+    readonly data: Data;
+  } & Struct;
+  readonly isRenameSub: boolean;
+  readonly asRenameSub: {
+    readonly sub: MultiAddress;
+    readonly data: Data;
+  } & Struct;
+  readonly isRemoveSub: boolean;
+  readonly asRemoveSub: {
+    readonly sub: MultiAddress;
+  } & Struct;
+  readonly isQuitSub: boolean;
+  readonly isForceInsertIdentities: boolean;
+  readonly asForceInsertIdentities: {
+    readonly identities: Vec<ITuple<[AccountId32, PalletIdentityRegistration]>>;
+  } & Struct;
+  readonly isForceRemoveIdentities: boolean;
+  readonly asForceRemoveIdentities: {
+    readonly identities: Vec<AccountId32>;
+  } & Struct;
+  readonly isForceSetSubs: boolean;
+  readonly asForceSetSubs: {
+    readonly subs: Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>>;
+  } & Struct;
+  readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs';
+}
+
+/** @name PalletIdentityError */
+export interface PalletIdentityError extends Enum {
+  readonly isTooManySubAccounts: boolean;
+  readonly isNotFound: boolean;
+  readonly isNotNamed: boolean;
+  readonly isEmptyIndex: boolean;
+  readonly isFeeChanged: boolean;
+  readonly isNoIdentity: boolean;
+  readonly isStickyJudgement: boolean;
+  readonly isJudgementGiven: boolean;
+  readonly isInvalidJudgement: boolean;
+  readonly isInvalidIndex: boolean;
+  readonly isInvalidTarget: boolean;
+  readonly isTooManyFields: boolean;
+  readonly isTooManyRegistrars: boolean;
+  readonly isAlreadyClaimed: boolean;
+  readonly isNotSub: boolean;
+  readonly isNotOwned: boolean;
+  readonly isJudgementForDifferentIdentity: boolean;
+  readonly isJudgementPaymentFailed: boolean;
+  readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed';
+}
+
+/** @name PalletIdentityEvent */
+export interface PalletIdentityEvent extends Enum {
+  readonly isIdentitySet: boolean;
+  readonly asIdentitySet: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly isIdentityCleared: boolean;
+  readonly asIdentityCleared: {
+    readonly who: AccountId32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isIdentityKilled: boolean;
+  readonly asIdentityKilled: {
+    readonly who: AccountId32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isIdentitiesInserted: boolean;
+  readonly asIdentitiesInserted: {
+    readonly amount: u32;
+  } & Struct;
+  readonly isIdentitiesRemoved: boolean;
+  readonly asIdentitiesRemoved: {
+    readonly amount: u32;
+  } & Struct;
+  readonly isJudgementRequested: boolean;
+  readonly asJudgementRequested: {
+    readonly who: AccountId32;
+    readonly registrarIndex: u32;
+  } & Struct;
+  readonly isJudgementUnrequested: boolean;
+  readonly asJudgementUnrequested: {
+    readonly who: AccountId32;
+    readonly registrarIndex: u32;
+  } & Struct;
+  readonly isJudgementGiven: boolean;
+  readonly asJudgementGiven: {
+    readonly target: AccountId32;
+    readonly registrarIndex: u32;
+  } & Struct;
+  readonly isRegistrarAdded: boolean;
+  readonly asRegistrarAdded: {
+    readonly registrarIndex: u32;
+  } & Struct;
+  readonly isSubIdentityAdded: boolean;
+  readonly asSubIdentityAdded: {
+    readonly sub: AccountId32;
+    readonly main: AccountId32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isSubIdentityRemoved: boolean;
+  readonly asSubIdentityRemoved: {
+    readonly sub: AccountId32;
+    readonly main: AccountId32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isSubIdentityRevoked: boolean;
+  readonly asSubIdentityRevoked: {
+    readonly sub: AccountId32;
+    readonly main: AccountId32;
+    readonly deposit: u128;
+  } & Struct;
+  readonly isSubIdentitiesInserted: boolean;
+  readonly asSubIdentitiesInserted: {
+    readonly amount: u32;
+  } & Struct;
+  readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted';
+}
+
+/** @name PalletIdentityIdentityField */
+export interface PalletIdentityIdentityField extends Enum {
+  readonly isDisplay: boolean;
+  readonly isLegal: boolean;
+  readonly isWeb: boolean;
+  readonly isRiot: boolean;
+  readonly isEmail: boolean;
+  readonly isPgpFingerprint: boolean;
+  readonly isImage: boolean;
+  readonly isTwitter: boolean;
+  readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter';
+}
+
+/** @name PalletIdentityIdentityInfo */
+export interface PalletIdentityIdentityInfo extends Struct {
+  readonly additional: Vec<ITuple<[Data, Data]>>;
+  readonly display: Data;
+  readonly legal: Data;
+  readonly web: Data;
+  readonly riot: Data;
+  readonly email: Data;
+  readonly pgpFingerprint: Option<U8aFixed>;
+  readonly image: Data;
+  readonly twitter: Data;
+}
+
+/** @name PalletIdentityJudgement */
+export interface PalletIdentityJudgement extends Enum {
+  readonly isUnknown: boolean;
+  readonly isFeePaid: boolean;
+  readonly asFeePaid: u128;
+  readonly isReasonable: boolean;
+  readonly isKnownGood: boolean;
+  readonly isOutOfDate: boolean;
+  readonly isLowQuality: boolean;
+  readonly isErroneous: boolean;
+  readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous';
+}
+
+/** @name PalletIdentityRegistrarInfo */
+export interface PalletIdentityRegistrarInfo extends Struct {
+  readonly account: AccountId32;
+  readonly fee: u128;
+  readonly fields: PalletIdentityBitFlags;
+}
+
+/** @name PalletIdentityRegistration */
+export interface PalletIdentityRegistration extends Struct {
+  readonly judgements: Vec<ITuple<[u32, PalletIdentityJudgement]>>;
+  readonly deposit: u128;
+  readonly info: PalletIdentityIdentityInfo;
+}
+
+/** @name PalletInflationCall */
+export interface PalletInflationCall extends Enum {
+  readonly isStartInflation: boolean;
+  readonly asStartInflation: {
+    readonly inflationStartRelayBlock: u32;
+  } & Struct;
+  readonly type: 'StartInflation';
+}
+
+/** @name PalletMaintenanceCall */
+export interface PalletMaintenanceCall extends Enum {
+  readonly isEnable: boolean;
+  readonly isDisable: boolean;
+  readonly type: 'Enable' | 'Disable';
+}
+
+/** @name PalletMaintenanceError */
+export interface PalletMaintenanceError extends Null {}
+
+/** @name PalletMaintenanceEvent */
+export interface PalletMaintenanceEvent extends Enum {
+  readonly isMaintenanceEnabled: boolean;
+  readonly isMaintenanceDisabled: boolean;
+  readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled';
+}
+
+/** @name PalletMembershipCall */
+export interface PalletMembershipCall extends Enum {
+  readonly isAddMember: boolean;
+  readonly asAddMember: {
+    readonly who: MultiAddress;
+  } & Struct;
+  readonly isRemoveMember: boolean;
+  readonly asRemoveMember: {
+    readonly who: MultiAddress;
+  } & Struct;
+  readonly isSwapMember: boolean;
+  readonly asSwapMember: {
+    readonly remove: MultiAddress;
+    readonly add: MultiAddress;
+  } & Struct;
+  readonly isResetMembers: boolean;
+  readonly asResetMembers: {
+    readonly members: Vec<AccountId32>;
+  } & Struct;
+  readonly isChangeKey: boolean;
+  readonly asChangeKey: {
+    readonly new_: MultiAddress;
+  } & Struct;
+  readonly isSetPrime: boolean;
+  readonly asSetPrime: {
+    readonly who: MultiAddress;
+  } & Struct;
+  readonly isClearPrime: boolean;
+  readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime';
+}
+
+/** @name PalletMembershipError */
+export interface PalletMembershipError extends Enum {
+  readonly isAlreadyMember: boolean;
+  readonly isNotMember: boolean;
+  readonly isTooManyMembers: boolean;
+  readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers';
+}
+
+/** @name PalletMembershipEvent */
+export interface PalletMembershipEvent extends Enum {
+  readonly isMemberAdded: boolean;
+  readonly isMemberRemoved: boolean;
+  readonly isMembersSwapped: boolean;
+  readonly isMembersReset: boolean;
+  readonly isKeyChanged: boolean;
+  readonly isDummy: boolean;
+  readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy';
+}
+
+/** @name PalletNonfungibleError */
+export interface PalletNonfungibleError extends Enum {
+  readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
+  readonly isNonfungibleItemsHaveNoAmount: boolean;
+  readonly isCantBurnNftWithChildren: boolean;
+  readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren';
+}
+
+/** @name PalletNonfungibleItemData */
+export interface PalletNonfungibleItemData extends Struct {
+  readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+}
+
+/** @name PalletPreimageCall */
+export interface PalletPreimageCall extends Enum {
+  readonly isNotePreimage: boolean;
+  readonly asNotePreimage: {
+    readonly bytes: Bytes;
+  } & Struct;
+  readonly isUnnotePreimage: boolean;
+  readonly asUnnotePreimage: {
+    readonly hash_: H256;
+  } & Struct;
+  readonly isRequestPreimage: boolean;
+  readonly asRequestPreimage: {
+    readonly hash_: H256;
+  } & Struct;
+  readonly isUnrequestPreimage: boolean;
+  readonly asUnrequestPreimage: {
+    readonly hash_: H256;
+  } & Struct;
+  readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage';
+}
+
+/** @name PalletPreimageError */
+export interface PalletPreimageError extends Enum {
+  readonly isTooBig: boolean;
+  readonly isAlreadyNoted: boolean;
+  readonly isNotAuthorized: boolean;
+  readonly isNotNoted: boolean;
+  readonly isRequested: boolean;
+  readonly isNotRequested: boolean;
+  readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested';
+}
+
+/** @name PalletPreimageEvent */
+export interface PalletPreimageEvent extends Enum {
+  readonly isNoted: boolean;
+  readonly asNoted: {
+    readonly hash_: H256;
+  } & Struct;
+  readonly isRequested: boolean;
+  readonly asRequested: {
+    readonly hash_: H256;
+  } & Struct;
+  readonly isCleared: boolean;
+  readonly asCleared: {
+    readonly hash_: H256;
+  } & Struct;
+  readonly type: 'Noted' | 'Requested' | 'Cleared';
+}
+
+/** @name PalletPreimageRequestStatus */
+export interface PalletPreimageRequestStatus extends Enum {
+  readonly isUnrequested: boolean;
+  readonly asUnrequested: {
+    readonly deposit: ITuple<[AccountId32, u128]>;
+    readonly len: u32;
+  } & Struct;
+  readonly isRequested: boolean;
+  readonly asRequested: {
+    readonly deposit: Option<ITuple<[AccountId32, u128]>>;
+    readonly count: u32;
+    readonly len: Option<u32>;
+  } & Struct;
+  readonly type: 'Unrequested' | 'Requested';
+}
+
+/** @name PalletRankedCollectiveCall */
+export interface PalletRankedCollectiveCall extends Enum {
+  readonly isAddMember: boolean;
+  readonly asAddMember: {
+    readonly who: MultiAddress;
+  } & Struct;
+  readonly isPromoteMember: boolean;
+  readonly asPromoteMember: {
+    readonly who: MultiAddress;
+  } & Struct;
+  readonly isDemoteMember: boolean;
+  readonly asDemoteMember: {
+    readonly who: MultiAddress;
+  } & Struct;
+  readonly isRemoveMember: boolean;
+  readonly asRemoveMember: {
+    readonly who: MultiAddress;
+    readonly minRank: u16;
+  } & Struct;
+  readonly isVote: boolean;
+  readonly asVote: {
+    readonly poll: u32;
+    readonly aye: bool;
+  } & Struct;
+  readonly isCleanupPoll: boolean;
+  readonly asCleanupPoll: {
+    readonly pollIndex: u32;
+    readonly max: u32;
+  } & Struct;
+  readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll';
+}
+
+/** @name PalletRankedCollectiveError */
+export interface PalletRankedCollectiveError extends Enum {
+  readonly isAlreadyMember: boolean;
+  readonly isNotMember: boolean;
+  readonly isNotPolling: boolean;
+  readonly isOngoing: boolean;
+  readonly isNoneRemaining: boolean;
+  readonly isCorruption: boolean;
+  readonly isRankTooLow: boolean;
+  readonly isInvalidWitness: boolean;
+  readonly isNoPermission: boolean;
+  readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission';
+}
+
+/** @name PalletRankedCollectiveEvent */
+export interface PalletRankedCollectiveEvent extends Enum {
+  readonly isMemberAdded: boolean;
+  readonly asMemberAdded: {
+    readonly who: AccountId32;
+  } & Struct;
+  readonly isRankChanged: boolean;
+  readonly asRankChanged: {
+    readonly who: AccountId32;
+    readonly rank: u16;
+  } & Struct;
+  readonly isMemberRemoved: boolean;
+  readonly asMemberRemoved: {
+    readonly who: AccountId32;
+    readonly rank: u16;
+  } & Struct;
+  readonly isVoted: boolean;
+  readonly asVoted: {
+    readonly who: AccountId32;
+    readonly poll: u32;
+    readonly vote: PalletRankedCollectiveVoteRecord;
+    readonly tally: PalletRankedCollectiveTally;
+  } & Struct;
+  readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted';
+}
+
+/** @name PalletRankedCollectiveMemberRecord */
+export interface PalletRankedCollectiveMemberRecord extends Struct {
+  readonly rank: u16;
+}
+
+/** @name PalletRankedCollectiveTally */
+export interface PalletRankedCollectiveTally extends Struct {
+  readonly bareAyes: u32;
+  readonly ayes: u32;
+  readonly nays: u32;
+}
+
+/** @name PalletRankedCollectiveVoteRecord */
+export interface PalletRankedCollectiveVoteRecord extends Enum {
+  readonly isAye: boolean;
+  readonly asAye: u32;
+  readonly isNay: boolean;
+  readonly asNay: u32;
+  readonly type: 'Aye' | 'Nay';
+}
+
+/** @name PalletReferendaCall */
+export interface PalletReferendaCall extends Enum {
+  readonly isSubmit: boolean;
+  readonly asSubmit: {
+    readonly proposalOrigin: OpalRuntimeOriginCaller;
+    readonly proposal: FrameSupportPreimagesBounded;
+    readonly enactmentMoment: FrameSupportScheduleDispatchTime;
+  } & Struct;
+  readonly isPlaceDecisionDeposit: boolean;
+  readonly asPlaceDecisionDeposit: {
+    readonly index: u32;
+  } & Struct;
+  readonly isRefundDecisionDeposit: boolean;
+  readonly asRefundDecisionDeposit: {
+    readonly index: u32;
+  } & Struct;
+  readonly isCancel: boolean;
+  readonly asCancel: {
+    readonly index: u32;
+  } & Struct;
+  readonly isKill: boolean;
+  readonly asKill: {
+    readonly index: u32;
+  } & Struct;
+  readonly isNudgeReferendum: boolean;
+  readonly asNudgeReferendum: {
+    readonly index: u32;
+  } & Struct;
+  readonly isOneFewerDeciding: boolean;
+  readonly asOneFewerDeciding: {
+    readonly track: u16;
+  } & Struct;
+  readonly isRefundSubmissionDeposit: boolean;
+  readonly asRefundSubmissionDeposit: {
+    readonly index: u32;
+  } & Struct;
+  readonly isSetMetadata: boolean;
+  readonly asSetMetadata: {
+    readonly index: u32;
+    readonly maybeHash: Option<H256>;
+  } & Struct;
+  readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata';
+}
+
+/** @name PalletReferendaCurve */
+export interface PalletReferendaCurve extends Enum {
+  readonly isLinearDecreasing: boolean;
+  readonly asLinearDecreasing: {
+    readonly length: Perbill;
+    readonly floor: Perbill;
+    readonly ceil: Perbill;
+  } & Struct;
+  readonly isSteppedDecreasing: boolean;
+  readonly asSteppedDecreasing: {
+    readonly begin: Perbill;
+    readonly end: Perbill;
+    readonly step: Perbill;
+    readonly period: Perbill;
+  } & Struct;
+  readonly isReciprocal: boolean;
+  readonly asReciprocal: {
+    readonly factor: i64;
+    readonly xOffset: i64;
+    readonly yOffset: i64;
+  } & Struct;
+  readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal';
+}
+
+/** @name PalletReferendaDecidingStatus */
+export interface PalletReferendaDecidingStatus extends Struct {
+  readonly since: u32;
+  readonly confirming: Option<u32>;
+}
+
+/** @name PalletReferendaDeposit */
+export interface PalletReferendaDeposit extends Struct {
+  readonly who: AccountId32;
+  readonly amount: u128;
+}
+
+/** @name PalletReferendaError */
+export interface PalletReferendaError extends Enum {
+  readonly isNotOngoing: boolean;
+  readonly isHasDeposit: boolean;
+  readonly isBadTrack: boolean;
+  readonly isFull: boolean;
+  readonly isQueueEmpty: boolean;
+  readonly isBadReferendum: boolean;
+  readonly isNothingToDo: boolean;
+  readonly isNoTrack: boolean;
+  readonly isUnfinished: boolean;
+  readonly isNoPermission: boolean;
+  readonly isNoDeposit: boolean;
+  readonly isBadStatus: boolean;
+  readonly isPreimageNotExist: boolean;
+  readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist';
+}
+
+/** @name PalletReferendaEvent */
+export interface PalletReferendaEvent extends Enum {
+  readonly isSubmitted: boolean;
+  readonly asSubmitted: {
+    readonly index: u32;
+    readonly track: u16;
+    readonly proposal: FrameSupportPreimagesBounded;
+  } & Struct;
+  readonly isDecisionDepositPlaced: boolean;
+  readonly asDecisionDepositPlaced: {
+    readonly index: u32;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isDecisionDepositRefunded: boolean;
+  readonly asDecisionDepositRefunded: {
+    readonly index: u32;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isDepositSlashed: boolean;
+  readonly asDepositSlashed: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isDecisionStarted: boolean;
+  readonly asDecisionStarted: {
+    readonly index: u32;
+    readonly track: u16;
+    readonly proposal: FrameSupportPreimagesBounded;
+    readonly tally: PalletRankedCollectiveTally;
+  } & Struct;
+  readonly isConfirmStarted: boolean;
+  readonly asConfirmStarted: {
+    readonly index: u32;
+  } & Struct;
+  readonly isConfirmAborted: boolean;
+  readonly asConfirmAborted: {
+    readonly index: u32;
+  } & Struct;
+  readonly isConfirmed: boolean;
+  readonly asConfirmed: {
+    readonly index: u32;
+    readonly tally: PalletRankedCollectiveTally;
+  } & Struct;
+  readonly isApproved: boolean;
+  readonly asApproved: {
+    readonly index: u32;
+  } & Struct;
+  readonly isRejected: boolean;
+  readonly asRejected: {
+    readonly index: u32;
+    readonly tally: PalletRankedCollectiveTally;
+  } & Struct;
+  readonly isTimedOut: boolean;
+  readonly asTimedOut: {
+    readonly index: u32;
+    readonly tally: PalletRankedCollectiveTally;
+  } & Struct;
+  readonly isCancelled: boolean;
+  readonly asCancelled: {
+    readonly index: u32;
+    readonly tally: PalletRankedCollectiveTally;
+  } & Struct;
+  readonly isKilled: boolean;
+  readonly asKilled: {
+    readonly index: u32;
+    readonly tally: PalletRankedCollectiveTally;
+  } & Struct;
+  readonly isSubmissionDepositRefunded: boolean;
+  readonly asSubmissionDepositRefunded: {
+    readonly index: u32;
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isMetadataSet: boolean;
+  readonly asMetadataSet: {
+    readonly index: u32;
+    readonly hash_: H256;
+  } & Struct;
+  readonly isMetadataCleared: boolean;
+  readonly asMetadataCleared: {
+    readonly index: u32;
+    readonly hash_: H256;
+  } & Struct;
+  readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared';
+}
+
+/** @name PalletReferendaReferendumInfo */
+export interface PalletReferendaReferendumInfo extends Enum {
+  readonly isOngoing: boolean;
+  readonly asOngoing: PalletReferendaReferendumStatus;
+  readonly isApproved: boolean;
+  readonly asApproved: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+  readonly isRejected: boolean;
+  readonly asRejected: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+  readonly isCancelled: boolean;
+  readonly asCancelled: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+  readonly isTimedOut: boolean;
+  readonly asTimedOut: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+  readonly isKilled: boolean;
+  readonly asKilled: u32;
+  readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed';
+}
+
+/** @name PalletReferendaReferendumStatus */
+export interface PalletReferendaReferendumStatus extends Struct {
+  readonly track: u16;
+  readonly origin: OpalRuntimeOriginCaller;
+  readonly proposal: FrameSupportPreimagesBounded;
+  readonly enactment: FrameSupportScheduleDispatchTime;
+  readonly submitted: u32;
+  readonly submissionDeposit: PalletReferendaDeposit;
+  readonly decisionDeposit: Option<PalletReferendaDeposit>;
+  readonly deciding: Option<PalletReferendaDecidingStatus>;
+  readonly tally: PalletRankedCollectiveTally;
+  readonly inQueue: bool;
+  readonly alarm: Option<ITuple<[u32, ITuple<[u32, u32]>]>>;
+}
+
+/** @name PalletReferendaTrackInfo */
+export interface PalletReferendaTrackInfo extends Struct {
+  readonly name: Text;
+  readonly maxDeciding: u32;
+  readonly decisionDeposit: u128;
+  readonly preparePeriod: u32;
+  readonly decisionPeriod: u32;
+  readonly confirmPeriod: u32;
+  readonly minEnactmentPeriod: u32;
+  readonly minApproval: PalletReferendaCurve;
+  readonly minSupport: PalletReferendaCurve;
+}
+
+/** @name PalletRefungibleError */
+export interface PalletRefungibleError extends Enum {
+  readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
+  readonly isWrongRefungiblePieces: boolean;
+  readonly isRepartitionWhileNotOwningAllPieces: boolean;
+  readonly isRefungibleDisallowsNesting: boolean;
+  readonly isSettingPropertiesNotAllowed: boolean;
+  readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
+}
+
+/** @name PalletSchedulerCall */
+export interface PalletSchedulerCall extends Enum {
+  readonly isSchedule: boolean;
+  readonly asSchedule: {
+    readonly when: u32;
+    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+    readonly priority: u8;
+    readonly call: Call;
+  } & Struct;
+  readonly isCancel: boolean;
+  readonly asCancel: {
+    readonly when: u32;
+    readonly index: u32;
+  } & Struct;
+  readonly isScheduleNamed: boolean;
+  readonly asScheduleNamed: {
+    readonly id: U8aFixed;
+    readonly when: u32;
+    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+    readonly priority: u8;
+    readonly call: Call;
+  } & Struct;
+  readonly isCancelNamed: boolean;
+  readonly asCancelNamed: {
+    readonly id: U8aFixed;
+  } & Struct;
+  readonly isScheduleAfter: boolean;
+  readonly asScheduleAfter: {
+    readonly after: u32;
+    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+    readonly priority: u8;
+    readonly call: Call;
+  } & Struct;
+  readonly isScheduleNamedAfter: boolean;
+  readonly asScheduleNamedAfter: {
+    readonly id: U8aFixed;
+    readonly after: u32;
+    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+    readonly priority: u8;
+    readonly call: Call;
+  } & Struct;
+  readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter';
+}
+
+/** @name PalletSchedulerError */
+export interface PalletSchedulerError extends Enum {
+  readonly isFailedToSchedule: boolean;
+  readonly isNotFound: boolean;
+  readonly isTargetBlockNumberInPast: boolean;
+  readonly isRescheduleNoChange: boolean;
+  readonly isNamed: boolean;
+  readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named';
+}
+
+/** @name PalletSchedulerEvent */
+export interface PalletSchedulerEvent extends Enum {
+  readonly isScheduled: boolean;
+  readonly asScheduled: {
+    readonly when: u32;
+    readonly index: u32;
+  } & Struct;
+  readonly isCanceled: boolean;
+  readonly asCanceled: {
+    readonly when: u32;
+    readonly index: u32;
+  } & Struct;
+  readonly isDispatched: boolean;
+  readonly asDispatched: {
+    readonly task: ITuple<[u32, u32]>;
+    readonly id: Option<U8aFixed>;
+    readonly result: Result<Null, SpRuntimeDispatchError>;
+  } & Struct;
+  readonly isCallUnavailable: boolean;
+  readonly asCallUnavailable: {
+    readonly task: ITuple<[u32, u32]>;
+    readonly id: Option<U8aFixed>;
+  } & Struct;
+  readonly isPeriodicFailed: boolean;
+  readonly asPeriodicFailed: {
+    readonly task: ITuple<[u32, u32]>;
+    readonly id: Option<U8aFixed>;
+  } & Struct;
+  readonly isPermanentlyOverweight: boolean;
+  readonly asPermanentlyOverweight: {
+    readonly task: ITuple<[u32, u32]>;
+    readonly id: Option<U8aFixed>;
+  } & Struct;
+  readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight';
+}
+
+/** @name PalletSchedulerScheduled */
+export interface PalletSchedulerScheduled extends Struct {
+  readonly maybeId: Option<U8aFixed>;
+  readonly priority: u8;
+  readonly call: FrameSupportPreimagesBounded;
+  readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+  readonly origin: OpalRuntimeOriginCaller;
+}
+
+/** @name PalletSessionCall */
+export interface PalletSessionCall extends Enum {
+  readonly isSetKeys: boolean;
+  readonly asSetKeys: {
+    readonly keys_: OpalRuntimeRuntimeCommonSessionKeys;
+    readonly proof: Bytes;
+  } & Struct;
+  readonly isPurgeKeys: boolean;
+  readonly type: 'SetKeys' | 'PurgeKeys';
+}
+
+/** @name PalletSessionError */
+export interface PalletSessionError extends Enum {
+  readonly isInvalidProof: boolean;
+  readonly isNoAssociatedValidatorId: boolean;
+  readonly isDuplicatedKey: boolean;
+  readonly isNoKeys: boolean;
+  readonly isNoAccount: boolean;
+  readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount';
+}
+
+/** @name PalletSessionEvent */
+export interface PalletSessionEvent extends Enum {
+  readonly isNewSession: boolean;
+  readonly asNewSession: {
+    readonly sessionIndex: u32;
+  } & Struct;
+  readonly type: 'NewSession';
+}
+
+/** @name PalletStateTrieMigrationCall */
+export interface PalletStateTrieMigrationCall extends Enum {
+  readonly isControlAutoMigration: boolean;
+  readonly asControlAutoMigration: {
+    readonly maybeConfig: Option<PalletStateTrieMigrationMigrationLimits>;
+  } & Struct;
+  readonly isContinueMigrate: boolean;
+  readonly asContinueMigrate: {
+    readonly limits: PalletStateTrieMigrationMigrationLimits;
+    readonly realSizeUpper: u32;
+    readonly witnessTask: PalletStateTrieMigrationMigrationTask;
+  } & Struct;
+  readonly isMigrateCustomTop: boolean;
+  readonly asMigrateCustomTop: {
+    readonly keys_: Vec<Bytes>;
+    readonly witnessSize: u32;
+  } & Struct;
+  readonly isMigrateCustomChild: boolean;
+  readonly asMigrateCustomChild: {
+    readonly root: Bytes;
+    readonly childKeys: Vec<Bytes>;
+    readonly totalSize: u32;
+  } & Struct;
+  readonly isSetSignedMaxLimits: boolean;
+  readonly asSetSignedMaxLimits: {
+    readonly limits: PalletStateTrieMigrationMigrationLimits;
+  } & Struct;
+  readonly isForceSetProgress: boolean;
+  readonly asForceSetProgress: {
+    readonly progressTop: PalletStateTrieMigrationProgress;
+    readonly progressChild: PalletStateTrieMigrationProgress;
+  } & Struct;
+  readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress';
+}
+
+/** @name PalletStateTrieMigrationError */
+export interface PalletStateTrieMigrationError extends Enum {
+  readonly isMaxSignedLimits: boolean;
+  readonly isKeyTooLong: boolean;
+  readonly isNotEnoughFunds: boolean;
+  readonly isBadWitness: boolean;
+  readonly isSignedMigrationNotAllowed: boolean;
+  readonly isBadChildRoot: boolean;
+  readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot';
+}
+
+/** @name PalletStateTrieMigrationEvent */
+export interface PalletStateTrieMigrationEvent extends Enum {
+  readonly isMigrated: boolean;
+  readonly asMigrated: {
+    readonly top: u32;
+    readonly child: u32;
+    readonly compute: PalletStateTrieMigrationMigrationCompute;
+  } & Struct;
+  readonly isSlashed: boolean;
+  readonly asSlashed: {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isAutoMigrationFinished: boolean;
+  readonly isHalted: boolean;
+  readonly asHalted: {
+    readonly error: PalletStateTrieMigrationError;
+  } & Struct;
+  readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted';
+}
+
+/** @name PalletStateTrieMigrationMigrationCompute */
+export interface PalletStateTrieMigrationMigrationCompute extends Enum {
+  readonly isSigned: boolean;
+  readonly isAuto: boolean;
+  readonly type: 'Signed' | 'Auto';
+}
+
+/** @name PalletStateTrieMigrationMigrationLimits */
+export interface PalletStateTrieMigrationMigrationLimits extends Struct {
+  readonly size_: u32;
+  readonly item: u32;
+}
+
+/** @name PalletStateTrieMigrationMigrationTask */
+export interface PalletStateTrieMigrationMigrationTask extends Struct {
+  readonly progressTop: PalletStateTrieMigrationProgress;
+  readonly progressChild: PalletStateTrieMigrationProgress;
+  readonly size_: u32;
+  readonly topItems: u32;
+  readonly childItems: u32;
+}
+
+/** @name PalletStateTrieMigrationProgress */
+export interface PalletStateTrieMigrationProgress extends Enum {
+  readonly isToStart: boolean;
+  readonly isLastKey: boolean;
+  readonly asLastKey: Bytes;
+  readonly isComplete: boolean;
+  readonly type: 'ToStart' | 'LastKey' | 'Complete';
+}
+
+/** @name PalletStructureCall */
+export interface PalletStructureCall extends Null {}
+
+/** @name PalletStructureError */
+export interface PalletStructureError extends Enum {
+  readonly isOuroborosDetected: boolean;
+  readonly isDepthLimit: boolean;
+  readonly isBreadthLimit: boolean;
+  readonly isTokenNotFound: boolean;
+  readonly isCantNestTokenUnderCollection: boolean;
+  readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection';
+}
+
+/** @name PalletStructureEvent */
+export interface PalletStructureEvent extends Enum {
+  readonly isExecuted: boolean;
+  readonly asExecuted: Result<Null, SpRuntimeDispatchError>;
+  readonly type: 'Executed';
+}
+
+/** @name PalletSudoCall */
+export interface PalletSudoCall extends Enum {
+  readonly isSudo: boolean;
+  readonly asSudo: {
+    readonly call: Call;
+  } & Struct;
+  readonly isSudoUncheckedWeight: boolean;
+  readonly asSudoUncheckedWeight: {
+    readonly call: Call;
+    readonly weight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isSetKey: boolean;
+  readonly asSetKey: {
+    readonly new_: MultiAddress;
+  } & Struct;
+  readonly isSudoAs: boolean;
+  readonly asSudoAs: {
+    readonly who: MultiAddress;
+    readonly call: Call;
+  } & Struct;
+  readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs';
+}
+
+/** @name PalletSudoError */
+export interface PalletSudoError extends Enum {
+  readonly isRequireSudo: boolean;
+  readonly type: 'RequireSudo';
+}
+
+/** @name PalletSudoEvent */
+export interface PalletSudoEvent extends Enum {
+  readonly isSudid: boolean;
+  readonly asSudid: {
+    readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
+  } & Struct;
+  readonly isKeyChanged: boolean;
+  readonly asKeyChanged: {
+    readonly oldSudoer: Option<AccountId32>;
+  } & Struct;
+  readonly isSudoAsDone: boolean;
+  readonly asSudoAsDone: {
+    readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
+  } & Struct;
+  readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone';
+}
+
+/** @name PalletTemplateTransactionPaymentChargeTransactionPayment */
+export interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
+
+/** @name PalletTestUtilsCall */
+export interface PalletTestUtilsCall extends Enum {
+  readonly isEnable: boolean;
+  readonly isSetTestValue: boolean;
+  readonly asSetTestValue: {
+    readonly value: u32;
+  } & Struct;
+  readonly isSetTestValueAndRollback: boolean;
+  readonly asSetTestValueAndRollback: {
+    readonly value: u32;
+  } & Struct;
+  readonly isIncTestValue: boolean;
+  readonly isJustTakeFee: boolean;
+  readonly isBatchAll: boolean;
+  readonly asBatchAll: {
+    readonly calls: Vec<Call>;
+  } & Struct;
+  readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll';
+}
+
+/** @name PalletTestUtilsError */
+export interface PalletTestUtilsError extends Enum {
+  readonly isTestPalletDisabled: boolean;
+  readonly isTriggerRollback: boolean;
+  readonly type: 'TestPalletDisabled' | 'TriggerRollback';
+}
+
+/** @name PalletTestUtilsEvent */
+export interface PalletTestUtilsEvent extends Enum {
+  readonly isValueIsSet: boolean;
+  readonly isShouldRollback: boolean;
+  readonly isBatchCompleted: boolean;
+  readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted';
+}
+
+/** @name PalletTimestampCall */
+export interface PalletTimestampCall extends Enum {
+  readonly isSet: boolean;
+  readonly asSet: {
+    readonly now: Compact<u64>;
+  } & Struct;
+  readonly type: 'Set';
+}
+
+/** @name PalletTransactionPaymentEvent */
+export interface PalletTransactionPaymentEvent extends Enum {
+  readonly isTransactionFeePaid: boolean;
+  readonly asTransactionFeePaid: {
+    readonly who: AccountId32;
+    readonly actualFee: u128;
+    readonly tip: u128;
+  } & Struct;
+  readonly type: 'TransactionFeePaid';
+}
+
+/** @name PalletTransactionPaymentReleases */
+export interface PalletTransactionPaymentReleases extends Enum {
+  readonly isV1Ancient: boolean;
+  readonly isV2: boolean;
+  readonly type: 'V1Ancient' | 'V2';
+}
+
+/** @name PalletTreasuryCall */
+export interface PalletTreasuryCall extends Enum {
+  readonly isProposeSpend: boolean;
+  readonly asProposeSpend: {
+    readonly value: Compact<u128>;
+    readonly beneficiary: MultiAddress;
+  } & Struct;
+  readonly isRejectProposal: boolean;
+  readonly asRejectProposal: {
+    readonly proposalId: Compact<u32>;
+  } & Struct;
+  readonly isApproveProposal: boolean;
+  readonly asApproveProposal: {
+    readonly proposalId: Compact<u32>;
+  } & Struct;
+  readonly isSpend: boolean;
+  readonly asSpend: {
+    readonly amount: Compact<u128>;
+    readonly beneficiary: MultiAddress;
+  } & Struct;
+  readonly isRemoveApproval: boolean;
+  readonly asRemoveApproval: {
+    readonly proposalId: Compact<u32>;
+  } & Struct;
+  readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval';
+}
+
+/** @name PalletTreasuryError */
+export interface PalletTreasuryError extends Enum {
+  readonly isInsufficientProposersBalance: boolean;
+  readonly isInvalidIndex: boolean;
+  readonly isTooManyApprovals: boolean;
+  readonly isInsufficientPermission: boolean;
+  readonly isProposalNotApproved: boolean;
+  readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved';
+}
+
+/** @name PalletTreasuryEvent */
+export interface PalletTreasuryEvent extends Enum {
+  readonly isProposed: boolean;
+  readonly asProposed: {
+    readonly proposalIndex: u32;
+  } & Struct;
+  readonly isSpending: boolean;
+  readonly asSpending: {
+    readonly budgetRemaining: u128;
+  } & Struct;
+  readonly isAwarded: boolean;
+  readonly asAwarded: {
+    readonly proposalIndex: u32;
+    readonly award: u128;
+    readonly account: AccountId32;
+  } & Struct;
+  readonly isRejected: boolean;
+  readonly asRejected: {
+    readonly proposalIndex: u32;
+    readonly slashed: u128;
+  } & Struct;
+  readonly isBurnt: boolean;
+  readonly asBurnt: {
+    readonly burntFunds: u128;
+  } & Struct;
+  readonly isRollover: boolean;
+  readonly asRollover: {
+    readonly rolloverBalance: u128;
+  } & Struct;
+  readonly isDeposit: boolean;
+  readonly asDeposit: {
+    readonly value: u128;
+  } & Struct;
+  readonly isSpendApproved: boolean;
+  readonly asSpendApproved: {
+    readonly proposalIndex: u32;
+    readonly amount: u128;
+    readonly beneficiary: AccountId32;
+  } & Struct;
+  readonly isUpdatedInactive: boolean;
+  readonly asUpdatedInactive: {
+    readonly reactivated: u128;
+    readonly deactivated: u128;
+  } & Struct;
+  readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive';
+}
+
+/** @name PalletTreasuryProposal */
+export interface PalletTreasuryProposal extends Struct {
+  readonly proposer: AccountId32;
+  readonly value: u128;
+  readonly beneficiary: AccountId32;
+  readonly bond: u128;
+}
+
+/** @name PalletUniqueCall */
+export interface PalletUniqueCall extends Enum {
+  readonly isCreateCollection: boolean;
+  readonly asCreateCollection: {
+    readonly collectionName: Vec<u16>;
+    readonly collectionDescription: Vec<u16>;
+    readonly tokenPrefix: Bytes;
+    readonly mode: UpDataStructsCollectionMode;
+  } & Struct;
+  readonly isCreateCollectionEx: boolean;
+  readonly asCreateCollectionEx: {
+    readonly data: UpDataStructsCreateCollectionData;
+  } & Struct;
+  readonly isDestroyCollection: boolean;
+  readonly asDestroyCollection: {
+    readonly collectionId: u32;
+  } & Struct;
+  readonly isAddToAllowList: boolean;
+  readonly asAddToAllowList: {
+    readonly collectionId: u32;
+    readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
+  } & Struct;
+  readonly isRemoveFromAllowList: boolean;
+  readonly asRemoveFromAllowList: {
+    readonly collectionId: u32;
+    readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
+  } & Struct;
+  readonly isChangeCollectionOwner: boolean;
+  readonly asChangeCollectionOwner: {
+    readonly collectionId: u32;
+    readonly newOwner: AccountId32;
+  } & Struct;
+  readonly isAddCollectionAdmin: boolean;
+  readonly asAddCollectionAdmin: {
+    readonly collectionId: u32;
+    readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr;
+  } & Struct;
+  readonly isRemoveCollectionAdmin: boolean;
+  readonly asRemoveCollectionAdmin: {
+    readonly collectionId: u32;
+    readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr;
+  } & Struct;
+  readonly isSetCollectionSponsor: boolean;
+  readonly asSetCollectionSponsor: {
+    readonly collectionId: u32;
+    readonly newSponsor: AccountId32;
+  } & Struct;
+  readonly isConfirmSponsorship: boolean;
+  readonly asConfirmSponsorship: {
+    readonly collectionId: u32;
+  } & Struct;
+  readonly isRemoveCollectionSponsor: boolean;
+  readonly asRemoveCollectionSponsor: {
+    readonly collectionId: u32;
+  } & Struct;
+  readonly isCreateItem: boolean;
+  readonly asCreateItem: {
+    readonly collectionId: u32;
+    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly data: UpDataStructsCreateItemData;
+  } & Struct;
+  readonly isCreateMultipleItems: boolean;
+  readonly asCreateMultipleItems: {
+    readonly collectionId: u32;
+    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly itemsData: Vec<UpDataStructsCreateItemData>;
+  } & Struct;
+  readonly isSetCollectionProperties: boolean;
+  readonly asSetCollectionProperties: {
+    readonly collectionId: u32;
+    readonly properties: Vec<UpDataStructsProperty>;
+  } & Struct;
+  readonly isDeleteCollectionProperties: boolean;
+  readonly asDeleteCollectionProperties: {
+    readonly collectionId: u32;
+    readonly propertyKeys: Vec<Bytes>;
+  } & Struct;
+  readonly isSetTokenProperties: boolean;
+  readonly asSetTokenProperties: {
+    readonly collectionId: u32;
+    readonly tokenId: u32;
+    readonly properties: Vec<UpDataStructsProperty>;
+  } & Struct;
+  readonly isDeleteTokenProperties: boolean;
+  readonly asDeleteTokenProperties: {
+    readonly collectionId: u32;
+    readonly tokenId: u32;
+    readonly propertyKeys: Vec<Bytes>;
+  } & Struct;
+  readonly isSetTokenPropertyPermissions: boolean;
+  readonly asSetTokenPropertyPermissions: {
+    readonly collectionId: u32;
+    readonly propertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
+  } & Struct;
+  readonly isCreateMultipleItemsEx: boolean;
+  readonly asCreateMultipleItemsEx: {
+    readonly collectionId: u32;
+    readonly data: UpDataStructsCreateItemExData;
+  } & Struct;
+  readonly isSetTransfersEnabledFlag: boolean;
+  readonly asSetTransfersEnabledFlag: {
+    readonly collectionId: u32;
+    readonly value: bool;
+  } & Struct;
+  readonly isBurnItem: boolean;
+  readonly asBurnItem: {
+    readonly collectionId: u32;
+    readonly itemId: u32;
+    readonly value: u128;
+  } & Struct;
+  readonly isBurnFrom: boolean;
+  readonly asBurnFrom: {
+    readonly collectionId: u32;
+    readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly itemId: u32;
+    readonly value: u128;
+  } & Struct;
+  readonly isTransfer: boolean;
+  readonly asTransfer: {
+    readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly collectionId: u32;
+    readonly itemId: u32;
+    readonly value: u128;
+  } & Struct;
+  readonly isApprove: boolean;
+  readonly asApprove: {
+    readonly spender: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly collectionId: u32;
+    readonly itemId: u32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isApproveFrom: boolean;
+  readonly asApproveFrom: {
+    readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly to: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly collectionId: u32;
+    readonly itemId: u32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isTransferFrom: boolean;
+  readonly asTransferFrom: {
+    readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly collectionId: u32;
+    readonly itemId: u32;
+    readonly value: u128;
+  } & Struct;
+  readonly isSetCollectionLimits: boolean;
+  readonly asSetCollectionLimits: {
+    readonly collectionId: u32;
+    readonly newLimit: UpDataStructsCollectionLimits;
+  } & Struct;
+  readonly isSetCollectionPermissions: boolean;
+  readonly asSetCollectionPermissions: {
+    readonly collectionId: u32;
+    readonly newPermission: UpDataStructsCollectionPermissions;
+  } & Struct;
+  readonly isRepartition: boolean;
+  readonly asRepartition: {
+    readonly collectionId: u32;
+    readonly tokenId: u32;
+    readonly amount: u128;
+  } & Struct;
+  readonly isSetAllowanceForAll: boolean;
+  readonly asSetAllowanceForAll: {
+    readonly collectionId: u32;
+    readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly approve: bool;
+  } & Struct;
+  readonly isForceRepairCollection: boolean;
+  readonly asForceRepairCollection: {
+    readonly collectionId: u32;
+  } & Struct;
+  readonly isForceRepairItem: boolean;
+  readonly asForceRepairItem: {
+    readonly collectionId: u32;
+    readonly itemId: u32;
+  } & Struct;
+  readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem';
+}
+
+/** @name PalletUniqueError */
+export interface PalletUniqueError extends Enum {
+  readonly isCollectionDecimalPointLimitExceeded: boolean;
+  readonly isEmptyArgument: boolean;
+  readonly isRepartitionCalledOnNonRefungibleCollection: boolean;
+  readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection';
+}
+
+/** @name PalletUtilityCall */
+export interface PalletUtilityCall extends Enum {
+  readonly isBatch: boolean;
+  readonly asBatch: {
+    readonly calls: Vec<Call>;
+  } & Struct;
+  readonly isAsDerivative: boolean;
+  readonly asAsDerivative: {
+    readonly index: u16;
+    readonly call: Call;
+  } & Struct;
+  readonly isBatchAll: boolean;
+  readonly asBatchAll: {
+    readonly calls: Vec<Call>;
+  } & Struct;
+  readonly isDispatchAs: boolean;
+  readonly asDispatchAs: {
+    readonly asOrigin: OpalRuntimeOriginCaller;
+    readonly call: Call;
+  } & Struct;
+  readonly isForceBatch: boolean;
+  readonly asForceBatch: {
+    readonly calls: Vec<Call>;
+  } & Struct;
+  readonly isWithWeight: boolean;
+  readonly asWithWeight: {
+    readonly call: Call;
+    readonly weight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight';
+}
+
+/** @name PalletUtilityError */
+export interface PalletUtilityError extends Enum {
+  readonly isTooManyCalls: boolean;
+  readonly type: 'TooManyCalls';
+}
+
+/** @name PalletUtilityEvent */
+export interface PalletUtilityEvent extends Enum {
+  readonly isBatchInterrupted: boolean;
+  readonly asBatchInterrupted: {
+    readonly index: u32;
+    readonly error: SpRuntimeDispatchError;
+  } & Struct;
+  readonly isBatchCompleted: boolean;
+  readonly isBatchCompletedWithErrors: boolean;
+  readonly isItemCompleted: boolean;
+  readonly isItemFailed: boolean;
+  readonly asItemFailed: {
+    readonly error: SpRuntimeDispatchError;
+  } & Struct;
+  readonly isDispatchedAs: boolean;
+  readonly asDispatchedAs: {
+    readonly result: Result<Null, SpRuntimeDispatchError>;
+  } & Struct;
+  readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs';
+}
+
+/** @name PalletXcmCall */
+export interface PalletXcmCall extends Enum {
+  readonly isSend: boolean;
+  readonly asSend: {
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly message: StagingXcmVersionedXcm;
+  } & Struct;
+  readonly isTeleportAssets: boolean;
+  readonly asTeleportAssets: {
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly beneficiary: StagingXcmVersionedMultiLocation;
+    readonly assets: StagingXcmVersionedMultiAssets;
+    readonly feeAssetItem: u32;
+  } & Struct;
+  readonly isReserveTransferAssets: boolean;
+  readonly asReserveTransferAssets: {
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly beneficiary: StagingXcmVersionedMultiLocation;
+    readonly assets: StagingXcmVersionedMultiAssets;
+    readonly feeAssetItem: u32;
+  } & Struct;
+  readonly isExecute: boolean;
+  readonly asExecute: {
+    readonly message: StagingXcmVersionedXcm;
+    readonly maxWeight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isForceXcmVersion: boolean;
+  readonly asForceXcmVersion: {
+    readonly location: StagingXcmV3MultiLocation;
+    readonly version: u32;
+  } & Struct;
+  readonly isForceDefaultXcmVersion: boolean;
+  readonly asForceDefaultXcmVersion: {
+    readonly maybeXcmVersion: Option<u32>;
+  } & Struct;
+  readonly isForceSubscribeVersionNotify: boolean;
+  readonly asForceSubscribeVersionNotify: {
+    readonly location: StagingXcmVersionedMultiLocation;
+  } & Struct;
+  readonly isForceUnsubscribeVersionNotify: boolean;
+  readonly asForceUnsubscribeVersionNotify: {
+    readonly location: StagingXcmVersionedMultiLocation;
+  } & Struct;
+  readonly isLimitedReserveTransferAssets: boolean;
+  readonly asLimitedReserveTransferAssets: {
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly beneficiary: StagingXcmVersionedMultiLocation;
+    readonly assets: StagingXcmVersionedMultiAssets;
+    readonly feeAssetItem: u32;
+    readonly weightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isLimitedTeleportAssets: boolean;
+  readonly asLimitedTeleportAssets: {
+    readonly dest: StagingXcmVersionedMultiLocation;
+    readonly beneficiary: StagingXcmVersionedMultiLocation;
+    readonly assets: StagingXcmVersionedMultiAssets;
+    readonly feeAssetItem: u32;
+    readonly weightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isForceSuspension: boolean;
+  readonly asForceSuspension: {
+    readonly suspended: bool;
+  } & Struct;
+  readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension';
+}
+
+/** @name PalletXcmError */
+export interface PalletXcmError extends Enum {
+  readonly isUnreachable: boolean;
+  readonly isSendFailure: boolean;
+  readonly isFiltered: boolean;
+  readonly isUnweighableMessage: boolean;
+  readonly isDestinationNotInvertible: boolean;
+  readonly isEmpty: boolean;
+  readonly isCannotReanchor: boolean;
+  readonly isTooManyAssets: boolean;
+  readonly isInvalidOrigin: boolean;
+  readonly isBadVersion: boolean;
+  readonly isBadLocation: boolean;
+  readonly isNoSubscription: boolean;
+  readonly isAlreadySubscribed: boolean;
+  readonly isInvalidAsset: boolean;
+  readonly isLowBalance: boolean;
+  readonly isTooManyLocks: boolean;
+  readonly isAccountNotSovereign: boolean;
+  readonly isFeesNotMet: boolean;
+  readonly isLockNotFound: boolean;
+  readonly isInUse: boolean;
+  readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse';
+}
+
+/** @name PalletXcmEvent */
+export interface PalletXcmEvent extends Enum {
+  readonly isAttempted: boolean;
+  readonly asAttempted: {
+    readonly outcome: StagingXcmV3TraitsOutcome;
+  } & Struct;
+  readonly isSent: boolean;
+  readonly asSent: {
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly destination: StagingXcmV3MultiLocation;
+    readonly message: StagingXcmV3Xcm;
+    readonly messageId: U8aFixed;
+  } & Struct;
+  readonly isUnexpectedResponse: boolean;
+  readonly asUnexpectedResponse: {
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly queryId: u64;
+  } & Struct;
+  readonly isResponseReady: boolean;
+  readonly asResponseReady: {
+    readonly queryId: u64;
+    readonly response: StagingXcmV3Response;
+  } & Struct;
+  readonly isNotified: boolean;
+  readonly asNotified: {
+    readonly queryId: u64;
+    readonly palletIndex: u8;
+    readonly callIndex: u8;
+  } & Struct;
+  readonly isNotifyOverweight: boolean;
+  readonly asNotifyOverweight: {
+    readonly queryId: u64;
+    readonly palletIndex: u8;
+    readonly callIndex: u8;
+    readonly actualWeight: SpWeightsWeightV2Weight;
+    readonly maxBudgetedWeight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isNotifyDispatchError: boolean;
+  readonly asNotifyDispatchError: {
+    readonly queryId: u64;
+    readonly palletIndex: u8;
+    readonly callIndex: u8;
+  } & Struct;
+  readonly isNotifyDecodeFailed: boolean;
+  readonly asNotifyDecodeFailed: {
+    readonly queryId: u64;
+    readonly palletIndex: u8;
+    readonly callIndex: u8;
+  } & Struct;
+  readonly isInvalidResponder: boolean;
+  readonly asInvalidResponder: {
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly queryId: u64;
+    readonly expectedLocation: Option<StagingXcmV3MultiLocation>;
+  } & Struct;
+  readonly isInvalidResponderVersion: boolean;
+  readonly asInvalidResponderVersion: {
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly queryId: u64;
+  } & Struct;
+  readonly isResponseTaken: boolean;
+  readonly asResponseTaken: {
+    readonly queryId: u64;
+  } & Struct;
+  readonly isAssetsTrapped: boolean;
+  readonly asAssetsTrapped: {
+    readonly hash_: H256;
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly assets: StagingXcmVersionedMultiAssets;
+  } & Struct;
+  readonly isVersionChangeNotified: boolean;
+  readonly asVersionChangeNotified: {
+    readonly destination: StagingXcmV3MultiLocation;
+    readonly result: u32;
+    readonly cost: StagingXcmV3MultiassetMultiAssets;
+    readonly messageId: U8aFixed;
+  } & Struct;
+  readonly isSupportedVersionChanged: boolean;
+  readonly asSupportedVersionChanged: {
+    readonly location: StagingXcmV3MultiLocation;
+    readonly version: u32;
+  } & Struct;
+  readonly isNotifyTargetSendFail: boolean;
+  readonly asNotifyTargetSendFail: {
+    readonly location: StagingXcmV3MultiLocation;
+    readonly queryId: u64;
+    readonly error: StagingXcmV3TraitsError;
+  } & Struct;
+  readonly isNotifyTargetMigrationFail: boolean;
+  readonly asNotifyTargetMigrationFail: {
+    readonly location: StagingXcmVersionedMultiLocation;
+    readonly queryId: u64;
+  } & Struct;
+  readonly isInvalidQuerierVersion: boolean;
+  readonly asInvalidQuerierVersion: {
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly queryId: u64;
+  } & Struct;
+  readonly isInvalidQuerier: boolean;
+  readonly asInvalidQuerier: {
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly queryId: u64;
+    readonly expectedQuerier: StagingXcmV3MultiLocation;
+    readonly maybeActualQuerier: Option<StagingXcmV3MultiLocation>;
+  } & Struct;
+  readonly isVersionNotifyStarted: boolean;
+  readonly asVersionNotifyStarted: {
+    readonly destination: StagingXcmV3MultiLocation;
+    readonly cost: StagingXcmV3MultiassetMultiAssets;
+    readonly messageId: U8aFixed;
+  } & Struct;
+  readonly isVersionNotifyRequested: boolean;
+  readonly asVersionNotifyRequested: {
+    readonly destination: StagingXcmV3MultiLocation;
+    readonly cost: StagingXcmV3MultiassetMultiAssets;
+    readonly messageId: U8aFixed;
+  } & Struct;
+  readonly isVersionNotifyUnrequested: boolean;
+  readonly asVersionNotifyUnrequested: {
+    readonly destination: StagingXcmV3MultiLocation;
+    readonly cost: StagingXcmV3MultiassetMultiAssets;
+    readonly messageId: U8aFixed;
+  } & Struct;
+  readonly isFeesPaid: boolean;
+  readonly asFeesPaid: {
+    readonly paying: StagingXcmV3MultiLocation;
+    readonly fees: StagingXcmV3MultiassetMultiAssets;
+  } & Struct;
+  readonly isAssetsClaimed: boolean;
+  readonly asAssetsClaimed: {
+    readonly hash_: H256;
+    readonly origin: StagingXcmV3MultiLocation;
+    readonly assets: StagingXcmVersionedMultiAssets;
+  } & Struct;
+  readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed';
+}
+
+/** @name PalletXcmOrigin */
+export interface PalletXcmOrigin extends Enum {
+  readonly isXcm: boolean;
+  readonly asXcm: StagingXcmV3MultiLocation;
+  readonly isResponse: boolean;
+  readonly asResponse: StagingXcmV3MultiLocation;
+  readonly type: 'Xcm' | 'Response';
+}
+
+/** @name PalletXcmQueryStatus */
+export interface PalletXcmQueryStatus extends Enum {
+  readonly isPending: boolean;
+  readonly asPending: {
+    readonly responder: StagingXcmVersionedMultiLocation;
+    readonly maybeMatchQuerier: Option<StagingXcmVersionedMultiLocation>;
+    readonly maybeNotify: Option<ITuple<[u8, u8]>>;
+    readonly timeout: u32;
+  } & Struct;
+  readonly isVersionNotifier: boolean;
+  readonly asVersionNotifier: {
+    readonly origin: StagingXcmVersionedMultiLocation;
+    readonly isActive: bool;
+  } & Struct;
+  readonly isReady: boolean;
+  readonly asReady: {
+    readonly response: StagingXcmVersionedResponse;
+    readonly at: u32;
+  } & Struct;
+  readonly type: 'Pending' | 'VersionNotifier' | 'Ready';
+}
+
+/** @name PalletXcmRemoteLockedFungibleRecord */
+export interface PalletXcmRemoteLockedFungibleRecord extends Struct {
+  readonly amount: u128;
+  readonly owner: StagingXcmVersionedMultiLocation;
+  readonly locker: StagingXcmVersionedMultiLocation;
+  readonly consumers: Vec<ITuple<[Null, u128]>>;
+}
+
+/** @name PalletXcmVersionMigrationStage */
+export interface PalletXcmVersionMigrationStage extends Enum {
+  readonly isMigrateSupportedVersion: boolean;
+  readonly isMigrateVersionNotifiers: boolean;
+  readonly isNotifyCurrentTargets: boolean;
+  readonly asNotifyCurrentTargets: Option<Bytes>;
+  readonly isMigrateAndNotifyOldTargets: boolean;
+  readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets';
+}
+
+/** @name ParachainInfoCall */
+export interface ParachainInfoCall extends Null {}
+
+/** @name PhantomTypeUpDataStructs */
+export interface PhantomTypeUpDataStructs extends Vec<ITuple<[UpDataStructsTokenData, UpDataStructsRpcCollection, UpPovEstimateRpcPovInfo]>> {}
+
+/** @name PolkadotCorePrimitivesInboundDownwardMessage */
+export interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct {
+  readonly sentAt: u32;
+  readonly msg: Bytes;
+}
+
+/** @name PolkadotCorePrimitivesInboundHrmpMessage */
+export interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct {
+  readonly sentAt: u32;
+  readonly data: Bytes;
+}
+
+/** @name PolkadotCorePrimitivesOutboundHrmpMessage */
+export interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct {
+  readonly recipient: u32;
+  readonly data: Bytes;
+}
+
+/** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat */
+export interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum {
+  readonly isConcatenatedVersionedXcm: boolean;
+  readonly isConcatenatedEncodedBlob: boolean;
+  readonly isSignals: boolean;
+  readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
+}
+
+/** @name PolkadotPrimitivesV5AbridgedHostConfiguration */
+export interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct {
+  readonly maxCodeSize: u32;
+  readonly maxHeadDataSize: u32;
+  readonly maxUpwardQueueCount: u32;
+  readonly maxUpwardQueueSize: u32;
+  readonly maxUpwardMessageSize: u32;
+  readonly maxUpwardMessageNumPerCandidate: u32;
+  readonly hrmpMaxMessageNumPerCandidate: u32;
+  readonly validationUpgradeCooldown: u32;
+  readonly validationUpgradeDelay: u32;
+  readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams;
+}
+
+/** @name PolkadotPrimitivesV5AbridgedHrmpChannel */
+export interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct {
+  readonly maxCapacity: u32;
+  readonly maxTotalSize: u32;
+  readonly maxMessageSize: u32;
+  readonly msgCount: u32;
+  readonly totalSize: u32;
+  readonly mqcHead: Option<H256>;
+}
+
+/** @name PolkadotPrimitivesV5PersistedValidationData */
+export interface PolkadotPrimitivesV5PersistedValidationData extends Struct {
+  readonly parentHead: Bytes;
+  readonly relayParentNumber: u32;
+  readonly relayParentStorageRoot: H256;
+  readonly maxPovSize: u32;
+}
+
+/** @name PolkadotPrimitivesV5UpgradeGoAhead */
+export interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum {
+  readonly isAbort: boolean;
+  readonly isGoAhead: boolean;
+  readonly type: 'Abort' | 'GoAhead';
+}
+
+/** @name PolkadotPrimitivesV5UpgradeRestriction */
+export interface PolkadotPrimitivesV5UpgradeRestriction extends Enum {
+  readonly isPresent: boolean;
+  readonly type: 'Present';
+}
+
+/** @name PolkadotPrimitivesVstagingAsyncBackingParams */
+export interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct {
+  readonly maxCandidateDepth: u32;
+  readonly allowedAncestryLen: u32;
+}
+
+/** @name SpArithmeticArithmeticError */
+export interface SpArithmeticArithmeticError extends Enum {
+  readonly isUnderflow: boolean;
+  readonly isOverflow: boolean;
+  readonly isDivisionByZero: boolean;
+  readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero';
+}
+
+/** @name SpConsensusAuraSr25519AppSr25519Public */
+export interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {}
+
+/** @name SpCoreCryptoKeyTypeId */
+export interface SpCoreCryptoKeyTypeId extends U8aFixed {}
+
+/** @name SpCoreEcdsaSignature */
+export interface SpCoreEcdsaSignature extends U8aFixed {}
+
+/** @name SpCoreEd25519Signature */
+export interface SpCoreEd25519Signature extends U8aFixed {}
+
+/** @name SpCoreSr25519Public */
+export interface SpCoreSr25519Public extends U8aFixed {}
+
+/** @name SpCoreSr25519Signature */
+export interface SpCoreSr25519Signature extends U8aFixed {}
+
+/** @name SpCoreVoid */
+export interface SpCoreVoid extends Null {}
+
+/** @name SpRuntimeDigest */
+export interface SpRuntimeDigest extends Struct {
+  readonly logs: Vec<SpRuntimeDigestDigestItem>;
+}
+
+/** @name SpRuntimeDigestDigestItem */
+export interface SpRuntimeDigestDigestItem extends Enum {
+  readonly isOther: boolean;
+  readonly asOther: Bytes;
+  readonly isConsensus: boolean;
+  readonly asConsensus: ITuple<[U8aFixed, Bytes]>;
+  readonly isSeal: boolean;
+  readonly asSeal: ITuple<[U8aFixed, Bytes]>;
+  readonly isPreRuntime: boolean;
+  readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>;
+  readonly isRuntimeEnvironmentUpdated: boolean;
+  readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated';
+}
+
+/** @name SpRuntimeDispatchError */
+export interface SpRuntimeDispatchError extends Enum {
+  readonly isOther: boolean;
+  readonly isCannotLookup: boolean;
+  readonly isBadOrigin: boolean;
+  readonly isModule: boolean;
+  readonly asModule: SpRuntimeModuleError;
+  readonly isConsumerRemaining: boolean;
+  readonly isNoProviders: boolean;
+  readonly isTooManyConsumers: boolean;
+  readonly isToken: boolean;
+  readonly asToken: SpRuntimeTokenError;
+  readonly isArithmetic: boolean;
+  readonly asArithmetic: SpArithmeticArithmeticError;
+  readonly isTransactional: boolean;
+  readonly asTransactional: SpRuntimeTransactionalError;
+  readonly isExhausted: boolean;
+  readonly isCorruption: boolean;
+  readonly isUnavailable: boolean;
+  readonly isRootNotAllowed: boolean;
+  readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed';
+}
+
+/** @name SpRuntimeModuleError */
+export interface SpRuntimeModuleError extends Struct {
+  readonly index: u8;
+  readonly error: U8aFixed;
+}
+
+/** @name SpRuntimeMultiSignature */
+export interface SpRuntimeMultiSignature extends Enum {
+  readonly isEd25519: boolean;
+  readonly asEd25519: SpCoreEd25519Signature;
+  readonly isSr25519: boolean;
+  readonly asSr25519: SpCoreSr25519Signature;
+  readonly isEcdsa: boolean;
+  readonly asEcdsa: SpCoreEcdsaSignature;
+  readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
+}
+
+/** @name SpRuntimeTokenError */
+export interface SpRuntimeTokenError extends Enum {
+  readonly isFundsUnavailable: boolean;
+  readonly isOnlyProvider: boolean;
+  readonly isBelowMinimum: boolean;
+  readonly isCannotCreate: boolean;
+  readonly isUnknownAsset: boolean;
+  readonly isFrozen: boolean;
+  readonly isUnsupported: boolean;
+  readonly isCannotCreateHold: boolean;
+  readonly isNotExpendable: boolean;
+  readonly isBlocked: boolean;
+  readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked';
+}
+
+/** @name SpRuntimeTransactionalError */
+export interface SpRuntimeTransactionalError extends Enum {
+  readonly isLimitReached: boolean;
+  readonly isNoLayer: boolean;
+  readonly type: 'LimitReached' | 'NoLayer';
+}
+
+/** @name SpRuntimeTransactionValidityInvalidTransaction */
+export interface SpRuntimeTransactionValidityInvalidTransaction extends Enum {
+  readonly isCall: boolean;
+  readonly isPayment: boolean;
+  readonly isFuture: boolean;
+  readonly isStale: boolean;
+  readonly isBadProof: boolean;
+  readonly isAncientBirthBlock: boolean;
+  readonly isExhaustsResources: boolean;
+  readonly isCustom: boolean;
+  readonly asCustom: u8;
+  readonly isBadMandatory: boolean;
+  readonly isMandatoryValidation: boolean;
+  readonly isBadSigner: boolean;
+  readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner';
+}
+
+/** @name SpRuntimeTransactionValidityTransactionValidityError */
+export interface SpRuntimeTransactionValidityTransactionValidityError extends Enum {
+  readonly isInvalid: boolean;
+  readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction;
+  readonly isUnknown: boolean;
+  readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction;
+  readonly type: 'Invalid' | 'Unknown';
+}
+
+/** @name SpRuntimeTransactionValidityUnknownTransaction */
+export interface SpRuntimeTransactionValidityUnknownTransaction extends Enum {
+  readonly isCannotLookup: boolean;
+  readonly isNoUnsignedValidator: boolean;
+  readonly isCustom: boolean;
+  readonly asCustom: u8;
+  readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom';
+}
+
+/** @name SpTrieStorageProof */
+export interface SpTrieStorageProof extends Struct {
+  readonly trieNodes: BTreeSet<Bytes>;
+}
+
+/** @name SpVersionRuntimeVersion */
+export interface SpVersionRuntimeVersion extends Struct {
+  readonly specName: Text;
+  readonly implName: Text;
+  readonly authoringVersion: u32;
+  readonly specVersion: u32;
+  readonly implVersion: u32;
+  readonly apis: Vec<ITuple<[U8aFixed, u32]>>;
+  readonly transactionVersion: u32;
+  readonly stateVersion: u8;
+}
+
+/** @name SpWeightsRuntimeDbWeight */
+export interface SpWeightsRuntimeDbWeight extends Struct {
+  readonly read: u64;
+  readonly write: u64;
+}
+
+/** @name SpWeightsWeightV2Weight */
+export interface SpWeightsWeightV2Weight extends Struct {
+  readonly refTime: Compact<u64>;
+  readonly proofSize: Compact<u64>;
+}
+
+/** @name StagingXcmDoubleEncoded */
+export interface StagingXcmDoubleEncoded extends Struct {
+  readonly encoded: Bytes;
+}
+
+/** @name StagingXcmV2BodyId */
+export interface StagingXcmV2BodyId extends Enum {
+  readonly isUnit: boolean;
+  readonly isNamed: boolean;
+  readonly asNamed: Bytes;
+  readonly isIndex: boolean;
+  readonly asIndex: Compact<u32>;
+  readonly isExecutive: boolean;
+  readonly isTechnical: boolean;
+  readonly isLegislative: boolean;
+  readonly isJudicial: boolean;
+  readonly isDefense: boolean;
+  readonly isAdministration: boolean;
+  readonly isTreasury: boolean;
+  readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
+}
+
+/** @name StagingXcmV2BodyPart */
+export interface StagingXcmV2BodyPart extends Enum {
+  readonly isVoice: boolean;
+  readonly isMembers: boolean;
+  readonly asMembers: {
+    readonly count: Compact<u32>;
+  } & Struct;
+  readonly isFraction: boolean;
+  readonly asFraction: {
+    readonly nom: Compact<u32>;
+    readonly denom: Compact<u32>;
+  } & Struct;
+  readonly isAtLeastProportion: boolean;
+  readonly asAtLeastProportion: {
+    readonly nom: Compact<u32>;
+    readonly denom: Compact<u32>;
+  } & Struct;
+  readonly isMoreThanProportion: boolean;
+  readonly asMoreThanProportion: {
+    readonly nom: Compact<u32>;
+    readonly denom: Compact<u32>;
+  } & Struct;
+  readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
+}
+
+/** @name StagingXcmV2Instruction */
+export interface StagingXcmV2Instruction extends Enum {
+  readonly isWithdrawAsset: boolean;
+  readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets;
+  readonly isReserveAssetDeposited: boolean;
+  readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets;
+  readonly isReceiveTeleportedAsset: boolean;
+  readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets;
+  readonly isQueryResponse: boolean;
+  readonly asQueryResponse: {
+    readonly queryId: Compact<u64>;
+    readonly response: StagingXcmV2Response;
+    readonly maxWeight: Compact<u64>;
+  } & Struct;
+  readonly isTransferAsset: boolean;
+  readonly asTransferAsset: {
+    readonly assets: StagingXcmV2MultiassetMultiAssets;
+    readonly beneficiary: StagingXcmV2MultiLocation;
+  } & Struct;
+  readonly isTransferReserveAsset: boolean;
+  readonly asTransferReserveAsset: {
+    readonly assets: StagingXcmV2MultiassetMultiAssets;
+    readonly dest: StagingXcmV2MultiLocation;
+    readonly xcm: StagingXcmV2Xcm;
+  } & Struct;
+  readonly isTransact: boolean;
+  readonly asTransact: {
+    readonly originType: StagingXcmV2OriginKind;
+    readonly requireWeightAtMost: Compact<u64>;
+    readonly call: StagingXcmDoubleEncoded;
+  } & Struct;
+  readonly isHrmpNewChannelOpenRequest: boolean;
+  readonly asHrmpNewChannelOpenRequest: {
+    readonly sender: Compact<u32>;
+    readonly maxMessageSize: Compact<u32>;
+    readonly maxCapacity: Compact<u32>;
+  } & Struct;
+  readonly isHrmpChannelAccepted: boolean;
+  readonly asHrmpChannelAccepted: {
+    readonly recipient: Compact<u32>;
+  } & Struct;
+  readonly isHrmpChannelClosing: boolean;
+  readonly asHrmpChannelClosing: {
+    readonly initiator: Compact<u32>;
+    readonly sender: Compact<u32>;
+    readonly recipient: Compact<u32>;
+  } & Struct;
+  readonly isClearOrigin: boolean;
+  readonly isDescendOrigin: boolean;
+  readonly asDescendOrigin: StagingXcmV2MultilocationJunctions;
+  readonly isReportError: boolean;
+  readonly asReportError: {
+    readonly queryId: Compact<u64>;
+    readonly dest: StagingXcmV2MultiLocation;
+    readonly maxResponseWeight: Compact<u64>;
+  } & Struct;
+  readonly isDepositAsset: boolean;
+  readonly asDepositAsset: {
+    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+    readonly maxAssets: Compact<u32>;
+    readonly beneficiary: StagingXcmV2MultiLocation;
+  } & Struct;
+  readonly isDepositReserveAsset: boolean;
+  readonly asDepositReserveAsset: {
+    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+    readonly maxAssets: Compact<u32>;
+    readonly dest: StagingXcmV2MultiLocation;
+    readonly xcm: StagingXcmV2Xcm;
+  } & Struct;
+  readonly isExchangeAsset: boolean;
+  readonly asExchangeAsset: {
+    readonly give: StagingXcmV2MultiassetMultiAssetFilter;
+    readonly receive: StagingXcmV2MultiassetMultiAssets;
+  } & Struct;
+  readonly isInitiateReserveWithdraw: boolean;
+  readonly asInitiateReserveWithdraw: {
+    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+    readonly reserve: StagingXcmV2MultiLocation;
+    readonly xcm: StagingXcmV2Xcm;
+  } & Struct;
+  readonly isInitiateTeleport: boolean;
+  readonly asInitiateTeleport: {
+    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+    readonly dest: StagingXcmV2MultiLocation;
+    readonly xcm: StagingXcmV2Xcm;
+  } & Struct;
+  readonly isQueryHolding: boolean;
+  readonly asQueryHolding: {
+    readonly queryId: Compact<u64>;
+    readonly dest: StagingXcmV2MultiLocation;
+    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+    readonly maxResponseWeight: Compact<u64>;
+  } & Struct;
+  readonly isBuyExecution: boolean;
+  readonly asBuyExecution: {
+    readonly fees: StagingXcmV2MultiAsset;
+    readonly weightLimit: StagingXcmV2WeightLimit;
+  } & Struct;
+  readonly isRefundSurplus: boolean;
+  readonly isSetErrorHandler: boolean;
+  readonly asSetErrorHandler: StagingXcmV2Xcm;
+  readonly isSetAppendix: boolean;
+  readonly asSetAppendix: StagingXcmV2Xcm;
+  readonly isClearError: boolean;
+  readonly isClaimAsset: boolean;
+  readonly asClaimAsset: {
+    readonly assets: StagingXcmV2MultiassetMultiAssets;
+    readonly ticket: StagingXcmV2MultiLocation;
+  } & Struct;
+  readonly isTrap: boolean;
+  readonly asTrap: Compact<u64>;
+  readonly isSubscribeVersion: boolean;
+  readonly asSubscribeVersion: {
+    readonly queryId: Compact<u64>;
+    readonly maxResponseWeight: Compact<u64>;
+  } & Struct;
+  readonly isUnsubscribeVersion: boolean;
+  readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion';
+}
+
+/** @name StagingXcmV2Junction */
+export interface StagingXcmV2Junction extends Enum {
+  readonly isParachain: boolean;
+  readonly asParachain: Compact<u32>;
+  readonly isAccountId32: boolean;
+  readonly asAccountId32: {
+    readonly network: StagingXcmV2NetworkId;
+    readonly id: U8aFixed;
+  } & Struct;
+  readonly isAccountIndex64: boolean;
+  readonly asAccountIndex64: {
+    readonly network: StagingXcmV2NetworkId;
+    readonly index: Compact<u64>;
+  } & Struct;
+  readonly isAccountKey20: boolean;
+  readonly asAccountKey20: {
+    readonly network: StagingXcmV2NetworkId;
+    readonly key: U8aFixed;
+  } & Struct;
+  readonly isPalletInstance: boolean;
+  readonly asPalletInstance: u8;
+  readonly isGeneralIndex: boolean;
+  readonly asGeneralIndex: Compact<u128>;
+  readonly isGeneralKey: boolean;
+  readonly asGeneralKey: Bytes;
+  readonly isOnlyChild: boolean;
+  readonly isPlurality: boolean;
+  readonly asPlurality: {
+    readonly id: StagingXcmV2BodyId;
+    readonly part: StagingXcmV2BodyPart;
+  } & Struct;
+  readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality';
+}
+
+/** @name StagingXcmV2MultiAsset */
+export interface StagingXcmV2MultiAsset extends Struct {
+  readonly id: StagingXcmV2MultiassetAssetId;
+  readonly fun: StagingXcmV2MultiassetFungibility;
+}
+
+/** @name StagingXcmV2MultiassetAssetId */
+export interface StagingXcmV2MultiassetAssetId extends Enum {
+  readonly isConcrete: boolean;
+  readonly asConcrete: StagingXcmV2MultiLocation;
+  readonly isAbstract: boolean;
+  readonly asAbstract: Bytes;
+  readonly type: 'Concrete' | 'Abstract';
+}
+
+/** @name StagingXcmV2MultiassetAssetInstance */
+export interface StagingXcmV2MultiassetAssetInstance extends Enum {
+  readonly isUndefined: boolean;
+  readonly isIndex: boolean;
+  readonly asIndex: Compact<u128>;
+  readonly isArray4: boolean;
+  readonly asArray4: U8aFixed;
+  readonly isArray8: boolean;
+  readonly asArray8: U8aFixed;
+  readonly isArray16: boolean;
+  readonly asArray16: U8aFixed;
+  readonly isArray32: boolean;
+  readonly asArray32: U8aFixed;
+  readonly isBlob: boolean;
+  readonly asBlob: Bytes;
+  readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob';
+}
+
+/** @name StagingXcmV2MultiassetFungibility */
+export interface StagingXcmV2MultiassetFungibility extends Enum {
+  readonly isFungible: boolean;
+  readonly asFungible: Compact<u128>;
+  readonly isNonFungible: boolean;
+  readonly asNonFungible: StagingXcmV2MultiassetAssetInstance;
+  readonly type: 'Fungible' | 'NonFungible';
+}
+
+/** @name StagingXcmV2MultiassetMultiAssetFilter */
+export interface StagingXcmV2MultiassetMultiAssetFilter extends Enum {
+  readonly isDefinite: boolean;
+  readonly asDefinite: StagingXcmV2MultiassetMultiAssets;
+  readonly isWild: boolean;
+  readonly asWild: StagingXcmV2MultiassetWildMultiAsset;
+  readonly type: 'Definite' | 'Wild';
+}
+
+/** @name StagingXcmV2MultiassetMultiAssets */
+export interface StagingXcmV2MultiassetMultiAssets extends Vec<StagingXcmV2MultiAsset> {}
+
+/** @name StagingXcmV2MultiassetWildFungibility */
+export interface StagingXcmV2MultiassetWildFungibility extends Enum {
+  readonly isFungible: boolean;
+  readonly isNonFungible: boolean;
+  readonly type: 'Fungible' | 'NonFungible';
+}
+
+/** @name StagingXcmV2MultiassetWildMultiAsset */
+export interface StagingXcmV2MultiassetWildMultiAsset extends Enum {
+  readonly isAll: boolean;
+  readonly isAllOf: boolean;
+  readonly asAllOf: {
+    readonly id: StagingXcmV2MultiassetAssetId;
+    readonly fun: StagingXcmV2MultiassetWildFungibility;
+  } & Struct;
+  readonly type: 'All' | 'AllOf';
+}
+
+/** @name StagingXcmV2MultiLocation */
+export interface StagingXcmV2MultiLocation extends Struct {
+  readonly parents: u8;
+  readonly interior: StagingXcmV2MultilocationJunctions;
+}
+
+/** @name StagingXcmV2MultilocationJunctions */
+export interface StagingXcmV2MultilocationJunctions extends Enum {
+  readonly isHere: boolean;
+  readonly isX1: boolean;
+  readonly asX1: StagingXcmV2Junction;
+  readonly isX2: boolean;
+  readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>;
+  readonly isX3: boolean;
+  readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+  readonly isX4: boolean;
+  readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+  readonly isX5: boolean;
+  readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+  readonly isX6: boolean;
+  readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+  readonly isX7: boolean;
+  readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+  readonly isX8: boolean;
+  readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+  readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
+}
+
+/** @name StagingXcmV2NetworkId */
+export interface StagingXcmV2NetworkId extends Enum {
+  readonly isAny: boolean;
+  readonly isNamed: boolean;
+  readonly asNamed: Bytes;
+  readonly isPolkadot: boolean;
+  readonly isKusama: boolean;
+  readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama';
+}
+
+/** @name StagingXcmV2OriginKind */
+export interface StagingXcmV2OriginKind extends Enum {
+  readonly isNative: boolean;
+  readonly isSovereignAccount: boolean;
+  readonly isSuperuser: boolean;
+  readonly isXcm: boolean;
+  readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm';
+}
+
+/** @name StagingXcmV2Response */
+export interface StagingXcmV2Response extends Enum {
+  readonly isNull: boolean;
+  readonly isAssets: boolean;
+  readonly asAssets: StagingXcmV2MultiassetMultiAssets;
+  readonly isExecutionResult: boolean;
+  readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV2TraitsError]>>;
+  readonly isVersion: boolean;
+  readonly asVersion: u32;
+  readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version';
+}
+
+/** @name StagingXcmV2TraitsError */
+export interface StagingXcmV2TraitsError extends Enum {
+  readonly isOverflow: boolean;
+  readonly isUnimplemented: boolean;
+  readonly isUntrustedReserveLocation: boolean;
+  readonly isUntrustedTeleportLocation: boolean;
+  readonly isMultiLocationFull: boolean;
+  readonly isMultiLocationNotInvertible: boolean;
+  readonly isBadOrigin: boolean;
+  readonly isInvalidLocation: boolean;
+  readonly isAssetNotFound: boolean;
+  readonly isFailedToTransactAsset: boolean;
+  readonly isNotWithdrawable: boolean;
+  readonly isLocationCannotHold: boolean;
+  readonly isExceedsMaxMessageSize: boolean;
+  readonly isDestinationUnsupported: boolean;
+  readonly isTransport: boolean;
+  readonly isUnroutable: boolean;
+  readonly isUnknownClaim: boolean;
+  readonly isFailedToDecode: boolean;
+  readonly isMaxWeightInvalid: boolean;
+  readonly isNotHoldingFees: boolean;
+  readonly isTooExpensive: boolean;
+  readonly isTrap: boolean;
+  readonly asTrap: u64;
+  readonly isUnhandledXcmVersion: boolean;
+  readonly isWeightLimitReached: boolean;
+  readonly asWeightLimitReached: u64;
+  readonly isBarrier: boolean;
+  readonly isWeightNotComputable: boolean;
+  readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable';
+}
+
+/** @name StagingXcmV2WeightLimit */
+export interface StagingXcmV2WeightLimit extends Enum {
+  readonly isUnlimited: boolean;
+  readonly isLimited: boolean;
+  readonly asLimited: Compact<u64>;
+  readonly type: 'Unlimited' | 'Limited';
+}
+
+/** @name StagingXcmV2Xcm */
+export interface StagingXcmV2Xcm extends Vec<StagingXcmV2Instruction> {}
+
+/** @name StagingXcmV3Instruction */
+export interface StagingXcmV3Instruction extends Enum {
+  readonly isWithdrawAsset: boolean;
+  readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets;
+  readonly isReserveAssetDeposited: boolean;
+  readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets;
+  readonly isReceiveTeleportedAsset: boolean;
+  readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets;
+  readonly isQueryResponse: boolean;
+  readonly asQueryResponse: {
+    readonly queryId: Compact<u64>;
+    readonly response: StagingXcmV3Response;
+    readonly maxWeight: SpWeightsWeightV2Weight;
+    readonly querier: Option<StagingXcmV3MultiLocation>;
+  } & Struct;
+  readonly isTransferAsset: boolean;
+  readonly asTransferAsset: {
+    readonly assets: StagingXcmV3MultiassetMultiAssets;
+    readonly beneficiary: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly isTransferReserveAsset: boolean;
+  readonly asTransferReserveAsset: {
+    readonly assets: StagingXcmV3MultiassetMultiAssets;
+    readonly dest: StagingXcmV3MultiLocation;
+    readonly xcm: StagingXcmV3Xcm;
+  } & Struct;
+  readonly isTransact: boolean;
+  readonly asTransact: {
+    readonly originKind: StagingXcmV2OriginKind;
+    readonly requireWeightAtMost: SpWeightsWeightV2Weight;
+    readonly call: StagingXcmDoubleEncoded;
+  } & Struct;
+  readonly isHrmpNewChannelOpenRequest: boolean;
+  readonly asHrmpNewChannelOpenRequest: {
+    readonly sender: Compact<u32>;
+    readonly maxMessageSize: Compact<u32>;
+    readonly maxCapacity: Compact<u32>;
+  } & Struct;
+  readonly isHrmpChannelAccepted: boolean;
+  readonly asHrmpChannelAccepted: {
+    readonly recipient: Compact<u32>;
+  } & Struct;
+  readonly isHrmpChannelClosing: boolean;
+  readonly asHrmpChannelClosing: {
+    readonly initiator: Compact<u32>;
+    readonly sender: Compact<u32>;
+    readonly recipient: Compact<u32>;
+  } & Struct;
+  readonly isClearOrigin: boolean;
+  readonly isDescendOrigin: boolean;
+  readonly asDescendOrigin: StagingXcmV3Junctions;
+  readonly isReportError: boolean;
+  readonly asReportError: StagingXcmV3QueryResponseInfo;
+  readonly isDepositAsset: boolean;
+  readonly asDepositAsset: {
+    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+    readonly beneficiary: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly isDepositReserveAsset: boolean;
+  readonly asDepositReserveAsset: {
+    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+    readonly dest: StagingXcmV3MultiLocation;
+    readonly xcm: StagingXcmV3Xcm;
+  } & Struct;
+  readonly isExchangeAsset: boolean;
+  readonly asExchangeAsset: {
+    readonly give: StagingXcmV3MultiassetMultiAssetFilter;
+    readonly want: StagingXcmV3MultiassetMultiAssets;
+    readonly maximal: bool;
+  } & Struct;
+  readonly isInitiateReserveWithdraw: boolean;
+  readonly asInitiateReserveWithdraw: {
+    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+    readonly reserve: StagingXcmV3MultiLocation;
+    readonly xcm: StagingXcmV3Xcm;
+  } & Struct;
+  readonly isInitiateTeleport: boolean;
+  readonly asInitiateTeleport: {
+    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+    readonly dest: StagingXcmV3MultiLocation;
+    readonly xcm: StagingXcmV3Xcm;
+  } & Struct;
+  readonly isReportHolding: boolean;
+  readonly asReportHolding: {
+    readonly responseInfo: StagingXcmV3QueryResponseInfo;
+    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+  } & Struct;
+  readonly isBuyExecution: boolean;
+  readonly asBuyExecution: {
+    readonly fees: StagingXcmV3MultiAsset;
+    readonly weightLimit: StagingXcmV3WeightLimit;
+  } & Struct;
+  readonly isRefundSurplus: boolean;
+  readonly isSetErrorHandler: boolean;
+  readonly asSetErrorHandler: StagingXcmV3Xcm;
+  readonly isSetAppendix: boolean;
+  readonly asSetAppendix: StagingXcmV3Xcm;
+  readonly isClearError: boolean;
+  readonly isClaimAsset: boolean;
+  readonly asClaimAsset: {
+    readonly assets: StagingXcmV3MultiassetMultiAssets;
+    readonly ticket: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly isTrap: boolean;
+  readonly asTrap: Compact<u64>;
+  readonly isSubscribeVersion: boolean;
+  readonly asSubscribeVersion: {
+    readonly queryId: Compact<u64>;
+    readonly maxResponseWeight: SpWeightsWeightV2Weight;
+  } & Struct;
+  readonly isUnsubscribeVersion: boolean;
+  readonly isBurnAsset: boolean;
+  readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets;
+  readonly isExpectAsset: boolean;
+  readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets;
+  readonly isExpectOrigin: boolean;
+  readonly asExpectOrigin: Option<StagingXcmV3MultiLocation>;
+  readonly isExpectError: boolean;
+  readonly asExpectError: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
+  readonly isExpectTransactStatus: boolean;
+  readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode;
+  readonly isQueryPallet: boolean;
+  readonly asQueryPallet: {
+    readonly moduleName: Bytes;
+    readonly responseInfo: StagingXcmV3QueryResponseInfo;
+  } & Struct;
+  readonly isExpectPallet: boolean;
+  readonly asExpectPallet: {
+    readonly index: Compact<u32>;
+    readonly name: Bytes;
+    readonly moduleName: Bytes;
+    readonly crateMajor: Compact<u32>;
+    readonly minCrateMinor: Compact<u32>;
+  } & Struct;
+  readonly isReportTransactStatus: boolean;
+  readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo;
+  readonly isClearTransactStatus: boolean;
+  readonly isUniversalOrigin: boolean;
+  readonly asUniversalOrigin: StagingXcmV3Junction;
+  readonly isExportMessage: boolean;
+  readonly asExportMessage: {
+    readonly network: StagingXcmV3JunctionNetworkId;
+    readonly destination: StagingXcmV3Junctions;
+    readonly xcm: StagingXcmV3Xcm;
+  } & Struct;
+  readonly isLockAsset: boolean;
+  readonly asLockAsset: {
+    readonly asset: StagingXcmV3MultiAsset;
+    readonly unlocker: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly isUnlockAsset: boolean;
+  readonly asUnlockAsset: {
+    readonly asset: StagingXcmV3MultiAsset;
+    readonly target: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly isNoteUnlockable: boolean;
+  readonly asNoteUnlockable: {
+    readonly asset: StagingXcmV3MultiAsset;
+    readonly owner: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly isRequestUnlock: boolean;
+  readonly asRequestUnlock: {
+    readonly asset: StagingXcmV3MultiAsset;
+    readonly locker: StagingXcmV3MultiLocation;
+  } & Struct;
+  readonly isSetFeesMode: boolean;
+  readonly asSetFeesMode: {
+    readonly jitWithdraw: bool;
+  } & Struct;
+  readonly isSetTopic: boolean;
+  readonly asSetTopic: U8aFixed;
+  readonly isClearTopic: boolean;
+  readonly isAliasOrigin: boolean;
+  readonly asAliasOrigin: StagingXcmV3MultiLocation;
+  readonly isUnpaidExecution: boolean;
+  readonly asUnpaidExecution: {
+    readonly weightLimit: StagingXcmV3WeightLimit;
+    readonly checkOrigin: Option<StagingXcmV3MultiLocation>;
+  } & Struct;
+  readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution';
+}
+
+/** @name StagingXcmV3Junction */
+export interface StagingXcmV3Junction extends Enum {
+  readonly isParachain: boolean;
+  readonly asParachain: Compact<u32>;
+  readonly isAccountId32: boolean;
+  readonly asAccountId32: {
+    readonly network: Option<StagingXcmV3JunctionNetworkId>;
+    readonly id: U8aFixed;
+  } & Struct;
+  readonly isAccountIndex64: boolean;
+  readonly asAccountIndex64: {
+    readonly network: Option<StagingXcmV3JunctionNetworkId>;
+    readonly index: Compact<u64>;
+  } & Struct;
+  readonly isAccountKey20: boolean;
+  readonly asAccountKey20: {
+    readonly network: Option<StagingXcmV3JunctionNetworkId>;
+    readonly key: U8aFixed;
+  } & Struct;
+  readonly isPalletInstance: boolean;
+  readonly asPalletInstance: u8;
+  readonly isGeneralIndex: boolean;
+  readonly asGeneralIndex: Compact<u128>;
+  readonly isGeneralKey: boolean;
+  readonly asGeneralKey: {
+    readonly length: u8;
+    readonly data: U8aFixed;
+  } & Struct;
+  readonly isOnlyChild: boolean;
+  readonly isPlurality: boolean;
+  readonly asPlurality: {
+    readonly id: StagingXcmV3JunctionBodyId;
+    readonly part: StagingXcmV3JunctionBodyPart;
+  } & Struct;
+  readonly isGlobalConsensus: boolean;
+  readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId;
+  readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus';
+}
+
+/** @name StagingXcmV3JunctionBodyId */
+export interface StagingXcmV3JunctionBodyId extends Enum {
+  readonly isUnit: boolean;
+  readonly isMoniker: boolean;
+  readonly asMoniker: U8aFixed;
+  readonly isIndex: boolean;
+  readonly asIndex: Compact<u32>;
+  readonly isExecutive: boolean;
+  readonly isTechnical: boolean;
+  readonly isLegislative: boolean;
+  readonly isJudicial: boolean;
+  readonly isDefense: boolean;
+  readonly isAdministration: boolean;
+  readonly isTreasury: boolean;
+  readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
+}
+
+/** @name StagingXcmV3JunctionBodyPart */
+export interface StagingXcmV3JunctionBodyPart extends Enum {
+  readonly isVoice: boolean;
+  readonly isMembers: boolean;
+  readonly asMembers: {
+    readonly count: Compact<u32>;
+  } & Struct;
+  readonly isFraction: boolean;
+  readonly asFraction: {
+    readonly nom: Compact<u32>;
+    readonly denom: Compact<u32>;
+  } & Struct;
+  readonly isAtLeastProportion: boolean;
+  readonly asAtLeastProportion: {
+    readonly nom: Compact<u32>;
+    readonly denom: Compact<u32>;
+  } & Struct;
+  readonly isMoreThanProportion: boolean;
+  readonly asMoreThanProportion: {
+    readonly nom: Compact<u32>;
+    readonly denom: Compact<u32>;
+  } & Struct;
+  readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
+}
+
+/** @name StagingXcmV3JunctionNetworkId */
+export interface StagingXcmV3JunctionNetworkId extends Enum {
+  readonly isByGenesis: boolean;
+  readonly asByGenesis: U8aFixed;
+  readonly isByFork: boolean;
+  readonly asByFork: {
+    readonly blockNumber: u64;
+    readonly blockHash: U8aFixed;
+  } & Struct;
+  readonly isPolkadot: boolean;
+  readonly isKusama: boolean;
+  readonly isWestend: boolean;
+  readonly isRococo: boolean;
+  readonly isWococo: boolean;
+  readonly isEthereum: boolean;
+  readonly asEthereum: {
+    readonly chainId: Compact<u64>;
+  } & Struct;
+  readonly isBitcoinCore: boolean;
+  readonly isBitcoinCash: boolean;
+  readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash';
+}
+
+/** @name StagingXcmV3Junctions */
+export interface StagingXcmV3Junctions extends Enum {
+  readonly isHere: boolean;
+  readonly isX1: boolean;
+  readonly asX1: StagingXcmV3Junction;
+  readonly isX2: boolean;
+  readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>;
+  readonly isX3: boolean;
+  readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+  readonly isX4: boolean;
+  readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+  readonly isX5: boolean;
+  readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+  readonly isX6: boolean;
+  readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+  readonly isX7: boolean;
+  readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+  readonly isX8: boolean;
+  readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+  readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
+}
+
+/** @name StagingXcmV3MaybeErrorCode */
+export interface StagingXcmV3MaybeErrorCode extends Enum {
+  readonly isSuccess: boolean;
+  readonly isError: boolean;
+  readonly asError: Bytes;
+  readonly isTruncatedError: boolean;
+  readonly asTruncatedError: Bytes;
+  readonly type: 'Success' | 'Error' | 'TruncatedError';
+}
+
+/** @name StagingXcmV3MultiAsset */
+export interface StagingXcmV3MultiAsset extends Struct {
+  readonly id: StagingXcmV3MultiassetAssetId;
+  readonly fun: StagingXcmV3MultiassetFungibility;
+}
+
+/** @name StagingXcmV3MultiassetAssetId */
+export interface StagingXcmV3MultiassetAssetId extends Enum {
+  readonly isConcrete: boolean;
+  readonly asConcrete: StagingXcmV3MultiLocation;
+  readonly isAbstract: boolean;
+  readonly asAbstract: U8aFixed;
+  readonly type: 'Concrete' | 'Abstract';
+}
+
+/** @name StagingXcmV3MultiassetAssetInstance */
+export interface StagingXcmV3MultiassetAssetInstance extends Enum {
+  readonly isUndefined: boolean;
+  readonly isIndex: boolean;
+  readonly asIndex: Compact<u128>;
+  readonly isArray4: boolean;
+  readonly asArray4: U8aFixed;
+  readonly isArray8: boolean;
+  readonly asArray8: U8aFixed;
+  readonly isArray16: boolean;
+  readonly asArray16: U8aFixed;
+  readonly isArray32: boolean;
+  readonly asArray32: U8aFixed;
+  readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32';
+}
+
+/** @name StagingXcmV3MultiassetFungibility */
+export interface StagingXcmV3MultiassetFungibility extends Enum {
+  readonly isFungible: boolean;
+  readonly asFungible: Compact<u128>;
+  readonly isNonFungible: boolean;
+  readonly asNonFungible: StagingXcmV3MultiassetAssetInstance;
+  readonly type: 'Fungible' | 'NonFungible';
+}
+
+/** @name StagingXcmV3MultiassetMultiAssetFilter */
+export interface StagingXcmV3MultiassetMultiAssetFilter extends Enum {
+  readonly isDefinite: boolean;
+  readonly asDefinite: StagingXcmV3MultiassetMultiAssets;
+  readonly isWild: boolean;
+  readonly asWild: StagingXcmV3MultiassetWildMultiAsset;
+  readonly type: 'Definite' | 'Wild';
+}
+
+/** @name StagingXcmV3MultiassetMultiAssets */
+export interface StagingXcmV3MultiassetMultiAssets extends Vec<StagingXcmV3MultiAsset> {}
+
+/** @name StagingXcmV3MultiassetWildFungibility */
+export interface StagingXcmV3MultiassetWildFungibility extends Enum {
+  readonly isFungible: boolean;
+  readonly isNonFungible: boolean;
+  readonly type: 'Fungible' | 'NonFungible';
+}
+
+/** @name StagingXcmV3MultiassetWildMultiAsset */
+export interface StagingXcmV3MultiassetWildMultiAsset extends Enum {
+  readonly isAll: boolean;
+  readonly isAllOf: boolean;
+  readonly asAllOf: {
+    readonly id: StagingXcmV3MultiassetAssetId;
+    readonly fun: StagingXcmV3MultiassetWildFungibility;
+  } & Struct;
+  readonly isAllCounted: boolean;
+  readonly asAllCounted: Compact<u32>;
+  readonly isAllOfCounted: boolean;
+  readonly asAllOfCounted: {
+    readonly id: StagingXcmV3MultiassetAssetId;
+    readonly fun: StagingXcmV3MultiassetWildFungibility;
+    readonly count: Compact<u32>;
+  } & Struct;
+  readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted';
+}
+
+/** @name StagingXcmV3MultiLocation */
+export interface StagingXcmV3MultiLocation extends Struct {
+  readonly parents: u8;
+  readonly interior: StagingXcmV3Junctions;
+}
+
+/** @name StagingXcmV3PalletInfo */
+export interface StagingXcmV3PalletInfo extends Struct {
+  readonly index: Compact<u32>;
+  readonly name: Bytes;
+  readonly moduleName: Bytes;
+  readonly major: Compact<u32>;
+  readonly minor: Compact<u32>;
+  readonly patch: Compact<u32>;
+}
+
+/** @name StagingXcmV3QueryResponseInfo */
+export interface StagingXcmV3QueryResponseInfo extends Struct {
+  readonly destination: StagingXcmV3MultiLocation;
+  readonly queryId: Compact<u64>;
+  readonly maxWeight: SpWeightsWeightV2Weight;
+}
+
+/** @name StagingXcmV3Response */
+export interface StagingXcmV3Response extends Enum {
+  readonly isNull: boolean;
+  readonly isAssets: boolean;
+  readonly asAssets: StagingXcmV3MultiassetMultiAssets;
+  readonly isExecutionResult: boolean;
+  readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
+  readonly isVersion: boolean;
+  readonly asVersion: u32;
+  readonly isPalletsInfo: boolean;
+  readonly asPalletsInfo: Vec<StagingXcmV3PalletInfo>;
+  readonly isDispatchResult: boolean;
+  readonly asDispatchResult: StagingXcmV3MaybeErrorCode;
+  readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult';
+}
+
+/** @name StagingXcmV3TraitsError */
+export interface StagingXcmV3TraitsError extends Enum {
+  readonly isOverflow: boolean;
+  readonly isUnimplemented: boolean;
+  readonly isUntrustedReserveLocation: boolean;
+  readonly isUntrustedTeleportLocation: boolean;
+  readonly isLocationFull: boolean;
+  readonly isLocationNotInvertible: boolean;
+  readonly isBadOrigin: boolean;
+  readonly isInvalidLocation: boolean;
+  readonly isAssetNotFound: boolean;
+  readonly isFailedToTransactAsset: boolean;
+  readonly isNotWithdrawable: boolean;
+  readonly isLocationCannotHold: boolean;
+  readonly isExceedsMaxMessageSize: boolean;
+  readonly isDestinationUnsupported: boolean;
+  readonly isTransport: boolean;
+  readonly isUnroutable: boolean;
+  readonly isUnknownClaim: boolean;
+  readonly isFailedToDecode: boolean;
+  readonly isMaxWeightInvalid: boolean;
+  readonly isNotHoldingFees: boolean;
+  readonly isTooExpensive: boolean;
+  readonly isTrap: boolean;
+  readonly asTrap: u64;
+  readonly isExpectationFalse: boolean;
+  readonly isPalletNotFound: boolean;
+  readonly isNameMismatch: boolean;
+  readonly isVersionIncompatible: boolean;
+  readonly isHoldingWouldOverflow: boolean;
+  readonly isExportError: boolean;
+  readonly isReanchorFailed: boolean;
+  readonly isNoDeal: boolean;
+  readonly isFeesNotMet: boolean;
+  readonly isLockError: boolean;
+  readonly isNoPermission: boolean;
+  readonly isUnanchored: boolean;
+  readonly isNotDepositable: boolean;
+  readonly isUnhandledXcmVersion: boolean;
+  readonly isWeightLimitReached: boolean;
+  readonly asWeightLimitReached: SpWeightsWeightV2Weight;
+  readonly isBarrier: boolean;
+  readonly isWeightNotComputable: boolean;
+  readonly isExceedsStackLimit: boolean;
+  readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit';
+}
+
+/** @name StagingXcmV3TraitsOutcome */
+export interface StagingXcmV3TraitsOutcome extends Enum {
+  readonly isComplete: boolean;
+  readonly asComplete: SpWeightsWeightV2Weight;
+  readonly isIncomplete: boolean;
+  readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>;
+  readonly isError: boolean;
+  readonly asError: StagingXcmV3TraitsError;
+  readonly type: 'Complete' | 'Incomplete' | 'Error';
+}
+
+/** @name StagingXcmV3WeightLimit */
+export interface StagingXcmV3WeightLimit extends Enum {
+  readonly isUnlimited: boolean;
+  readonly isLimited: boolean;
+  readonly asLimited: SpWeightsWeightV2Weight;
+  readonly type: 'Unlimited' | 'Limited';
+}
+
+/** @name StagingXcmV3Xcm */
+export interface StagingXcmV3Xcm extends Vec<StagingXcmV3Instruction> {}
+
+/** @name StagingXcmVersionedAssetId */
+export interface StagingXcmVersionedAssetId extends Enum {
+  readonly isV3: boolean;
+  readonly asV3: StagingXcmV3MultiassetAssetId;
+  readonly type: 'V3';
+}
+
+/** @name StagingXcmVersionedMultiAsset */
+export interface StagingXcmVersionedMultiAsset extends Enum {
+  readonly isV2: boolean;
+  readonly asV2: StagingXcmV2MultiAsset;
+  readonly isV3: boolean;
+  readonly asV3: StagingXcmV3MultiAsset;
+  readonly type: 'V2' | 'V3';
+}
+
+/** @name StagingXcmVersionedMultiAssets */
+export interface StagingXcmVersionedMultiAssets extends Enum {
+  readonly isV2: boolean;
+  readonly asV2: StagingXcmV2MultiassetMultiAssets;
+  readonly isV3: boolean;
+  readonly asV3: StagingXcmV3MultiassetMultiAssets;
+  readonly type: 'V2' | 'V3';
+}
+
+/** @name StagingXcmVersionedMultiLocation */
+export interface StagingXcmVersionedMultiLocation extends Enum {
+  readonly isV2: boolean;
+  readonly asV2: StagingXcmV2MultiLocation;
+  readonly isV3: boolean;
+  readonly asV3: StagingXcmV3MultiLocation;
+  readonly type: 'V2' | 'V3';
+}
+
+/** @name StagingXcmVersionedResponse */
+export interface StagingXcmVersionedResponse extends Enum {
+  readonly isV2: boolean;
+  readonly asV2: StagingXcmV2Response;
+  readonly isV3: boolean;
+  readonly asV3: StagingXcmV3Response;
+  readonly type: 'V2' | 'V3';
+}
+
+/** @name StagingXcmVersionedXcm */
+export interface StagingXcmVersionedXcm extends Enum {
+  readonly isV2: boolean;
+  readonly asV2: StagingXcmV2Xcm;
+  readonly isV3: boolean;
+  readonly asV3: StagingXcmV3Xcm;
+  readonly type: 'V2' | 'V3';
+}
+
+/** @name UpDataStructsAccessMode */
+export interface UpDataStructsAccessMode extends Enum {
+  readonly isNormal: boolean;
+  readonly isAllowList: boolean;
+  readonly type: 'Normal' | 'AllowList';
+}
+
+/** @name UpDataStructsCollection */
+export interface UpDataStructsCollection extends Struct {
+  readonly owner: AccountId32;
+  readonly mode: UpDataStructsCollectionMode;
+  readonly name: Vec<u16>;
+  readonly description: Vec<u16>;
+  readonly tokenPrefix: Bytes;
+  readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
+  readonly limits: UpDataStructsCollectionLimits;
+  readonly permissions: UpDataStructsCollectionPermissions;
+  readonly flags: U8aFixed;
+}
+
+/** @name UpDataStructsCollectionLimits */
+export interface UpDataStructsCollectionLimits extends Struct {
+  readonly accountTokenOwnershipLimit: Option<u32>;
+  readonly sponsoredDataSize: Option<u32>;
+  readonly sponsoredDataRateLimit: Option<UpDataStructsSponsoringRateLimit>;
+  readonly tokenLimit: Option<u32>;
+  readonly sponsorTransferTimeout: Option<u32>;
+  readonly sponsorApproveTimeout: Option<u32>;
+  readonly ownerCanTransfer: Option<bool>;
+  readonly ownerCanDestroy: Option<bool>;
+  readonly transfersEnabled: Option<bool>;
+}
+
+/** @name UpDataStructsCollectionMode */
+export interface UpDataStructsCollectionMode extends Enum {
+  readonly isNft: boolean;
+  readonly isFungible: boolean;
+  readonly asFungible: u8;
+  readonly isReFungible: boolean;
+  readonly type: 'Nft' | 'Fungible' | 'ReFungible';
+}
+
+/** @name UpDataStructsCollectionPermissions */
+export interface UpDataStructsCollectionPermissions extends Struct {
+  readonly access: Option<UpDataStructsAccessMode>;
+  readonly mintMode: Option<bool>;
+  readonly nesting: Option<UpDataStructsNestingPermissions>;
+}
+
+/** @name UpDataStructsCollectionStats */
+export interface UpDataStructsCollectionStats extends Struct {
+  readonly created: u32;
+  readonly destroyed: u32;
+  readonly alive: u32;
+}
+
+/** @name UpDataStructsCreateCollectionData */
+export interface UpDataStructsCreateCollectionData extends Struct {
+  readonly mode: UpDataStructsCollectionMode;
+  readonly access: Option<UpDataStructsAccessMode>;
+  readonly name: Vec<u16>;
+  readonly description: Vec<u16>;
+  readonly tokenPrefix: Bytes;
+  readonly limits: Option<UpDataStructsCollectionLimits>;
+  readonly permissions: Option<UpDataStructsCollectionPermissions>;
+  readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
+  readonly properties: Vec<UpDataStructsProperty>;
+  readonly adminList: Vec<PalletEvmAccountBasicCrossAccountIdRepr>;
+  readonly pendingSponsor: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
+  readonly flags: U8aFixed;
+}
+
+/** @name UpDataStructsCreateFungibleData */
+export interface UpDataStructsCreateFungibleData extends Struct {
+  readonly value: u128;
+}
+
+/** @name UpDataStructsCreateItemData */
+export interface UpDataStructsCreateItemData extends Enum {
+  readonly isNft: boolean;
+  readonly asNft: UpDataStructsCreateNftData;
+  readonly isFungible: boolean;
+  readonly asFungible: UpDataStructsCreateFungibleData;
+  readonly isReFungible: boolean;
+  readonly asReFungible: UpDataStructsCreateReFungibleData;
+  readonly type: 'Nft' | 'Fungible' | 'ReFungible';
+}
+
+/** @name UpDataStructsCreateItemExData */
+export interface UpDataStructsCreateItemExData extends Enum {
+  readonly isNft: boolean;
+  readonly asNft: Vec<UpDataStructsCreateNftExData>;
+  readonly isFungible: boolean;
+  readonly asFungible: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr,u128>;
+  readonly isRefungibleMultipleItems: boolean;
+  readonly asRefungibleMultipleItems: Vec<UpDataStructsCreateRefungibleExSingleOwner>;
+  readonly isRefungibleMultipleOwners: boolean;
+  readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners;
+  readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
+}
+
+/** @name UpDataStructsCreateNftData */
+export interface UpDataStructsCreateNftData extends Struct {
+  readonly properties: Vec<UpDataStructsProperty>;
+}
+
+/** @name UpDataStructsCreateNftExData */
+export interface UpDataStructsCreateNftExData extends Struct {
+  readonly properties: Vec<UpDataStructsProperty>;
+  readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+}
+
+/** @name UpDataStructsCreateReFungibleData */
+export interface UpDataStructsCreateReFungibleData extends Struct {
+  readonly pieces: u128;
+  readonly properties: Vec<UpDataStructsProperty>;
+}
+
+/** @name UpDataStructsCreateRefungibleExMultipleOwners */
+export interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct {
+  readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
+  readonly properties: Vec<UpDataStructsProperty>;
+}
+
+/** @name UpDataStructsCreateRefungibleExSingleOwner */
+export interface UpDataStructsCreateRefungibleExSingleOwner extends Struct {
+  readonly user: PalletEvmAccountBasicCrossAccountIdRepr;
+  readonly pieces: u128;
+  readonly properties: Vec<UpDataStructsProperty>;
+}
+
+/** @name UpDataStructsNestingPermissions */
+export interface UpDataStructsNestingPermissions extends Struct {
+  readonly tokenOwner: bool;
+  readonly collectionAdmin: bool;
+  readonly restricted: Option<UpDataStructsOwnerRestrictedSet>;
+}
+
+/** @name UpDataStructsOwnerRestrictedSet */
+export interface UpDataStructsOwnerRestrictedSet extends BTreeSet<u32> {}
+
+/** @name UpDataStructsProperties */
+export interface UpDataStructsProperties extends Struct {
+  readonly map: UpDataStructsPropertiesMapBoundedVec;
+  readonly consumedSpace: u32;
+  readonly reserved: u32;
+}
+
+/** @name UpDataStructsPropertiesMapBoundedVec */
+export interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap<Bytes, Bytes> {}
+
+/** @name UpDataStructsPropertiesMapPropertyPermission */
+export interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap<Bytes, UpDataStructsPropertyPermission> {}
+
+/** @name UpDataStructsProperty */
+export interface UpDataStructsProperty extends Struct {
+  readonly key: Bytes;
+  readonly value: Bytes;
+}
+
+/** @name UpDataStructsPropertyKeyPermission */
+export interface UpDataStructsPropertyKeyPermission extends Struct {
+  readonly key: Bytes;
+  readonly permission: UpDataStructsPropertyPermission;
+}
+
+/** @name UpDataStructsPropertyPermission */
+export interface UpDataStructsPropertyPermission extends Struct {
+  readonly mutable: bool;
+  readonly collectionAdmin: bool;
+  readonly tokenOwner: bool;
+}
+
+/** @name UpDataStructsPropertyScope */
+export interface UpDataStructsPropertyScope extends Enum {
+  readonly isNone: boolean;
+  readonly isRmrk: boolean;
+  readonly type: 'None' | 'Rmrk';
+}
+
+/** @name UpDataStructsRpcCollection */
+export interface UpDataStructsRpcCollection extends Struct {
+  readonly owner: AccountId32;
+  readonly mode: UpDataStructsCollectionMode;
+  readonly name: Vec<u16>;
+  readonly description: Vec<u16>;
+  readonly tokenPrefix: Bytes;
+  readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
+  readonly limits: UpDataStructsCollectionLimits;
+  readonly permissions: UpDataStructsCollectionPermissions;
+  readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
+  readonly properties: Vec<UpDataStructsProperty>;
+  readonly readOnly: bool;
+  readonly flags: UpDataStructsRpcCollectionFlags;
+}
+
+/** @name UpDataStructsRpcCollectionFlags */
+export interface UpDataStructsRpcCollectionFlags extends Struct {
+  readonly foreign: bool;
+  readonly erc721metadata: bool;
+}
+
+/** @name UpDataStructsSponsoringRateLimit */
+export interface UpDataStructsSponsoringRateLimit extends Enum {
+  readonly isSponsoringDisabled: boolean;
+  readonly isBlocks: boolean;
+  readonly asBlocks: u32;
+  readonly type: 'SponsoringDisabled' | 'Blocks';
+}
+
+/** @name UpDataStructsSponsorshipStateAccountId32 */
+export interface UpDataStructsSponsorshipStateAccountId32 extends Enum {
+  readonly isDisabled: boolean;
+  readonly isUnconfirmed: boolean;
+  readonly asUnconfirmed: AccountId32;
+  readonly isConfirmed: boolean;
+  readonly asConfirmed: AccountId32;
+  readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
+}
+
+/** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr */
+export interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum {
+  readonly isDisabled: boolean;
+  readonly isUnconfirmed: boolean;
+  readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
+  readonly isConfirmed: boolean;
+  readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
+  readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
+}
+
+/** @name UpDataStructsTokenChild */
+export interface UpDataStructsTokenChild extends Struct {
+  readonly token: u32;
+  readonly collection: u32;
+}
+
+/** @name UpDataStructsTokenData */
+export interface UpDataStructsTokenData extends Struct {
+  readonly properties: Vec<UpDataStructsProperty>;
+  readonly owner: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
+  readonly pieces: u128;
+}
+
+/** @name UpPovEstimateRpcPovInfo */
+export interface UpPovEstimateRpcPovInfo extends Struct {
+  readonly proofSize: u64;
+  readonly compactProofSize: u64;
+  readonly compressedProofSize: u64;
+  readonly results: Vec<Result<Result<Null, SpRuntimeDispatchError>, SpRuntimeTransactionValidityTransactionValidityError>>;
+  readonly keyValues: Vec<UpPovEstimateRpcTrieKeyValue>;
+}
+
+/** @name UpPovEstimateRpcTrieKeyValue */
+export interface UpPovEstimateRpcTrieKeyValue extends Struct {
+  readonly key: Bytes;
+  readonly value: Bytes;
+}
+
+export type PHANTOM_DEFAULT = 'default';
addedjs-packages/types/definitions.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/definitions.ts
@@ -0,0 +1,20 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+export {default as unique} from './unique/definitions.js';
+export {default as appPromotion} from './appPromotion/definitions.js';
+export {default as povinfo} from './povinfo/definitions.js';
+export {default as default} from './default/definitions.js';
addedjs-packages/types/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/index.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export * from './types.js';
addedjs-packages/types/lookup.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/lookup.ts
@@ -0,0 +1,5165 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+/* eslint-disable sort-keys */
+
+export default {
+  /**
+   * Lookup3: frame_system::AccountInfo<Nonce, pallet_balances::types::AccountData<Balance>>
+   **/
+  FrameSystemAccountInfo: {
+    nonce: 'u32',
+    consumers: 'u32',
+    providers: 'u32',
+    sufficients: 'u32',
+    data: 'PalletBalancesAccountData'
+  },
+  /**
+   * Lookup5: pallet_balances::types::AccountData<Balance>
+   **/
+  PalletBalancesAccountData: {
+    free: 'u128',
+    reserved: 'u128',
+    frozen: 'u128',
+    flags: 'u128'
+  },
+  /**
+   * Lookup8: frame_support::dispatch::PerDispatchClass<sp_weights::weight_v2::Weight>
+   **/
+  FrameSupportDispatchPerDispatchClassWeight: {
+    normal: 'SpWeightsWeightV2Weight',
+    operational: 'SpWeightsWeightV2Weight',
+    mandatory: 'SpWeightsWeightV2Weight'
+  },
+  /**
+   * Lookup9: sp_weights::weight_v2::Weight
+   **/
+  SpWeightsWeightV2Weight: {
+    refTime: 'Compact<u64>',
+    proofSize: 'Compact<u64>'
+  },
+  /**
+   * Lookup14: sp_runtime::generic::digest::Digest
+   **/
+  SpRuntimeDigest: {
+    logs: 'Vec<SpRuntimeDigestDigestItem>'
+  },
+  /**
+   * Lookup16: sp_runtime::generic::digest::DigestItem
+   **/
+  SpRuntimeDigestDigestItem: {
+    _enum: {
+      Other: 'Bytes',
+      __Unused1: 'Null',
+      __Unused2: 'Null',
+      __Unused3: 'Null',
+      Consensus: '([u8;4],Bytes)',
+      Seal: '([u8;4],Bytes)',
+      PreRuntime: '([u8;4],Bytes)',
+      __Unused7: 'Null',
+      RuntimeEnvironmentUpdated: 'Null'
+    }
+  },
+  /**
+   * Lookup19: frame_system::EventRecord<opal_runtime::RuntimeEvent, primitive_types::H256>
+   **/
+  FrameSystemEventRecord: {
+    phase: 'FrameSystemPhase',
+    event: 'Event',
+    topics: 'Vec<H256>'
+  },
+  /**
+   * Lookup21: frame_system::pallet::Event<T>
+   **/
+  FrameSystemEvent: {
+    _enum: {
+      ExtrinsicSuccess: {
+        dispatchInfo: 'FrameSupportDispatchDispatchInfo',
+      },
+      ExtrinsicFailed: {
+        dispatchError: 'SpRuntimeDispatchError',
+        dispatchInfo: 'FrameSupportDispatchDispatchInfo',
+      },
+      CodeUpdated: 'Null',
+      NewAccount: {
+        account: 'AccountId32',
+      },
+      KilledAccount: {
+        account: 'AccountId32',
+      },
+      Remarked: {
+        _alias: {
+          hash_: 'hash',
+        },
+        sender: 'AccountId32',
+        hash_: 'H256'
+      }
+    }
+  },
+  /**
+   * Lookup22: frame_support::dispatch::DispatchInfo
+   **/
+  FrameSupportDispatchDispatchInfo: {
+    weight: 'SpWeightsWeightV2Weight',
+    class: 'FrameSupportDispatchDispatchClass',
+    paysFee: 'FrameSupportDispatchPays'
+  },
+  /**
+   * Lookup23: frame_support::dispatch::DispatchClass
+   **/
+  FrameSupportDispatchDispatchClass: {
+    _enum: ['Normal', 'Operational', 'Mandatory']
+  },
+  /**
+   * Lookup24: frame_support::dispatch::Pays
+   **/
+  FrameSupportDispatchPays: {
+    _enum: ['Yes', 'No']
+  },
+  /**
+   * Lookup25: sp_runtime::DispatchError
+   **/
+  SpRuntimeDispatchError: {
+    _enum: {
+      Other: 'Null',
+      CannotLookup: 'Null',
+      BadOrigin: 'Null',
+      Module: 'SpRuntimeModuleError',
+      ConsumerRemaining: 'Null',
+      NoProviders: 'Null',
+      TooManyConsumers: 'Null',
+      Token: 'SpRuntimeTokenError',
+      Arithmetic: 'SpArithmeticArithmeticError',
+      Transactional: 'SpRuntimeTransactionalError',
+      Exhausted: 'Null',
+      Corruption: 'Null',
+      Unavailable: 'Null',
+      RootNotAllowed: 'Null'
+    }
+  },
+  /**
+   * Lookup26: sp_runtime::ModuleError
+   **/
+  SpRuntimeModuleError: {
+    index: 'u8',
+    error: '[u8;4]'
+  },
+  /**
+   * Lookup27: sp_runtime::TokenError
+   **/
+  SpRuntimeTokenError: {
+    _enum: ['FundsUnavailable', 'OnlyProvider', 'BelowMinimum', 'CannotCreate', 'UnknownAsset', 'Frozen', 'Unsupported', 'CannotCreateHold', 'NotExpendable', 'Blocked']
+  },
+  /**
+   * Lookup28: sp_arithmetic::ArithmeticError
+   **/
+  SpArithmeticArithmeticError: {
+    _enum: ['Underflow', 'Overflow', 'DivisionByZero']
+  },
+  /**
+   * Lookup29: sp_runtime::TransactionalError
+   **/
+  SpRuntimeTransactionalError: {
+    _enum: ['LimitReached', 'NoLayer']
+  },
+  /**
+   * Lookup30: pallet_state_trie_migration::pallet::Event<T>
+   **/
+  PalletStateTrieMigrationEvent: {
+    _enum: {
+      Migrated: {
+        top: 'u32',
+        child: 'u32',
+        compute: 'PalletStateTrieMigrationMigrationCompute',
+      },
+      Slashed: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      AutoMigrationFinished: 'Null',
+      Halted: {
+        error: 'PalletStateTrieMigrationError'
+      }
+    }
+  },
+  /**
+   * Lookup31: pallet_state_trie_migration::pallet::MigrationCompute
+   **/
+  PalletStateTrieMigrationMigrationCompute: {
+    _enum: ['Signed', 'Auto']
+  },
+  /**
+   * Lookup32: pallet_state_trie_migration::pallet::Error<T>
+   **/
+  PalletStateTrieMigrationError: {
+    _enum: ['MaxSignedLimits', 'KeyTooLong', 'NotEnoughFunds', 'BadWitness', 'SignedMigrationNotAllowed', 'BadChildRoot']
+  },
+  /**
+   * Lookup33: cumulus_pallet_parachain_system::pallet::Event<T>
+   **/
+  CumulusPalletParachainSystemEvent: {
+    _enum: {
+      ValidationFunctionStored: 'Null',
+      ValidationFunctionApplied: {
+        relayChainBlockNum: 'u32',
+      },
+      ValidationFunctionDiscarded: 'Null',
+      UpgradeAuthorized: {
+        codeHash: 'H256',
+      },
+      DownwardMessagesReceived: {
+        count: 'u32',
+      },
+      DownwardMessagesProcessed: {
+        weightUsed: 'SpWeightsWeightV2Weight',
+        dmqHead: 'H256',
+      },
+      UpwardMessageSent: {
+        messageHash: 'Option<[u8;32]>'
+      }
+    }
+  },
+  /**
+   * Lookup35: pallet_collator_selection::pallet::Event<T>
+   **/
+  PalletCollatorSelectionEvent: {
+    _enum: {
+      InvulnerableAdded: {
+        invulnerable: 'AccountId32',
+      },
+      InvulnerableRemoved: {
+        invulnerable: 'AccountId32',
+      },
+      LicenseObtained: {
+        accountId: 'AccountId32',
+        deposit: 'u128',
+      },
+      LicenseReleased: {
+        accountId: 'AccountId32',
+        depositReturned: 'u128',
+      },
+      CandidateAdded: {
+        accountId: 'AccountId32',
+      },
+      CandidateRemoved: {
+        accountId: 'AccountId32'
+      }
+    }
+  },
+  /**
+   * Lookup36: pallet_session::pallet::Event
+   **/
+  PalletSessionEvent: {
+    _enum: {
+      NewSession: {
+        sessionIndex: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup37: pallet_balances::pallet::Event<T, I>
+   **/
+  PalletBalancesEvent: {
+    _enum: {
+      Endowed: {
+        account: 'AccountId32',
+        freeBalance: 'u128',
+      },
+      DustLost: {
+        account: 'AccountId32',
+        amount: 'u128',
+      },
+      Transfer: {
+        from: 'AccountId32',
+        to: 'AccountId32',
+        amount: 'u128',
+      },
+      BalanceSet: {
+        who: 'AccountId32',
+        free: 'u128',
+      },
+      Reserved: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Unreserved: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      ReserveRepatriated: {
+        from: 'AccountId32',
+        to: 'AccountId32',
+        amount: 'u128',
+        destinationStatus: 'FrameSupportTokensMiscBalanceStatus',
+      },
+      Deposit: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Withdraw: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Slashed: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Minted: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Burned: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Suspended: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Restored: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Upgraded: {
+        who: 'AccountId32',
+      },
+      Issued: {
+        amount: 'u128',
+      },
+      Rescinded: {
+        amount: 'u128',
+      },
+      Locked: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Unlocked: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Frozen: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Thawed: {
+        who: 'AccountId32',
+        amount: 'u128'
+      }
+    }
+  },
+  /**
+   * Lookup38: frame_support::traits::tokens::misc::BalanceStatus
+   **/
+  FrameSupportTokensMiscBalanceStatus: {
+    _enum: ['Free', 'Reserved']
+  },
+  /**
+   * Lookup39: pallet_transaction_payment::pallet::Event<T>
+   **/
+  PalletTransactionPaymentEvent: {
+    _enum: {
+      TransactionFeePaid: {
+        who: 'AccountId32',
+        actualFee: 'u128',
+        tip: 'u128'
+      }
+    }
+  },
+  /**
+   * Lookup40: pallet_treasury::pallet::Event<T, I>
+   **/
+  PalletTreasuryEvent: {
+    _enum: {
+      Proposed: {
+        proposalIndex: 'u32',
+      },
+      Spending: {
+        budgetRemaining: 'u128',
+      },
+      Awarded: {
+        proposalIndex: 'u32',
+        award: 'u128',
+        account: 'AccountId32',
+      },
+      Rejected: {
+        proposalIndex: 'u32',
+        slashed: 'u128',
+      },
+      Burnt: {
+        burntFunds: 'u128',
+      },
+      Rollover: {
+        rolloverBalance: 'u128',
+      },
+      Deposit: {
+        value: 'u128',
+      },
+      SpendApproved: {
+        proposalIndex: 'u32',
+        amount: 'u128',
+        beneficiary: 'AccountId32',
+      },
+      UpdatedInactive: {
+        reactivated: 'u128',
+        deactivated: 'u128'
+      }
+    }
+  },
+  /**
+   * Lookup41: pallet_sudo::pallet::Event<T>
+   **/
+  PalletSudoEvent: {
+    _enum: {
+      Sudid: {
+        sudoResult: 'Result<Null, SpRuntimeDispatchError>',
+      },
+      KeyChanged: {
+        oldSudoer: 'Option<AccountId32>',
+      },
+      SudoAsDone: {
+        sudoResult: 'Result<Null, SpRuntimeDispatchError>'
+      }
+    }
+  },
+  /**
+   * Lookup45: orml_vesting::module::Event<T>
+   **/
+  OrmlVestingModuleEvent: {
+    _enum: {
+      VestingScheduleAdded: {
+        from: 'AccountId32',
+        to: 'AccountId32',
+        vestingSchedule: 'OrmlVestingVestingSchedule',
+      },
+      Claimed: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      VestingSchedulesUpdated: {
+        who: 'AccountId32'
+      }
+    }
+  },
+  /**
+   * Lookup46: orml_vesting::VestingSchedule<BlockNumber, Balance>
+   **/
+  OrmlVestingVestingSchedule: {
+    start: 'u32',
+    period: 'u32',
+    periodCount: 'u32',
+    perPeriod: 'Compact<u128>'
+  },
+  /**
+   * Lookup48: orml_xtokens::module::Event<T>
+   **/
+  OrmlXtokensModuleEvent: {
+    _enum: {
+      TransferredMultiAssets: {
+        sender: 'AccountId32',
+        assets: 'StagingXcmV3MultiassetMultiAssets',
+        fee: 'StagingXcmV3MultiAsset',
+        dest: 'StagingXcmV3MultiLocation'
+      }
+    }
+  },
+  /**
+   * Lookup49: staging_xcm::v3::multiasset::MultiAssets
+   **/
+  StagingXcmV3MultiassetMultiAssets: 'Vec<StagingXcmV3MultiAsset>',
+  /**
+   * Lookup51: staging_xcm::v3::multiasset::MultiAsset
+   **/
+  StagingXcmV3MultiAsset: {
+    id: 'StagingXcmV3MultiassetAssetId',
+    fun: 'StagingXcmV3MultiassetFungibility'
+  },
+  /**
+   * Lookup52: staging_xcm::v3::multiasset::AssetId
+   **/
+  StagingXcmV3MultiassetAssetId: {
+    _enum: {
+      Concrete: 'StagingXcmV3MultiLocation',
+      Abstract: '[u8;32]'
+    }
+  },
+  /**
+   * Lookup53: staging_xcm::v3::multilocation::MultiLocation
+   **/
+  StagingXcmV3MultiLocation: {
+    parents: 'u8',
+    interior: 'StagingXcmV3Junctions'
+  },
+  /**
+   * Lookup54: staging_xcm::v3::junctions::Junctions
+   **/
+  StagingXcmV3Junctions: {
+    _enum: {
+      Here: 'Null',
+      X1: 'StagingXcmV3Junction',
+      X2: '(StagingXcmV3Junction,StagingXcmV3Junction)',
+      X3: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
+      X4: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
+      X5: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
+      X6: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
+      X7: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
+      X8: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)'
+    }
+  },
+  /**
+   * Lookup55: staging_xcm::v3::junction::Junction
+   **/
+  StagingXcmV3Junction: {
+    _enum: {
+      Parachain: 'Compact<u32>',
+      AccountId32: {
+        network: 'Option<StagingXcmV3JunctionNetworkId>',
+        id: '[u8;32]',
+      },
+      AccountIndex64: {
+        network: 'Option<StagingXcmV3JunctionNetworkId>',
+        index: 'Compact<u64>',
+      },
+      AccountKey20: {
+        network: 'Option<StagingXcmV3JunctionNetworkId>',
+        key: '[u8;20]',
+      },
+      PalletInstance: 'u8',
+      GeneralIndex: 'Compact<u128>',
+      GeneralKey: {
+        length: 'u8',
+        data: '[u8;32]',
+      },
+      OnlyChild: 'Null',
+      Plurality: {
+        id: 'StagingXcmV3JunctionBodyId',
+        part: 'StagingXcmV3JunctionBodyPart',
+      },
+      GlobalConsensus: 'StagingXcmV3JunctionNetworkId'
+    }
+  },
+  /**
+   * Lookup58: staging_xcm::v3::junction::NetworkId
+   **/
+  StagingXcmV3JunctionNetworkId: {
+    _enum: {
+      ByGenesis: '[u8;32]',
+      ByFork: {
+        blockNumber: 'u64',
+        blockHash: '[u8;32]',
+      },
+      Polkadot: 'Null',
+      Kusama: 'Null',
+      Westend: 'Null',
+      Rococo: 'Null',
+      Wococo: 'Null',
+      Ethereum: {
+        chainId: 'Compact<u64>',
+      },
+      BitcoinCore: 'Null',
+      BitcoinCash: 'Null'
+    }
+  },
+  /**
+   * Lookup60: staging_xcm::v3::junction::BodyId
+   **/
+  StagingXcmV3JunctionBodyId: {
+    _enum: {
+      Unit: 'Null',
+      Moniker: '[u8;4]',
+      Index: 'Compact<u32>',
+      Executive: 'Null',
+      Technical: 'Null',
+      Legislative: 'Null',
+      Judicial: 'Null',
+      Defense: 'Null',
+      Administration: 'Null',
+      Treasury: 'Null'
+    }
+  },
+  /**
+   * Lookup61: staging_xcm::v3::junction::BodyPart
+   **/
+  StagingXcmV3JunctionBodyPart: {
+    _enum: {
+      Voice: 'Null',
+      Members: {
+        count: 'Compact<u32>',
+      },
+      Fraction: {
+        nom: 'Compact<u32>',
+        denom: 'Compact<u32>',
+      },
+      AtLeastProportion: {
+        nom: 'Compact<u32>',
+        denom: 'Compact<u32>',
+      },
+      MoreThanProportion: {
+        nom: 'Compact<u32>',
+        denom: 'Compact<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup62: staging_xcm::v3::multiasset::Fungibility
+   **/
+  StagingXcmV3MultiassetFungibility: {
+    _enum: {
+      Fungible: 'Compact<u128>',
+      NonFungible: 'StagingXcmV3MultiassetAssetInstance'
+    }
+  },
+  /**
+   * Lookup63: staging_xcm::v3::multiasset::AssetInstance
+   **/
+  StagingXcmV3MultiassetAssetInstance: {
+    _enum: {
+      Undefined: 'Null',
+      Index: 'Compact<u128>',
+      Array4: '[u8;4]',
+      Array8: '[u8;8]',
+      Array16: '[u8;16]',
+      Array32: '[u8;32]'
+    }
+  },
+  /**
+   * Lookup66: orml_tokens::module::Event<T>
+   **/
+  OrmlTokensModuleEvent: {
+    _enum: {
+      Endowed: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      DustLost: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Transfer: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        from: 'AccountId32',
+        to: 'AccountId32',
+        amount: 'u128',
+      },
+      Reserved: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Unreserved: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      ReserveRepatriated: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        from: 'AccountId32',
+        to: 'AccountId32',
+        amount: 'u128',
+        status: 'FrameSupportTokensMiscBalanceStatus',
+      },
+      BalanceSet: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        free: 'u128',
+        reserved: 'u128',
+      },
+      TotalIssuanceSet: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'u128',
+      },
+      Withdrawn: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Slashed: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        freeAmount: 'u128',
+        reservedAmount: 'u128',
+      },
+      Deposited: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      LockSet: {
+        lockId: '[u8;8]',
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      LockRemoved: {
+        lockId: '[u8;8]',
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+      },
+      Locked: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Unlocked: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      Issued: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'u128',
+      },
+      Rescinded: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'u128'
+      }
+    }
+  },
+  /**
+   * Lookup67: pallet_foreign_assets::AssetId
+   **/
+  PalletForeignAssetsAssetId: {
+    _enum: {
+      ForeignAssetId: 'u32',
+      NativeAssetId: 'PalletForeignAssetsNativeCurrency'
+    }
+  },
+  /**
+   * Lookup68: pallet_foreign_assets::NativeCurrency
+   **/
+  PalletForeignAssetsNativeCurrency: {
+    _enum: ['Here', 'Parent']
+  },
+  /**
+   * Lookup69: pallet_identity::pallet::Event<T>
+   **/
+  PalletIdentityEvent: {
+    _enum: {
+      IdentitySet: {
+        who: 'AccountId32',
+      },
+      IdentityCleared: {
+        who: 'AccountId32',
+        deposit: 'u128',
+      },
+      IdentityKilled: {
+        who: 'AccountId32',
+        deposit: 'u128',
+      },
+      IdentitiesInserted: {
+        amount: 'u32',
+      },
+      IdentitiesRemoved: {
+        amount: 'u32',
+      },
+      JudgementRequested: {
+        who: 'AccountId32',
+        registrarIndex: 'u32',
+      },
+      JudgementUnrequested: {
+        who: 'AccountId32',
+        registrarIndex: 'u32',
+      },
+      JudgementGiven: {
+        target: 'AccountId32',
+        registrarIndex: 'u32',
+      },
+      RegistrarAdded: {
+        registrarIndex: 'u32',
+      },
+      SubIdentityAdded: {
+        sub: 'AccountId32',
+        main: 'AccountId32',
+        deposit: 'u128',
+      },
+      SubIdentityRemoved: {
+        sub: 'AccountId32',
+        main: 'AccountId32',
+        deposit: 'u128',
+      },
+      SubIdentityRevoked: {
+        sub: 'AccountId32',
+        main: 'AccountId32',
+        deposit: 'u128',
+      },
+      SubIdentitiesInserted: {
+        amount: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup70: pallet_preimage::pallet::Event<T>
+   **/
+  PalletPreimageEvent: {
+    _enum: {
+      Noted: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+      },
+      Requested: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+      },
+      Cleared: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256'
+      }
+    }
+  },
+  /**
+   * Lookup71: pallet_democracy::pallet::Event<T>
+   **/
+  PalletDemocracyEvent: {
+    _enum: {
+      Proposed: {
+        proposalIndex: 'u32',
+        deposit: 'u128',
+      },
+      Tabled: {
+        proposalIndex: 'u32',
+        deposit: 'u128',
+      },
+      ExternalTabled: 'Null',
+      Started: {
+        refIndex: 'u32',
+        threshold: 'PalletDemocracyVoteThreshold',
+      },
+      Passed: {
+        refIndex: 'u32',
+      },
+      NotPassed: {
+        refIndex: 'u32',
+      },
+      Cancelled: {
+        refIndex: 'u32',
+      },
+      Delegated: {
+        who: 'AccountId32',
+        target: 'AccountId32',
+      },
+      Undelegated: {
+        account: 'AccountId32',
+      },
+      Vetoed: {
+        who: 'AccountId32',
+        proposalHash: 'H256',
+        until: 'u32',
+      },
+      Blacklisted: {
+        proposalHash: 'H256',
+      },
+      Voted: {
+        voter: 'AccountId32',
+        refIndex: 'u32',
+        vote: 'PalletDemocracyVoteAccountVote',
+      },
+      Seconded: {
+        seconder: 'AccountId32',
+        propIndex: 'u32',
+      },
+      ProposalCanceled: {
+        propIndex: 'u32',
+      },
+      MetadataSet: {
+        _alias: {
+          hash_: 'hash',
+        },
+        owner: 'PalletDemocracyMetadataOwner',
+        hash_: 'H256',
+      },
+      MetadataCleared: {
+        _alias: {
+          hash_: 'hash',
+        },
+        owner: 'PalletDemocracyMetadataOwner',
+        hash_: 'H256',
+      },
+      MetadataTransferred: {
+        _alias: {
+          hash_: 'hash',
+        },
+        prevOwner: 'PalletDemocracyMetadataOwner',
+        owner: 'PalletDemocracyMetadataOwner',
+        hash_: 'H256'
+      }
+    }
+  },
+  /**
+   * Lookup72: pallet_democracy::vote_threshold::VoteThreshold
+   **/
+  PalletDemocracyVoteThreshold: {
+    _enum: ['SuperMajorityApprove', 'SuperMajorityAgainst', 'SimpleMajority']
+  },
+  /**
+   * Lookup73: pallet_democracy::vote::AccountVote<Balance>
+   **/
+  PalletDemocracyVoteAccountVote: {
+    _enum: {
+      Standard: {
+        vote: 'Vote',
+        balance: 'u128',
+      },
+      Split: {
+        aye: 'u128',
+        nay: 'u128'
+      }
+    }
+  },
+  /**
+   * Lookup75: pallet_democracy::types::MetadataOwner
+   **/
+  PalletDemocracyMetadataOwner: {
+    _enum: {
+      External: 'Null',
+      Proposal: 'u32',
+      Referendum: 'u32'
+    }
+  },
+  /**
+   * Lookup76: pallet_collective::pallet::Event<T, I>
+   **/
+  PalletCollectiveEvent: {
+    _enum: {
+      Proposed: {
+        account: 'AccountId32',
+        proposalIndex: 'u32',
+        proposalHash: 'H256',
+        threshold: 'u32',
+      },
+      Voted: {
+        account: 'AccountId32',
+        proposalHash: 'H256',
+        voted: 'bool',
+        yes: 'u32',
+        no: 'u32',
+      },
+      Approved: {
+        proposalHash: 'H256',
+      },
+      Disapproved: {
+        proposalHash: 'H256',
+      },
+      Executed: {
+        proposalHash: 'H256',
+        result: 'Result<Null, SpRuntimeDispatchError>',
+      },
+      MemberExecuted: {
+        proposalHash: 'H256',
+        result: 'Result<Null, SpRuntimeDispatchError>',
+      },
+      Closed: {
+        proposalHash: 'H256',
+        yes: 'u32',
+        no: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup79: pallet_membership::pallet::Event<T, I>
+   **/
+  PalletMembershipEvent: {
+    _enum: ['MemberAdded', 'MemberRemoved', 'MembersSwapped', 'MembersReset', 'KeyChanged', 'Dummy']
+  },
+  /**
+   * Lookup81: pallet_ranked_collective::pallet::Event<T, I>
+   **/
+  PalletRankedCollectiveEvent: {
+    _enum: {
+      MemberAdded: {
+        who: 'AccountId32',
+      },
+      RankChanged: {
+        who: 'AccountId32',
+        rank: 'u16',
+      },
+      MemberRemoved: {
+        who: 'AccountId32',
+        rank: 'u16',
+      },
+      Voted: {
+        who: 'AccountId32',
+        poll: 'u32',
+        vote: 'PalletRankedCollectiveVoteRecord',
+        tally: 'PalletRankedCollectiveTally'
+      }
+    }
+  },
+  /**
+   * Lookup83: pallet_ranked_collective::VoteRecord
+   **/
+  PalletRankedCollectiveVoteRecord: {
+    _enum: {
+      Aye: 'u32',
+      Nay: 'u32'
+    }
+  },
+  /**
+   * Lookup84: pallet_ranked_collective::Tally<T, I, M>
+   **/
+  PalletRankedCollectiveTally: {
+    bareAyes: 'u32',
+    ayes: 'u32',
+    nays: 'u32'
+  },
+  /**
+   * Lookup85: pallet_referenda::pallet::Event<T, I>
+   **/
+  PalletReferendaEvent: {
+    _enum: {
+      Submitted: {
+        index: 'u32',
+        track: 'u16',
+        proposal: 'FrameSupportPreimagesBounded',
+      },
+      DecisionDepositPlaced: {
+        index: 'u32',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      DecisionDepositRefunded: {
+        index: 'u32',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      DepositSlashed: {
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      DecisionStarted: {
+        index: 'u32',
+        track: 'u16',
+        proposal: 'FrameSupportPreimagesBounded',
+        tally: 'PalletRankedCollectiveTally',
+      },
+      ConfirmStarted: {
+        index: 'u32',
+      },
+      ConfirmAborted: {
+        index: 'u32',
+      },
+      Confirmed: {
+        index: 'u32',
+        tally: 'PalletRankedCollectiveTally',
+      },
+      Approved: {
+        index: 'u32',
+      },
+      Rejected: {
+        index: 'u32',
+        tally: 'PalletRankedCollectiveTally',
+      },
+      TimedOut: {
+        index: 'u32',
+        tally: 'PalletRankedCollectiveTally',
+      },
+      Cancelled: {
+        index: 'u32',
+        tally: 'PalletRankedCollectiveTally',
+      },
+      Killed: {
+        index: 'u32',
+        tally: 'PalletRankedCollectiveTally',
+      },
+      SubmissionDepositRefunded: {
+        index: 'u32',
+        who: 'AccountId32',
+        amount: 'u128',
+      },
+      MetadataSet: {
+        _alias: {
+          hash_: 'hash',
+        },
+        index: 'u32',
+        hash_: 'H256',
+      },
+      MetadataCleared: {
+        _alias: {
+          hash_: 'hash',
+        },
+        index: 'u32',
+        hash_: 'H256'
+      }
+    }
+  },
+  /**
+   * Lookup86: frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>
+   **/
+  FrameSupportPreimagesBounded: {
+    _enum: {
+      Legacy: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+      },
+      Inline: 'Bytes',
+      Lookup: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+        len: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup88: frame_system::pallet::Call<T>
+   **/
+  FrameSystemCall: {
+    _enum: {
+      remark: {
+        remark: 'Bytes',
+      },
+      set_heap_pages: {
+        pages: 'u64',
+      },
+      set_code: {
+        code: 'Bytes',
+      },
+      set_code_without_checks: {
+        code: 'Bytes',
+      },
+      set_storage: {
+        items: 'Vec<(Bytes,Bytes)>',
+      },
+      kill_storage: {
+        _alias: {
+          keys_: 'keys',
+        },
+        keys_: 'Vec<Bytes>',
+      },
+      kill_prefix: {
+        prefix: 'Bytes',
+        subkeys: 'u32',
+      },
+      remark_with_event: {
+        remark: 'Bytes'
+      }
+    }
+  },
+  /**
+   * Lookup92: pallet_state_trie_migration::pallet::Call<T>
+   **/
+  PalletStateTrieMigrationCall: {
+    _enum: {
+      control_auto_migration: {
+        maybeConfig: 'Option<PalletStateTrieMigrationMigrationLimits>',
+      },
+      continue_migrate: {
+        limits: 'PalletStateTrieMigrationMigrationLimits',
+        realSizeUpper: 'u32',
+        witnessTask: 'PalletStateTrieMigrationMigrationTask',
+      },
+      migrate_custom_top: {
+        _alias: {
+          keys_: 'keys',
+        },
+        keys_: 'Vec<Bytes>',
+        witnessSize: 'u32',
+      },
+      migrate_custom_child: {
+        root: 'Bytes',
+        childKeys: 'Vec<Bytes>',
+        totalSize: 'u32',
+      },
+      set_signed_max_limits: {
+        limits: 'PalletStateTrieMigrationMigrationLimits',
+      },
+      force_set_progress: {
+        progressTop: 'PalletStateTrieMigrationProgress',
+        progressChild: 'PalletStateTrieMigrationProgress'
+      }
+    }
+  },
+  /**
+   * Lookup94: pallet_state_trie_migration::pallet::MigrationLimits
+   **/
+  PalletStateTrieMigrationMigrationLimits: {
+    _alias: {
+      size_: 'size'
+    },
+    size_: 'u32',
+    item: 'u32'
+  },
+  /**
+   * Lookup95: pallet_state_trie_migration::pallet::MigrationTask<T>
+   **/
+  PalletStateTrieMigrationMigrationTask: {
+    _alias: {
+      size_: 'size'
+    },
+    progressTop: 'PalletStateTrieMigrationProgress',
+    progressChild: 'PalletStateTrieMigrationProgress',
+    size_: 'u32',
+    topItems: 'u32',
+    childItems: 'u32'
+  },
+  /**
+   * Lookup96: pallet_state_trie_migration::pallet::Progress<MaxKeyLen>
+   **/
+  PalletStateTrieMigrationProgress: {
+    _enum: {
+      ToStart: 'Null',
+      LastKey: 'Bytes',
+      Complete: 'Null'
+    }
+  },
+  /**
+   * Lookup98: cumulus_pallet_parachain_system::pallet::Call<T>
+   **/
+  CumulusPalletParachainSystemCall: {
+    _enum: {
+      set_validation_data: {
+        data: 'CumulusPrimitivesParachainInherentParachainInherentData',
+      },
+      sudo_send_upward_message: {
+        message: 'Bytes',
+      },
+      authorize_upgrade: {
+        codeHash: 'H256',
+        checkVersion: 'bool',
+      },
+      enact_authorized_upgrade: {
+        code: 'Bytes'
+      }
+    }
+  },
+  /**
+   * Lookup99: cumulus_primitives_parachain_inherent::ParachainInherentData
+   **/
+  CumulusPrimitivesParachainInherentParachainInherentData: {
+    validationData: 'PolkadotPrimitivesV5PersistedValidationData',
+    relayChainState: 'SpTrieStorageProof',
+    downwardMessages: 'Vec<PolkadotCorePrimitivesInboundDownwardMessage>',
+    horizontalMessages: 'BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>'
+  },
+  /**
+   * Lookup100: polkadot_primitives::v5::PersistedValidationData<primitive_types::H256, N>
+   **/
+  PolkadotPrimitivesV5PersistedValidationData: {
+    parentHead: 'Bytes',
+    relayParentNumber: 'u32',
+    relayParentStorageRoot: 'H256',
+    maxPovSize: 'u32'
+  },
+  /**
+   * Lookup102: sp_trie::storage_proof::StorageProof
+   **/
+  SpTrieStorageProof: {
+    trieNodes: 'BTreeSet<Bytes>'
+  },
+  /**
+   * Lookup105: polkadot_core_primitives::InboundDownwardMessage<BlockNumber>
+   **/
+  PolkadotCorePrimitivesInboundDownwardMessage: {
+    sentAt: 'u32',
+    msg: 'Bytes'
+  },
+  /**
+   * Lookup109: polkadot_core_primitives::InboundHrmpMessage<BlockNumber>
+   **/
+  PolkadotCorePrimitivesInboundHrmpMessage: {
+    sentAt: 'u32',
+    data: 'Bytes'
+  },
+  /**
+   * Lookup112: parachain_info::pallet::Call<T>
+   **/
+  ParachainInfoCall: 'Null',
+  /**
+   * Lookup113: pallet_collator_selection::pallet::Call<T>
+   **/
+  PalletCollatorSelectionCall: {
+    _enum: {
+      add_invulnerable: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'AccountId32',
+      },
+      remove_invulnerable: {
+        who: 'AccountId32',
+      },
+      get_license: 'Null',
+      onboard: 'Null',
+      offboard: 'Null',
+      release_license: 'Null',
+      force_release_license: {
+        who: 'AccountId32'
+      }
+    }
+  },
+  /**
+   * Lookup114: pallet_session::pallet::Call<T>
+   **/
+  PalletSessionCall: {
+    _enum: {
+      set_keys: {
+        _alias: {
+          keys_: 'keys',
+        },
+        keys_: 'OpalRuntimeRuntimeCommonSessionKeys',
+        proof: 'Bytes',
+      },
+      purge_keys: 'Null'
+    }
+  },
+  /**
+   * Lookup115: opal_runtime::runtime_common::SessionKeys
+   **/
+  OpalRuntimeRuntimeCommonSessionKeys: {
+    aura: 'SpConsensusAuraSr25519AppSr25519Public'
+  },
+  /**
+   * Lookup116: sp_consensus_aura::sr25519::app_sr25519::Public
+   **/
+  SpConsensusAuraSr25519AppSr25519Public: 'SpCoreSr25519Public',
+  /**
+   * Lookup117: sp_core::sr25519::Public
+   **/
+  SpCoreSr25519Public: '[u8;32]',
+  /**
+   * Lookup118: pallet_balances::pallet::Call<T, I>
+   **/
+  PalletBalancesCall: {
+    _enum: {
+      transfer_allow_death: {
+        dest: 'MultiAddress',
+        value: 'Compact<u128>',
+      },
+      set_balance_deprecated: {
+        who: 'MultiAddress',
+        newFree: 'Compact<u128>',
+        oldReserved: 'Compact<u128>',
+      },
+      force_transfer: {
+        source: 'MultiAddress',
+        dest: 'MultiAddress',
+        value: 'Compact<u128>',
+      },
+      transfer_keep_alive: {
+        dest: 'MultiAddress',
+        value: 'Compact<u128>',
+      },
+      transfer_all: {
+        dest: 'MultiAddress',
+        keepAlive: 'bool',
+      },
+      force_unreserve: {
+        who: 'MultiAddress',
+        amount: 'u128',
+      },
+      upgrade_accounts: {
+        who: 'Vec<AccountId32>',
+      },
+      transfer: {
+        dest: 'MultiAddress',
+        value: 'Compact<u128>',
+      },
+      force_set_balance: {
+        who: 'MultiAddress',
+        newFree: 'Compact<u128>'
+      }
+    }
+  },
+  /**
+   * Lookup122: pallet_timestamp::pallet::Call<T>
+   **/
+  PalletTimestampCall: {
+    _enum: {
+      set: {
+        now: 'Compact<u64>'
+      }
+    }
+  },
+  /**
+   * Lookup123: pallet_treasury::pallet::Call<T, I>
+   **/
+  PalletTreasuryCall: {
+    _enum: {
+      propose_spend: {
+        value: 'Compact<u128>',
+        beneficiary: 'MultiAddress',
+      },
+      reject_proposal: {
+        proposalId: 'Compact<u32>',
+      },
+      approve_proposal: {
+        proposalId: 'Compact<u32>',
+      },
+      spend: {
+        amount: 'Compact<u128>',
+        beneficiary: 'MultiAddress',
+      },
+      remove_approval: {
+        proposalId: 'Compact<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup124: pallet_sudo::pallet::Call<T>
+   **/
+  PalletSudoCall: {
+    _enum: {
+      sudo: {
+        call: 'Call',
+      },
+      sudo_unchecked_weight: {
+        call: 'Call',
+        weight: 'SpWeightsWeightV2Weight',
+      },
+      set_key: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'MultiAddress',
+      },
+      sudo_as: {
+        who: 'MultiAddress',
+        call: 'Call'
+      }
+    }
+  },
+  /**
+   * Lookup125: orml_vesting::module::Call<T>
+   **/
+  OrmlVestingModuleCall: {
+    _enum: {
+      claim: 'Null',
+      vested_transfer: {
+        dest: 'MultiAddress',
+        schedule: 'OrmlVestingVestingSchedule',
+      },
+      update_vesting_schedules: {
+        who: 'MultiAddress',
+        vestingSchedules: 'Vec<OrmlVestingVestingSchedule>',
+      },
+      claim_for: {
+        dest: 'MultiAddress'
+      }
+    }
+  },
+  /**
+   * Lookup127: orml_xtokens::module::Call<T>
+   **/
+  OrmlXtokensModuleCall: {
+    _enum: {
+      transfer: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'u128',
+        dest: 'StagingXcmVersionedMultiLocation',
+        destWeightLimit: 'StagingXcmV3WeightLimit',
+      },
+      transfer_multiasset: {
+        asset: 'StagingXcmVersionedMultiAsset',
+        dest: 'StagingXcmVersionedMultiLocation',
+        destWeightLimit: 'StagingXcmV3WeightLimit',
+      },
+      transfer_with_fee: {
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'u128',
+        fee: 'u128',
+        dest: 'StagingXcmVersionedMultiLocation',
+        destWeightLimit: 'StagingXcmV3WeightLimit',
+      },
+      transfer_multiasset_with_fee: {
+        asset: 'StagingXcmVersionedMultiAsset',
+        fee: 'StagingXcmVersionedMultiAsset',
+        dest: 'StagingXcmVersionedMultiLocation',
+        destWeightLimit: 'StagingXcmV3WeightLimit',
+      },
+      transfer_multicurrencies: {
+        currencies: 'Vec<(PalletForeignAssetsAssetId,u128)>',
+        feeItem: 'u32',
+        dest: 'StagingXcmVersionedMultiLocation',
+        destWeightLimit: 'StagingXcmV3WeightLimit',
+      },
+      transfer_multiassets: {
+        assets: 'StagingXcmVersionedMultiAssets',
+        feeItem: 'u32',
+        dest: 'StagingXcmVersionedMultiLocation',
+        destWeightLimit: 'StagingXcmV3WeightLimit'
+      }
+    }
+  },
+  /**
+   * Lookup128: staging_xcm::VersionedMultiLocation
+   **/
+  StagingXcmVersionedMultiLocation: {
+    _enum: {
+      __Unused0: 'Null',
+      V2: 'StagingXcmV2MultiLocation',
+      __Unused2: 'Null',
+      V3: 'StagingXcmV3MultiLocation'
+    }
+  },
+  /**
+   * Lookup129: staging_xcm::v2::multilocation::MultiLocation
+   **/
+  StagingXcmV2MultiLocation: {
+    parents: 'u8',
+    interior: 'StagingXcmV2MultilocationJunctions'
+  },
+  /**
+   * Lookup130: staging_xcm::v2::multilocation::Junctions
+   **/
+  StagingXcmV2MultilocationJunctions: {
+    _enum: {
+      Here: 'Null',
+      X1: 'StagingXcmV2Junction',
+      X2: '(StagingXcmV2Junction,StagingXcmV2Junction)',
+      X3: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
+      X4: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
+      X5: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
+      X6: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
+      X7: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
+      X8: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)'
+    }
+  },
+  /**
+   * Lookup131: staging_xcm::v2::junction::Junction
+   **/
+  StagingXcmV2Junction: {
+    _enum: {
+      Parachain: 'Compact<u32>',
+      AccountId32: {
+        network: 'StagingXcmV2NetworkId',
+        id: '[u8;32]',
+      },
+      AccountIndex64: {
+        network: 'StagingXcmV2NetworkId',
+        index: 'Compact<u64>',
+      },
+      AccountKey20: {
+        network: 'StagingXcmV2NetworkId',
+        key: '[u8;20]',
+      },
+      PalletInstance: 'u8',
+      GeneralIndex: 'Compact<u128>',
+      GeneralKey: 'Bytes',
+      OnlyChild: 'Null',
+      Plurality: {
+        id: 'StagingXcmV2BodyId',
+        part: 'StagingXcmV2BodyPart'
+      }
+    }
+  },
+  /**
+   * Lookup132: staging_xcm::v2::NetworkId
+   **/
+  StagingXcmV2NetworkId: {
+    _enum: {
+      Any: 'Null',
+      Named: 'Bytes',
+      Polkadot: 'Null',
+      Kusama: 'Null'
+    }
+  },
+  /**
+   * Lookup134: staging_xcm::v2::BodyId
+   **/
+  StagingXcmV2BodyId: {
+    _enum: {
+      Unit: 'Null',
+      Named: 'Bytes',
+      Index: 'Compact<u32>',
+      Executive: 'Null',
+      Technical: 'Null',
+      Legislative: 'Null',
+      Judicial: 'Null',
+      Defense: 'Null',
+      Administration: 'Null',
+      Treasury: 'Null'
+    }
+  },
+  /**
+   * Lookup135: staging_xcm::v2::BodyPart
+   **/
+  StagingXcmV2BodyPart: {
+    _enum: {
+      Voice: 'Null',
+      Members: {
+        count: 'Compact<u32>',
+      },
+      Fraction: {
+        nom: 'Compact<u32>',
+        denom: 'Compact<u32>',
+      },
+      AtLeastProportion: {
+        nom: 'Compact<u32>',
+        denom: 'Compact<u32>',
+      },
+      MoreThanProportion: {
+        nom: 'Compact<u32>',
+        denom: 'Compact<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup136: staging_xcm::v3::WeightLimit
+   **/
+  StagingXcmV3WeightLimit: {
+    _enum: {
+      Unlimited: 'Null',
+      Limited: 'SpWeightsWeightV2Weight'
+    }
+  },
+  /**
+   * Lookup137: staging_xcm::VersionedMultiAsset
+   **/
+  StagingXcmVersionedMultiAsset: {
+    _enum: {
+      __Unused0: 'Null',
+      V2: 'StagingXcmV2MultiAsset',
+      __Unused2: 'Null',
+      V3: 'StagingXcmV3MultiAsset'
+    }
+  },
+  /**
+   * Lookup138: staging_xcm::v2::multiasset::MultiAsset
+   **/
+  StagingXcmV2MultiAsset: {
+    id: 'StagingXcmV2MultiassetAssetId',
+    fun: 'StagingXcmV2MultiassetFungibility'
+  },
+  /**
+   * Lookup139: staging_xcm::v2::multiasset::AssetId
+   **/
+  StagingXcmV2MultiassetAssetId: {
+    _enum: {
+      Concrete: 'StagingXcmV2MultiLocation',
+      Abstract: 'Bytes'
+    }
+  },
+  /**
+   * Lookup140: staging_xcm::v2::multiasset::Fungibility
+   **/
+  StagingXcmV2MultiassetFungibility: {
+    _enum: {
+      Fungible: 'Compact<u128>',
+      NonFungible: 'StagingXcmV2MultiassetAssetInstance'
+    }
+  },
+  /**
+   * Lookup141: staging_xcm::v2::multiasset::AssetInstance
+   **/
+  StagingXcmV2MultiassetAssetInstance: {
+    _enum: {
+      Undefined: 'Null',
+      Index: 'Compact<u128>',
+      Array4: '[u8;4]',
+      Array8: '[u8;8]',
+      Array16: '[u8;16]',
+      Array32: '[u8;32]',
+      Blob: 'Bytes'
+    }
+  },
+  /**
+   * Lookup144: staging_xcm::VersionedMultiAssets
+   **/
+  StagingXcmVersionedMultiAssets: {
+    _enum: {
+      __Unused0: 'Null',
+      V2: 'StagingXcmV2MultiassetMultiAssets',
+      __Unused2: 'Null',
+      V3: 'StagingXcmV3MultiassetMultiAssets'
+    }
+  },
+  /**
+   * Lookup145: staging_xcm::v2::multiasset::MultiAssets
+   **/
+  StagingXcmV2MultiassetMultiAssets: 'Vec<StagingXcmV2MultiAsset>',
+  /**
+   * Lookup147: orml_tokens::module::Call<T>
+   **/
+  OrmlTokensModuleCall: {
+    _enum: {
+      transfer: {
+        dest: 'MultiAddress',
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'Compact<u128>',
+      },
+      transfer_all: {
+        dest: 'MultiAddress',
+        currencyId: 'PalletForeignAssetsAssetId',
+        keepAlive: 'bool',
+      },
+      transfer_keep_alive: {
+        dest: 'MultiAddress',
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'Compact<u128>',
+      },
+      force_transfer: {
+        source: 'MultiAddress',
+        dest: 'MultiAddress',
+        currencyId: 'PalletForeignAssetsAssetId',
+        amount: 'Compact<u128>',
+      },
+      set_balance: {
+        who: 'MultiAddress',
+        currencyId: 'PalletForeignAssetsAssetId',
+        newFree: 'Compact<u128>',
+        newReserved: 'Compact<u128>'
+      }
+    }
+  },
+  /**
+   * Lookup148: pallet_identity::pallet::Call<T>
+   **/
+  PalletIdentityCall: {
+    _enum: {
+      add_registrar: {
+        account: 'MultiAddress',
+      },
+      set_identity: {
+        info: 'PalletIdentityIdentityInfo',
+      },
+      set_subs: {
+        subs: 'Vec<(AccountId32,Data)>',
+      },
+      clear_identity: 'Null',
+      request_judgement: {
+        regIndex: 'Compact<u32>',
+        maxFee: 'Compact<u128>',
+      },
+      cancel_request: {
+        regIndex: 'u32',
+      },
+      set_fee: {
+        index: 'Compact<u32>',
+        fee: 'Compact<u128>',
+      },
+      set_account_id: {
+        _alias: {
+          new_: 'new',
+        },
+        index: 'Compact<u32>',
+        new_: 'MultiAddress',
+      },
+      set_fields: {
+        index: 'Compact<u32>',
+        fields: 'PalletIdentityBitFlags',
+      },
+      provide_judgement: {
+        regIndex: 'Compact<u32>',
+        target: 'MultiAddress',
+        judgement: 'PalletIdentityJudgement',
+        identity: 'H256',
+      },
+      kill_identity: {
+        target: 'MultiAddress',
+      },
+      add_sub: {
+        sub: 'MultiAddress',
+        data: 'Data',
+      },
+      rename_sub: {
+        sub: 'MultiAddress',
+        data: 'Data',
+      },
+      remove_sub: {
+        sub: 'MultiAddress',
+      },
+      quit_sub: 'Null',
+      force_insert_identities: {
+        identities: 'Vec<(AccountId32,PalletIdentityRegistration)>',
+      },
+      force_remove_identities: {
+        identities: 'Vec<AccountId32>',
+      },
+      force_set_subs: {
+        subs: 'Vec<(AccountId32,(u128,Vec<(AccountId32,Data)>))>'
+      }
+    }
+  },
+  /**
+   * Lookup149: pallet_identity::types::IdentityInfo<FieldLimit>
+   **/
+  PalletIdentityIdentityInfo: {
+    additional: 'Vec<(Data,Data)>',
+    display: 'Data',
+    legal: 'Data',
+    web: 'Data',
+    riot: 'Data',
+    email: 'Data',
+    pgpFingerprint: 'Option<[u8;20]>',
+    image: 'Data',
+    twitter: 'Data'
+  },
+  /**
+   * Lookup185: pallet_identity::types::BitFlags<pallet_identity::types::IdentityField>
+   **/
+  PalletIdentityBitFlags: {
+    _bitLength: 64,
+    Display: 1,
+    Legal: 2,
+    Web: 4,
+    Riot: 8,
+    Email: 16,
+    PgpFingerprint: 32,
+    Image: 64,
+    Twitter: 128
+  },
+  /**
+   * Lookup186: pallet_identity::types::IdentityField
+   **/
+  PalletIdentityIdentityField: {
+    _enum: ['__Unused0', 'Display', 'Legal', '__Unused3', 'Web', '__Unused5', '__Unused6', '__Unused7', 'Riot', '__Unused9', '__Unused10', '__Unused11', '__Unused12', '__Unused13', '__Unused14', '__Unused15', 'Email', '__Unused17', '__Unused18', '__Unused19', '__Unused20', '__Unused21', '__Unused22', '__Unused23', '__Unused24', '__Unused25', '__Unused26', '__Unused27', '__Unused28', '__Unused29', '__Unused30', '__Unused31', 'PgpFingerprint', '__Unused33', '__Unused34', '__Unused35', '__Unused36', '__Unused37', '__Unused38', '__Unused39', '__Unused40', '__Unused41', '__Unused42', '__Unused43', '__Unused44', '__Unused45', '__Unused46', '__Unused47', '__Unused48', '__Unused49', '__Unused50', '__Unused51', '__Unused52', '__Unused53', '__Unused54', '__Unused55', '__Unused56', '__Unused57', '__Unused58', '__Unused59', '__Unused60', '__Unused61', '__Unused62', '__Unused63', 'Image', '__Unused65', '__Unused66', '__Unused67', '__Unused68', '__Unused69', '__Unused70', '__Unused71', '__Unused72', '__Unused73', '__Unused74', '__Unused75', '__Unused76', '__Unused77', '__Unused78', '__Unused79', '__Unused80', '__Unused81', '__Unused82', '__Unused83', '__Unused84', '__Unused85', '__Unused86', '__Unused87', '__Unused88', '__Unused89', '__Unused90', '__Unused91', '__Unused92', '__Unused93', '__Unused94', '__Unused95', '__Unused96', '__Unused97', '__Unused98', '__Unused99', '__Unused100', '__Unused101', '__Unused102', '__Unused103', '__Unused104', '__Unused105', '__Unused106', '__Unused107', '__Unused108', '__Unused109', '__Unused110', '__Unused111', '__Unused112', '__Unused113', '__Unused114', '__Unused115', '__Unused116', '__Unused117', '__Unused118', '__Unused119', '__Unused120', '__Unused121', '__Unused122', '__Unused123', '__Unused124', '__Unused125', '__Unused126', '__Unused127', 'Twitter']
+  },
+  /**
+   * Lookup187: pallet_identity::types::Judgement<Balance>
+   **/
+  PalletIdentityJudgement: {
+    _enum: {
+      Unknown: 'Null',
+      FeePaid: 'u128',
+      Reasonable: 'Null',
+      KnownGood: 'Null',
+      OutOfDate: 'Null',
+      LowQuality: 'Null',
+      Erroneous: 'Null'
+    }
+  },
+  /**
+   * Lookup190: pallet_identity::types::Registration<Balance, MaxJudgements, MaxAdditionalFields>
+   **/
+  PalletIdentityRegistration: {
+    judgements: 'Vec<(u32,PalletIdentityJudgement)>',
+    deposit: 'u128',
+    info: 'PalletIdentityIdentityInfo'
+  },
+  /**
+   * Lookup198: pallet_preimage::pallet::Call<T>
+   **/
+  PalletPreimageCall: {
+    _enum: {
+      note_preimage: {
+        bytes: 'Bytes',
+      },
+      unnote_preimage: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+      },
+      request_preimage: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+      },
+      unrequest_preimage: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256'
+      }
+    }
+  },
+  /**
+   * Lookup199: pallet_democracy::pallet::Call<T>
+   **/
+  PalletDemocracyCall: {
+    _enum: {
+      propose: {
+        proposal: 'FrameSupportPreimagesBounded',
+        value: 'Compact<u128>',
+      },
+      second: {
+        proposal: 'Compact<u32>',
+      },
+      vote: {
+        refIndex: 'Compact<u32>',
+        vote: 'PalletDemocracyVoteAccountVote',
+      },
+      emergency_cancel: {
+        refIndex: 'u32',
+      },
+      external_propose: {
+        proposal: 'FrameSupportPreimagesBounded',
+      },
+      external_propose_majority: {
+        proposal: 'FrameSupportPreimagesBounded',
+      },
+      external_propose_default: {
+        proposal: 'FrameSupportPreimagesBounded',
+      },
+      fast_track: {
+        proposalHash: 'H256',
+        votingPeriod: 'u32',
+        delay: 'u32',
+      },
+      veto_external: {
+        proposalHash: 'H256',
+      },
+      cancel_referendum: {
+        refIndex: 'Compact<u32>',
+      },
+      delegate: {
+        to: 'MultiAddress',
+        conviction: 'PalletDemocracyConviction',
+        balance: 'u128',
+      },
+      undelegate: 'Null',
+      clear_public_proposals: 'Null',
+      unlock: {
+        target: 'MultiAddress',
+      },
+      remove_vote: {
+        index: 'u32',
+      },
+      remove_other_vote: {
+        target: 'MultiAddress',
+        index: 'u32',
+      },
+      blacklist: {
+        proposalHash: 'H256',
+        maybeRefIndex: 'Option<u32>',
+      },
+      cancel_proposal: {
+        propIndex: 'Compact<u32>',
+      },
+      set_metadata: {
+        owner: 'PalletDemocracyMetadataOwner',
+        maybeHash: 'Option<H256>'
+      }
+    }
+  },
+  /**
+   * Lookup200: pallet_democracy::conviction::Conviction
+   **/
+  PalletDemocracyConviction: {
+    _enum: ['None', 'Locked1x', 'Locked2x', 'Locked3x', 'Locked4x', 'Locked5x', 'Locked6x']
+  },
+  /**
+   * Lookup203: pallet_collective::pallet::Call<T, I>
+   **/
+  PalletCollectiveCall: {
+    _enum: {
+      set_members: {
+        newMembers: 'Vec<AccountId32>',
+        prime: 'Option<AccountId32>',
+        oldCount: 'u32',
+      },
+      execute: {
+        proposal: 'Call',
+        lengthBound: 'Compact<u32>',
+      },
+      propose: {
+        threshold: 'Compact<u32>',
+        proposal: 'Call',
+        lengthBound: 'Compact<u32>',
+      },
+      vote: {
+        proposal: 'H256',
+        index: 'Compact<u32>',
+        approve: 'bool',
+      },
+      __Unused4: 'Null',
+      disapprove_proposal: {
+        proposalHash: 'H256',
+      },
+      close: {
+        proposalHash: 'H256',
+        index: 'Compact<u32>',
+        proposalWeightBound: 'SpWeightsWeightV2Weight',
+        lengthBound: 'Compact<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup205: pallet_membership::pallet::Call<T, I>
+   **/
+  PalletMembershipCall: {
+    _enum: {
+      add_member: {
+        who: 'MultiAddress',
+      },
+      remove_member: {
+        who: 'MultiAddress',
+      },
+      swap_member: {
+        remove: 'MultiAddress',
+        add: 'MultiAddress',
+      },
+      reset_members: {
+        members: 'Vec<AccountId32>',
+      },
+      change_key: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'MultiAddress',
+      },
+      set_prime: {
+        who: 'MultiAddress',
+      },
+      clear_prime: 'Null'
+    }
+  },
+  /**
+   * Lookup207: pallet_ranked_collective::pallet::Call<T, I>
+   **/
+  PalletRankedCollectiveCall: {
+    _enum: {
+      add_member: {
+        who: 'MultiAddress',
+      },
+      promote_member: {
+        who: 'MultiAddress',
+      },
+      demote_member: {
+        who: 'MultiAddress',
+      },
+      remove_member: {
+        who: 'MultiAddress',
+        minRank: 'u16',
+      },
+      vote: {
+        poll: 'u32',
+        aye: 'bool',
+      },
+      cleanup_poll: {
+        pollIndex: 'u32',
+        max: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup208: pallet_referenda::pallet::Call<T, I>
+   **/
+  PalletReferendaCall: {
+    _enum: {
+      submit: {
+        proposalOrigin: 'OpalRuntimeOriginCaller',
+        proposal: 'FrameSupportPreimagesBounded',
+        enactmentMoment: 'FrameSupportScheduleDispatchTime',
+      },
+      place_decision_deposit: {
+        index: 'u32',
+      },
+      refund_decision_deposit: {
+        index: 'u32',
+      },
+      cancel: {
+        index: 'u32',
+      },
+      kill: {
+        index: 'u32',
+      },
+      nudge_referendum: {
+        index: 'u32',
+      },
+      one_fewer_deciding: {
+        track: 'u16',
+      },
+      refund_submission_deposit: {
+        index: 'u32',
+      },
+      set_metadata: {
+        index: 'u32',
+        maybeHash: 'Option<H256>'
+      }
+    }
+  },
+  /**
+   * Lookup209: opal_runtime::OriginCaller
+   **/
+  OpalRuntimeOriginCaller: {
+    _enum: {
+      system: 'FrameSupportDispatchRawOrigin',
+      __Unused1: 'Null',
+      __Unused2: 'Null',
+      __Unused3: 'Null',
+      __Unused4: 'Null',
+      __Unused5: 'Null',
+      __Unused6: 'Null',
+      Void: 'SpCoreVoid',
+      __Unused8: 'Null',
+      __Unused9: 'Null',
+      __Unused10: 'Null',
+      __Unused11: 'Null',
+      __Unused12: 'Null',
+      __Unused13: 'Null',
+      __Unused14: 'Null',
+      __Unused15: 'Null',
+      __Unused16: 'Null',
+      __Unused17: 'Null',
+      __Unused18: 'Null',
+      __Unused19: 'Null',
+      __Unused20: 'Null',
+      __Unused21: 'Null',
+      __Unused22: 'Null',
+      __Unused23: 'Null',
+      __Unused24: 'Null',
+      __Unused25: 'Null',
+      __Unused26: 'Null',
+      __Unused27: 'Null',
+      __Unused28: 'Null',
+      __Unused29: 'Null',
+      __Unused30: 'Null',
+      __Unused31: 'Null',
+      __Unused32: 'Null',
+      __Unused33: 'Null',
+      __Unused34: 'Null',
+      __Unused35: 'Null',
+      __Unused36: 'Null',
+      __Unused37: 'Null',
+      __Unused38: 'Null',
+      __Unused39: 'Null',
+      __Unused40: 'Null',
+      __Unused41: 'Null',
+      __Unused42: 'Null',
+      Council: 'PalletCollectiveRawOrigin',
+      TechnicalCommittee: 'PalletCollectiveRawOrigin',
+      __Unused45: 'Null',
+      __Unused46: 'Null',
+      __Unused47: 'Null',
+      __Unused48: 'Null',
+      __Unused49: 'Null',
+      __Unused50: 'Null',
+      PolkadotXcm: 'PalletXcmOrigin',
+      CumulusXcm: 'CumulusPalletXcmOrigin',
+      __Unused53: 'Null',
+      __Unused54: 'Null',
+      __Unused55: 'Null',
+      __Unused56: 'Null',
+      __Unused57: 'Null',
+      __Unused58: 'Null',
+      __Unused59: 'Null',
+      __Unused60: 'Null',
+      __Unused61: 'Null',
+      __Unused62: 'Null',
+      __Unused63: 'Null',
+      __Unused64: 'Null',
+      __Unused65: 'Null',
+      __Unused66: 'Null',
+      __Unused67: 'Null',
+      __Unused68: 'Null',
+      __Unused69: 'Null',
+      __Unused70: 'Null',
+      __Unused71: 'Null',
+      __Unused72: 'Null',
+      __Unused73: 'Null',
+      __Unused74: 'Null',
+      __Unused75: 'Null',
+      __Unused76: 'Null',
+      __Unused77: 'Null',
+      __Unused78: 'Null',
+      __Unused79: 'Null',
+      __Unused80: 'Null',
+      __Unused81: 'Null',
+      __Unused82: 'Null',
+      __Unused83: 'Null',
+      __Unused84: 'Null',
+      __Unused85: 'Null',
+      __Unused86: 'Null',
+      __Unused87: 'Null',
+      __Unused88: 'Null',
+      __Unused89: 'Null',
+      __Unused90: 'Null',
+      __Unused91: 'Null',
+      __Unused92: 'Null',
+      __Unused93: 'Null',
+      __Unused94: 'Null',
+      __Unused95: 'Null',
+      __Unused96: 'Null',
+      __Unused97: 'Null',
+      __Unused98: 'Null',
+      Origins: 'PalletGovOriginsOrigin',
+      __Unused100: 'Null',
+      Ethereum: 'PalletEthereumRawOrigin'
+    }
+  },
+  /**
+   * Lookup210: frame_support::dispatch::RawOrigin<sp_core::crypto::AccountId32>
+   **/
+  FrameSupportDispatchRawOrigin: {
+    _enum: {
+      Root: 'Null',
+      Signed: 'AccountId32',
+      None: 'Null'
+    }
+  },
+  /**
+   * Lookup211: pallet_collective::RawOrigin<sp_core::crypto::AccountId32, I>
+   **/
+  PalletCollectiveRawOrigin: {
+    _enum: {
+      Members: '(u32,u32)',
+      Member: 'AccountId32',
+      _Phantom: 'Null'
+    }
+  },
+  /**
+   * Lookup213: pallet_gov_origins::pallet::Origin
+   **/
+  PalletGovOriginsOrigin: {
+    _enum: ['FellowshipProposition']
+  },
+  /**
+   * Lookup214: pallet_xcm::pallet::Origin
+   **/
+  PalletXcmOrigin: {
+    _enum: {
+      Xcm: 'StagingXcmV3MultiLocation',
+      Response: 'StagingXcmV3MultiLocation'
+    }
+  },
+  /**
+   * Lookup215: cumulus_pallet_xcm::pallet::Origin
+   **/
+  CumulusPalletXcmOrigin: {
+    _enum: {
+      Relay: 'Null',
+      SiblingParachain: 'u32'
+    }
+  },
+  /**
+   * Lookup216: pallet_ethereum::RawOrigin
+   **/
+  PalletEthereumRawOrigin: {
+    _enum: {
+      EthereumTransaction: 'H160'
+    }
+  },
+  /**
+   * Lookup218: sp_core::Void
+   **/
+  SpCoreVoid: 'Null',
+  /**
+   * Lookup219: frame_support::traits::schedule::DispatchTime<BlockNumber>
+   **/
+  FrameSupportScheduleDispatchTime: {
+    _enum: {
+      At: 'u32',
+      After: 'u32'
+    }
+  },
+  /**
+   * Lookup220: pallet_scheduler::pallet::Call<T>
+   **/
+  PalletSchedulerCall: {
+    _enum: {
+      schedule: {
+        when: 'u32',
+        maybePeriodic: 'Option<(u32,u32)>',
+        priority: 'u8',
+        call: 'Call',
+      },
+      cancel: {
+        when: 'u32',
+        index: 'u32',
+      },
+      schedule_named: {
+        id: '[u8;32]',
+        when: 'u32',
+        maybePeriodic: 'Option<(u32,u32)>',
+        priority: 'u8',
+        call: 'Call',
+      },
+      cancel_named: {
+        id: '[u8;32]',
+      },
+      schedule_after: {
+        after: 'u32',
+        maybePeriodic: 'Option<(u32,u32)>',
+        priority: 'u8',
+        call: 'Call',
+      },
+      schedule_named_after: {
+        id: '[u8;32]',
+        after: 'u32',
+        maybePeriodic: 'Option<(u32,u32)>',
+        priority: 'u8',
+        call: 'Call'
+      }
+    }
+  },
+  /**
+   * Lookup223: cumulus_pallet_xcmp_queue::pallet::Call<T>
+   **/
+  CumulusPalletXcmpQueueCall: {
+    _enum: {
+      service_overweight: {
+        index: 'u64',
+        weightLimit: 'SpWeightsWeightV2Weight',
+      },
+      suspend_xcm_execution: 'Null',
+      resume_xcm_execution: 'Null',
+      update_suspend_threshold: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'u32',
+      },
+      update_drop_threshold: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'u32',
+      },
+      update_resume_threshold: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'u32',
+      },
+      update_threshold_weight: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'SpWeightsWeightV2Weight',
+      },
+      update_weight_restrict_decay: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'SpWeightsWeightV2Weight',
+      },
+      update_xcmp_max_individual_weight: {
+        _alias: {
+          new_: 'new',
+        },
+        new_: 'SpWeightsWeightV2Weight'
+      }
+    }
+  },
+  /**
+   * Lookup224: pallet_xcm::pallet::Call<T>
+   **/
+  PalletXcmCall: {
+    _enum: {
+      send: {
+        dest: 'StagingXcmVersionedMultiLocation',
+        message: 'StagingXcmVersionedXcm',
+      },
+      teleport_assets: {
+        dest: 'StagingXcmVersionedMultiLocation',
+        beneficiary: 'StagingXcmVersionedMultiLocation',
+        assets: 'StagingXcmVersionedMultiAssets',
+        feeAssetItem: 'u32',
+      },
+      reserve_transfer_assets: {
+        dest: 'StagingXcmVersionedMultiLocation',
+        beneficiary: 'StagingXcmVersionedMultiLocation',
+        assets: 'StagingXcmVersionedMultiAssets',
+        feeAssetItem: 'u32',
+      },
+      execute: {
+        message: 'StagingXcmVersionedXcm',
+        maxWeight: 'SpWeightsWeightV2Weight',
+      },
+      force_xcm_version: {
+        location: 'StagingXcmV3MultiLocation',
+        version: 'u32',
+      },
+      force_default_xcm_version: {
+        maybeXcmVersion: 'Option<u32>',
+      },
+      force_subscribe_version_notify: {
+        location: 'StagingXcmVersionedMultiLocation',
+      },
+      force_unsubscribe_version_notify: {
+        location: 'StagingXcmVersionedMultiLocation',
+      },
+      limited_reserve_transfer_assets: {
+        dest: 'StagingXcmVersionedMultiLocation',
+        beneficiary: 'StagingXcmVersionedMultiLocation',
+        assets: 'StagingXcmVersionedMultiAssets',
+        feeAssetItem: 'u32',
+        weightLimit: 'StagingXcmV3WeightLimit',
+      },
+      limited_teleport_assets: {
+        dest: 'StagingXcmVersionedMultiLocation',
+        beneficiary: 'StagingXcmVersionedMultiLocation',
+        assets: 'StagingXcmVersionedMultiAssets',
+        feeAssetItem: 'u32',
+        weightLimit: 'StagingXcmV3WeightLimit',
+      },
+      force_suspension: {
+        suspended: 'bool'
+      }
+    }
+  },
+  /**
+   * Lookup225: staging_xcm::VersionedXcm<RuntimeCall>
+   **/
+  StagingXcmVersionedXcm: {
+    _enum: {
+      __Unused0: 'Null',
+      __Unused1: 'Null',
+      V2: 'StagingXcmV2Xcm',
+      V3: 'StagingXcmV3Xcm'
+    }
+  },
+  /**
+   * Lookup226: staging_xcm::v2::Xcm<RuntimeCall>
+   **/
+  StagingXcmV2Xcm: 'Vec<StagingXcmV2Instruction>',
+  /**
+   * Lookup228: staging_xcm::v2::Instruction<RuntimeCall>
+   **/
+  StagingXcmV2Instruction: {
+    _enum: {
+      WithdrawAsset: 'StagingXcmV2MultiassetMultiAssets',
+      ReserveAssetDeposited: 'StagingXcmV2MultiassetMultiAssets',
+      ReceiveTeleportedAsset: 'StagingXcmV2MultiassetMultiAssets',
+      QueryResponse: {
+        queryId: 'Compact<u64>',
+        response: 'StagingXcmV2Response',
+        maxWeight: 'Compact<u64>',
+      },
+      TransferAsset: {
+        assets: 'StagingXcmV2MultiassetMultiAssets',
+        beneficiary: 'StagingXcmV2MultiLocation',
+      },
+      TransferReserveAsset: {
+        assets: 'StagingXcmV2MultiassetMultiAssets',
+        dest: 'StagingXcmV2MultiLocation',
+        xcm: 'StagingXcmV2Xcm',
+      },
+      Transact: {
+        originType: 'StagingXcmV2OriginKind',
+        requireWeightAtMost: 'Compact<u64>',
+        call: 'StagingXcmDoubleEncoded',
+      },
+      HrmpNewChannelOpenRequest: {
+        sender: 'Compact<u32>',
+        maxMessageSize: 'Compact<u32>',
+        maxCapacity: 'Compact<u32>',
+      },
+      HrmpChannelAccepted: {
+        recipient: 'Compact<u32>',
+      },
+      HrmpChannelClosing: {
+        initiator: 'Compact<u32>',
+        sender: 'Compact<u32>',
+        recipient: 'Compact<u32>',
+      },
+      ClearOrigin: 'Null',
+      DescendOrigin: 'StagingXcmV2MultilocationJunctions',
+      ReportError: {
+        queryId: 'Compact<u64>',
+        dest: 'StagingXcmV2MultiLocation',
+        maxResponseWeight: 'Compact<u64>',
+      },
+      DepositAsset: {
+        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
+        maxAssets: 'Compact<u32>',
+        beneficiary: 'StagingXcmV2MultiLocation',
+      },
+      DepositReserveAsset: {
+        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
+        maxAssets: 'Compact<u32>',
+        dest: 'StagingXcmV2MultiLocation',
+        xcm: 'StagingXcmV2Xcm',
+      },
+      ExchangeAsset: {
+        give: 'StagingXcmV2MultiassetMultiAssetFilter',
+        receive: 'StagingXcmV2MultiassetMultiAssets',
+      },
+      InitiateReserveWithdraw: {
+        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
+        reserve: 'StagingXcmV2MultiLocation',
+        xcm: 'StagingXcmV2Xcm',
+      },
+      InitiateTeleport: {
+        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
+        dest: 'StagingXcmV2MultiLocation',
+        xcm: 'StagingXcmV2Xcm',
+      },
+      QueryHolding: {
+        queryId: 'Compact<u64>',
+        dest: 'StagingXcmV2MultiLocation',
+        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
+        maxResponseWeight: 'Compact<u64>',
+      },
+      BuyExecution: {
+        fees: 'StagingXcmV2MultiAsset',
+        weightLimit: 'StagingXcmV2WeightLimit',
+      },
+      RefundSurplus: 'Null',
+      SetErrorHandler: 'StagingXcmV2Xcm',
+      SetAppendix: 'StagingXcmV2Xcm',
+      ClearError: 'Null',
+      ClaimAsset: {
+        assets: 'StagingXcmV2MultiassetMultiAssets',
+        ticket: 'StagingXcmV2MultiLocation',
+      },
+      Trap: 'Compact<u64>',
+      SubscribeVersion: {
+        queryId: 'Compact<u64>',
+        maxResponseWeight: 'Compact<u64>',
+      },
+      UnsubscribeVersion: 'Null'
+    }
+  },
+  /**
+   * Lookup229: staging_xcm::v2::Response
+   **/
+  StagingXcmV2Response: {
+    _enum: {
+      Null: 'Null',
+      Assets: 'StagingXcmV2MultiassetMultiAssets',
+      ExecutionResult: 'Option<(u32,StagingXcmV2TraitsError)>',
+      Version: 'u32'
+    }
+  },
+  /**
+   * Lookup232: staging_xcm::v2::traits::Error
+   **/
+  StagingXcmV2TraitsError: {
+    _enum: {
+      Overflow: 'Null',
+      Unimplemented: 'Null',
+      UntrustedReserveLocation: 'Null',
+      UntrustedTeleportLocation: 'Null',
+      MultiLocationFull: 'Null',
+      MultiLocationNotInvertible: 'Null',
+      BadOrigin: 'Null',
+      InvalidLocation: 'Null',
+      AssetNotFound: 'Null',
+      FailedToTransactAsset: 'Null',
+      NotWithdrawable: 'Null',
+      LocationCannotHold: 'Null',
+      ExceedsMaxMessageSize: 'Null',
+      DestinationUnsupported: 'Null',
+      Transport: 'Null',
+      Unroutable: 'Null',
+      UnknownClaim: 'Null',
+      FailedToDecode: 'Null',
+      MaxWeightInvalid: 'Null',
+      NotHoldingFees: 'Null',
+      TooExpensive: 'Null',
+      Trap: 'u64',
+      UnhandledXcmVersion: 'Null',
+      WeightLimitReached: 'u64',
+      Barrier: 'Null',
+      WeightNotComputable: 'Null'
+    }
+  },
+  /**
+   * Lookup233: staging_xcm::v2::OriginKind
+   **/
+  StagingXcmV2OriginKind: {
+    _enum: ['Native', 'SovereignAccount', 'Superuser', 'Xcm']
+  },
+  /**
+   * Lookup234: staging_xcm::double_encoded::DoubleEncoded<T>
+   **/
+  StagingXcmDoubleEncoded: {
+    encoded: 'Bytes'
+  },
+  /**
+   * Lookup235: staging_xcm::v2::multiasset::MultiAssetFilter
+   **/
+  StagingXcmV2MultiassetMultiAssetFilter: {
+    _enum: {
+      Definite: 'StagingXcmV2MultiassetMultiAssets',
+      Wild: 'StagingXcmV2MultiassetWildMultiAsset'
+    }
+  },
+  /**
+   * Lookup236: staging_xcm::v2::multiasset::WildMultiAsset
+   **/
+  StagingXcmV2MultiassetWildMultiAsset: {
+    _enum: {
+      All: 'Null',
+      AllOf: {
+        id: 'StagingXcmV2MultiassetAssetId',
+        fun: 'StagingXcmV2MultiassetWildFungibility'
+      }
+    }
+  },
+  /**
+   * Lookup237: staging_xcm::v2::multiasset::WildFungibility
+   **/
+  StagingXcmV2MultiassetWildFungibility: {
+    _enum: ['Fungible', 'NonFungible']
+  },
+  /**
+   * Lookup238: staging_xcm::v2::WeightLimit
+   **/
+  StagingXcmV2WeightLimit: {
+    _enum: {
+      Unlimited: 'Null',
+      Limited: 'Compact<u64>'
+    }
+  },
+  /**
+   * Lookup239: staging_xcm::v3::Xcm<Call>
+   **/
+  StagingXcmV3Xcm: 'Vec<StagingXcmV3Instruction>',
+  /**
+   * Lookup241: staging_xcm::v3::Instruction<Call>
+   **/
+  StagingXcmV3Instruction: {
+    _enum: {
+      WithdrawAsset: 'StagingXcmV3MultiassetMultiAssets',
+      ReserveAssetDeposited: 'StagingXcmV3MultiassetMultiAssets',
+      ReceiveTeleportedAsset: 'StagingXcmV3MultiassetMultiAssets',
+      QueryResponse: {
+        queryId: 'Compact<u64>',
+        response: 'StagingXcmV3Response',
+        maxWeight: 'SpWeightsWeightV2Weight',
+        querier: 'Option<StagingXcmV3MultiLocation>',
+      },
+      TransferAsset: {
+        assets: 'StagingXcmV3MultiassetMultiAssets',
+        beneficiary: 'StagingXcmV3MultiLocation',
+      },
+      TransferReserveAsset: {
+        assets: 'StagingXcmV3MultiassetMultiAssets',
+        dest: 'StagingXcmV3MultiLocation',
+        xcm: 'StagingXcmV3Xcm',
+      },
+      Transact: {
+        originKind: 'StagingXcmV2OriginKind',
+        requireWeightAtMost: 'SpWeightsWeightV2Weight',
+        call: 'StagingXcmDoubleEncoded',
+      },
+      HrmpNewChannelOpenRequest: {
+        sender: 'Compact<u32>',
+        maxMessageSize: 'Compact<u32>',
+        maxCapacity: 'Compact<u32>',
+      },
+      HrmpChannelAccepted: {
+        recipient: 'Compact<u32>',
+      },
+      HrmpChannelClosing: {
+        initiator: 'Compact<u32>',
+        sender: 'Compact<u32>',
+        recipient: 'Compact<u32>',
+      },
+      ClearOrigin: 'Null',
+      DescendOrigin: 'StagingXcmV3Junctions',
+      ReportError: 'StagingXcmV3QueryResponseInfo',
+      DepositAsset: {
+        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
+        beneficiary: 'StagingXcmV3MultiLocation',
+      },
+      DepositReserveAsset: {
+        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
+        dest: 'StagingXcmV3MultiLocation',
+        xcm: 'StagingXcmV3Xcm',
+      },
+      ExchangeAsset: {
+        give: 'StagingXcmV3MultiassetMultiAssetFilter',
+        want: 'StagingXcmV3MultiassetMultiAssets',
+        maximal: 'bool',
+      },
+      InitiateReserveWithdraw: {
+        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
+        reserve: 'StagingXcmV3MultiLocation',
+        xcm: 'StagingXcmV3Xcm',
+      },
+      InitiateTeleport: {
+        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
+        dest: 'StagingXcmV3MultiLocation',
+        xcm: 'StagingXcmV3Xcm',
+      },
+      ReportHolding: {
+        responseInfo: 'StagingXcmV3QueryResponseInfo',
+        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
+      },
+      BuyExecution: {
+        fees: 'StagingXcmV3MultiAsset',
+        weightLimit: 'StagingXcmV3WeightLimit',
+      },
+      RefundSurplus: 'Null',
+      SetErrorHandler: 'StagingXcmV3Xcm',
+      SetAppendix: 'StagingXcmV3Xcm',
+      ClearError: 'Null',
+      ClaimAsset: {
+        assets: 'StagingXcmV3MultiassetMultiAssets',
+        ticket: 'StagingXcmV3MultiLocation',
+      },
+      Trap: 'Compact<u64>',
+      SubscribeVersion: {
+        queryId: 'Compact<u64>',
+        maxResponseWeight: 'SpWeightsWeightV2Weight',
+      },
+      UnsubscribeVersion: 'Null',
+      BurnAsset: 'StagingXcmV3MultiassetMultiAssets',
+      ExpectAsset: 'StagingXcmV3MultiassetMultiAssets',
+      ExpectOrigin: 'Option<StagingXcmV3MultiLocation>',
+      ExpectError: 'Option<(u32,StagingXcmV3TraitsError)>',
+      ExpectTransactStatus: 'StagingXcmV3MaybeErrorCode',
+      QueryPallet: {
+        moduleName: 'Bytes',
+        responseInfo: 'StagingXcmV3QueryResponseInfo',
+      },
+      ExpectPallet: {
+        index: 'Compact<u32>',
+        name: 'Bytes',
+        moduleName: 'Bytes',
+        crateMajor: 'Compact<u32>',
+        minCrateMinor: 'Compact<u32>',
+      },
+      ReportTransactStatus: 'StagingXcmV3QueryResponseInfo',
+      ClearTransactStatus: 'Null',
+      UniversalOrigin: 'StagingXcmV3Junction',
+      ExportMessage: {
+        network: 'StagingXcmV3JunctionNetworkId',
+        destination: 'StagingXcmV3Junctions',
+        xcm: 'StagingXcmV3Xcm',
+      },
+      LockAsset: {
+        asset: 'StagingXcmV3MultiAsset',
+        unlocker: 'StagingXcmV3MultiLocation',
+      },
+      UnlockAsset: {
+        asset: 'StagingXcmV3MultiAsset',
+        target: 'StagingXcmV3MultiLocation',
+      },
+      NoteUnlockable: {
+        asset: 'StagingXcmV3MultiAsset',
+        owner: 'StagingXcmV3MultiLocation',
+      },
+      RequestUnlock: {
+        asset: 'StagingXcmV3MultiAsset',
+        locker: 'StagingXcmV3MultiLocation',
+      },
+      SetFeesMode: {
+        jitWithdraw: 'bool',
+      },
+      SetTopic: '[u8;32]',
+      ClearTopic: 'Null',
+      AliasOrigin: 'StagingXcmV3MultiLocation',
+      UnpaidExecution: {
+        weightLimit: 'StagingXcmV3WeightLimit',
+        checkOrigin: 'Option<StagingXcmV3MultiLocation>'
+      }
+    }
+  },
+  /**
+   * Lookup242: staging_xcm::v3::Response
+   **/
+  StagingXcmV3Response: {
+    _enum: {
+      Null: 'Null',
+      Assets: 'StagingXcmV3MultiassetMultiAssets',
+      ExecutionResult: 'Option<(u32,StagingXcmV3TraitsError)>',
+      Version: 'u32',
+      PalletsInfo: 'Vec<StagingXcmV3PalletInfo>',
+      DispatchResult: 'StagingXcmV3MaybeErrorCode'
+    }
+  },
+  /**
+   * Lookup245: staging_xcm::v3::traits::Error
+   **/
+  StagingXcmV3TraitsError: {
+    _enum: {
+      Overflow: 'Null',
+      Unimplemented: 'Null',
+      UntrustedReserveLocation: 'Null',
+      UntrustedTeleportLocation: 'Null',
+      LocationFull: 'Null',
+      LocationNotInvertible: 'Null',
+      BadOrigin: 'Null',
+      InvalidLocation: 'Null',
+      AssetNotFound: 'Null',
+      FailedToTransactAsset: 'Null',
+      NotWithdrawable: 'Null',
+      LocationCannotHold: 'Null',
+      ExceedsMaxMessageSize: 'Null',
+      DestinationUnsupported: 'Null',
+      Transport: 'Null',
+      Unroutable: 'Null',
+      UnknownClaim: 'Null',
+      FailedToDecode: 'Null',
+      MaxWeightInvalid: 'Null',
+      NotHoldingFees: 'Null',
+      TooExpensive: 'Null',
+      Trap: 'u64',
+      ExpectationFalse: 'Null',
+      PalletNotFound: 'Null',
+      NameMismatch: 'Null',
+      VersionIncompatible: 'Null',
+      HoldingWouldOverflow: 'Null',
+      ExportError: 'Null',
+      ReanchorFailed: 'Null',
+      NoDeal: 'Null',
+      FeesNotMet: 'Null',
+      LockError: 'Null',
+      NoPermission: 'Null',
+      Unanchored: 'Null',
+      NotDepositable: 'Null',
+      UnhandledXcmVersion: 'Null',
+      WeightLimitReached: 'SpWeightsWeightV2Weight',
+      Barrier: 'Null',
+      WeightNotComputable: 'Null',
+      ExceedsStackLimit: 'Null'
+    }
+  },
+  /**
+   * Lookup247: staging_xcm::v3::PalletInfo
+   **/
+  StagingXcmV3PalletInfo: {
+    index: 'Compact<u32>',
+    name: 'Bytes',
+    moduleName: 'Bytes',
+    major: 'Compact<u32>',
+    minor: 'Compact<u32>',
+    patch: 'Compact<u32>'
+  },
+  /**
+   * Lookup250: staging_xcm::v3::MaybeErrorCode
+   **/
+  StagingXcmV3MaybeErrorCode: {
+    _enum: {
+      Success: 'Null',
+      Error: 'Bytes',
+      TruncatedError: 'Bytes'
+    }
+  },
+  /**
+   * Lookup253: staging_xcm::v3::QueryResponseInfo
+   **/
+  StagingXcmV3QueryResponseInfo: {
+    destination: 'StagingXcmV3MultiLocation',
+    queryId: 'Compact<u64>',
+    maxWeight: 'SpWeightsWeightV2Weight'
+  },
+  /**
+   * Lookup254: staging_xcm::v3::multiasset::MultiAssetFilter
+   **/
+  StagingXcmV3MultiassetMultiAssetFilter: {
+    _enum: {
+      Definite: 'StagingXcmV3MultiassetMultiAssets',
+      Wild: 'StagingXcmV3MultiassetWildMultiAsset'
+    }
+  },
+  /**
+   * Lookup255: staging_xcm::v3::multiasset::WildMultiAsset
+   **/
+  StagingXcmV3MultiassetWildMultiAsset: {
+    _enum: {
+      All: 'Null',
+      AllOf: {
+        id: 'StagingXcmV3MultiassetAssetId',
+        fun: 'StagingXcmV3MultiassetWildFungibility',
+      },
+      AllCounted: 'Compact<u32>',
+      AllOfCounted: {
+        id: 'StagingXcmV3MultiassetAssetId',
+        fun: 'StagingXcmV3MultiassetWildFungibility',
+        count: 'Compact<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup256: staging_xcm::v3::multiasset::WildFungibility
+   **/
+  StagingXcmV3MultiassetWildFungibility: {
+    _enum: ['Fungible', 'NonFungible']
+  },
+  /**
+   * Lookup265: cumulus_pallet_xcm::pallet::Call<T>
+   **/
+  CumulusPalletXcmCall: 'Null',
+  /**
+   * Lookup266: cumulus_pallet_dmp_queue::pallet::Call<T>
+   **/
+  CumulusPalletDmpQueueCall: {
+    _enum: {
+      service_overweight: {
+        index: 'u64',
+        weightLimit: 'SpWeightsWeightV2Weight'
+      }
+    }
+  },
+  /**
+   * Lookup267: pallet_inflation::pallet::Call<T>
+   **/
+  PalletInflationCall: {
+    _enum: {
+      start_inflation: {
+        inflationStartRelayBlock: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup268: pallet_unique::pallet::Call<T>
+   **/
+  PalletUniqueCall: {
+    _enum: {
+      create_collection: {
+        collectionName: 'Vec<u16>',
+        collectionDescription: 'Vec<u16>',
+        tokenPrefix: 'Bytes',
+        mode: 'UpDataStructsCollectionMode',
+      },
+      create_collection_ex: {
+        data: 'UpDataStructsCreateCollectionData',
+      },
+      destroy_collection: {
+        collectionId: 'u32',
+      },
+      add_to_allow_list: {
+        collectionId: 'u32',
+        address: 'PalletEvmAccountBasicCrossAccountIdRepr',
+      },
+      remove_from_allow_list: {
+        collectionId: 'u32',
+        address: 'PalletEvmAccountBasicCrossAccountIdRepr',
+      },
+      change_collection_owner: {
+        collectionId: 'u32',
+        newOwner: 'AccountId32',
+      },
+      add_collection_admin: {
+        collectionId: 'u32',
+        newAdminId: 'PalletEvmAccountBasicCrossAccountIdRepr',
+      },
+      remove_collection_admin: {
+        collectionId: 'u32',
+        accountId: 'PalletEvmAccountBasicCrossAccountIdRepr',
+      },
+      set_collection_sponsor: {
+        collectionId: 'u32',
+        newSponsor: 'AccountId32',
+      },
+      confirm_sponsorship: {
+        collectionId: 'u32',
+      },
+      remove_collection_sponsor: {
+        collectionId: 'u32',
+      },
+      create_item: {
+        collectionId: 'u32',
+        owner: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        data: 'UpDataStructsCreateItemData',
+      },
+      create_multiple_items: {
+        collectionId: 'u32',
+        owner: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        itemsData: 'Vec<UpDataStructsCreateItemData>',
+      },
+      set_collection_properties: {
+        collectionId: 'u32',
+        properties: 'Vec<UpDataStructsProperty>',
+      },
+      delete_collection_properties: {
+        collectionId: 'u32',
+        propertyKeys: 'Vec<Bytes>',
+      },
+      set_token_properties: {
+        collectionId: 'u32',
+        tokenId: 'u32',
+        properties: 'Vec<UpDataStructsProperty>',
+      },
+      delete_token_properties: {
+        collectionId: 'u32',
+        tokenId: 'u32',
+        propertyKeys: 'Vec<Bytes>',
+      },
+      set_token_property_permissions: {
+        collectionId: 'u32',
+        propertyPermissions: 'Vec<UpDataStructsPropertyKeyPermission>',
+      },
+      create_multiple_items_ex: {
+        collectionId: 'u32',
+        data: 'UpDataStructsCreateItemExData',
+      },
+      set_transfers_enabled_flag: {
+        collectionId: 'u32',
+        value: 'bool',
+      },
+      burn_item: {
+        collectionId: 'u32',
+        itemId: 'u32',
+        value: 'u128',
+      },
+      burn_from: {
+        collectionId: 'u32',
+        from: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        itemId: 'u32',
+        value: 'u128',
+      },
+      transfer: {
+        recipient: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        collectionId: 'u32',
+        itemId: 'u32',
+        value: 'u128',
+      },
+      approve: {
+        spender: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        collectionId: 'u32',
+        itemId: 'u32',
+        amount: 'u128',
+      },
+      approve_from: {
+        from: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        to: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        collectionId: 'u32',
+        itemId: 'u32',
+        amount: 'u128',
+      },
+      transfer_from: {
+        from: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        recipient: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        collectionId: 'u32',
+        itemId: 'u32',
+        value: 'u128',
+      },
+      set_collection_limits: {
+        collectionId: 'u32',
+        newLimit: 'UpDataStructsCollectionLimits',
+      },
+      set_collection_permissions: {
+        collectionId: 'u32',
+        newPermission: 'UpDataStructsCollectionPermissions',
+      },
+      repartition: {
+        collectionId: 'u32',
+        tokenId: 'u32',
+        amount: 'u128',
+      },
+      set_allowance_for_all: {
+        collectionId: 'u32',
+        operator: 'PalletEvmAccountBasicCrossAccountIdRepr',
+        approve: 'bool',
+      },
+      force_repair_collection: {
+        collectionId: 'u32',
+      },
+      force_repair_item: {
+        collectionId: 'u32',
+        itemId: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup273: up_data_structs::CollectionMode
+   **/
+  UpDataStructsCollectionMode: {
+    _enum: {
+      NFT: 'Null',
+      Fungible: 'u8',
+      ReFungible: 'Null'
+    }
+  },
+  /**
+   * Lookup274: up_data_structs::CreateCollectionData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsCreateCollectionData: {
+    mode: 'UpDataStructsCollectionMode',
+    access: 'Option<UpDataStructsAccessMode>',
+    name: 'Vec<u16>',
+    description: 'Vec<u16>',
+    tokenPrefix: 'Bytes',
+    limits: 'Option<UpDataStructsCollectionLimits>',
+    permissions: 'Option<UpDataStructsCollectionPermissions>',
+    tokenPropertyPermissions: 'Vec<UpDataStructsPropertyKeyPermission>',
+    properties: 'Vec<UpDataStructsProperty>',
+    adminList: 'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
+    pendingSponsor: 'Option<PalletEvmAccountBasicCrossAccountIdRepr>',
+    flags: '[u8;1]'
+  },
+  /**
+   * Lookup275: pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>
+   **/
+  PalletEvmAccountBasicCrossAccountIdRepr: {
+    _enum: {
+      Substrate: 'AccountId32',
+      Ethereum: 'H160'
+    }
+  },
+  /**
+   * Lookup277: up_data_structs::AccessMode
+   **/
+  UpDataStructsAccessMode: {
+    _enum: ['Normal', 'AllowList']
+  },
+  /**
+   * Lookup279: up_data_structs::CollectionLimits
+   **/
+  UpDataStructsCollectionLimits: {
+    accountTokenOwnershipLimit: 'Option<u32>',
+    sponsoredDataSize: 'Option<u32>',
+    sponsoredDataRateLimit: 'Option<UpDataStructsSponsoringRateLimit>',
+    tokenLimit: 'Option<u32>',
+    sponsorTransferTimeout: 'Option<u32>',
+    sponsorApproveTimeout: 'Option<u32>',
+    ownerCanTransfer: 'Option<bool>',
+    ownerCanDestroy: 'Option<bool>',
+    transfersEnabled: 'Option<bool>'
+  },
+  /**
+   * Lookup281: up_data_structs::SponsoringRateLimit
+   **/
+  UpDataStructsSponsoringRateLimit: {
+    _enum: {
+      SponsoringDisabled: 'Null',
+      Blocks: 'u32'
+    }
+  },
+  /**
+   * Lookup284: up_data_structs::CollectionPermissions
+   **/
+  UpDataStructsCollectionPermissions: {
+    access: 'Option<UpDataStructsAccessMode>',
+    mintMode: 'Option<bool>',
+    nesting: 'Option<UpDataStructsNestingPermissions>'
+  },
+  /**
+   * Lookup286: up_data_structs::NestingPermissions
+   **/
+  UpDataStructsNestingPermissions: {
+    tokenOwner: 'bool',
+    collectionAdmin: 'bool',
+    restricted: 'Option<UpDataStructsOwnerRestrictedSet>'
+  },
+  /**
+   * Lookup288: up_data_structs::OwnerRestrictedSet
+   **/
+  UpDataStructsOwnerRestrictedSet: 'BTreeSet<u32>',
+  /**
+   * Lookup294: up_data_structs::PropertyKeyPermission
+   **/
+  UpDataStructsPropertyKeyPermission: {
+    key: 'Bytes',
+    permission: 'UpDataStructsPropertyPermission'
+  },
+  /**
+   * Lookup296: up_data_structs::PropertyPermission
+   **/
+  UpDataStructsPropertyPermission: {
+    mutable: 'bool',
+    collectionAdmin: 'bool',
+    tokenOwner: 'bool'
+  },
+  /**
+   * Lookup299: up_data_structs::Property
+   **/
+  UpDataStructsProperty: {
+    key: 'Bytes',
+    value: 'Bytes'
+  },
+  /**
+   * Lookup304: up_data_structs::CreateItemData
+   **/
+  UpDataStructsCreateItemData: {
+    _enum: {
+      NFT: 'UpDataStructsCreateNftData',
+      Fungible: 'UpDataStructsCreateFungibleData',
+      ReFungible: 'UpDataStructsCreateReFungibleData'
+    }
+  },
+  /**
+   * Lookup305: up_data_structs::CreateNftData
+   **/
+  UpDataStructsCreateNftData: {
+    properties: 'Vec<UpDataStructsProperty>'
+  },
+  /**
+   * Lookup306: up_data_structs::CreateFungibleData
+   **/
+  UpDataStructsCreateFungibleData: {
+    value: 'u128'
+  },
+  /**
+   * Lookup307: up_data_structs::CreateReFungibleData
+   **/
+  UpDataStructsCreateReFungibleData: {
+    pieces: 'u128',
+    properties: 'Vec<UpDataStructsProperty>'
+  },
+  /**
+   * Lookup311: up_data_structs::CreateItemExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsCreateItemExData: {
+    _enum: {
+      NFT: 'Vec<UpDataStructsCreateNftExData>',
+      Fungible: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>',
+      RefungibleMultipleItems: 'Vec<UpDataStructsCreateRefungibleExSingleOwner>',
+      RefungibleMultipleOwners: 'UpDataStructsCreateRefungibleExMultipleOwners'
+    }
+  },
+  /**
+   * Lookup313: up_data_structs::CreateNftExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsCreateNftExData: {
+    properties: 'Vec<UpDataStructsProperty>',
+    owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
+  },
+  /**
+   * Lookup320: up_data_structs::CreateRefungibleExSingleOwner<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsCreateRefungibleExSingleOwner: {
+    user: 'PalletEvmAccountBasicCrossAccountIdRepr',
+    pieces: 'u128',
+    properties: 'Vec<UpDataStructsProperty>'
+  },
+  /**
+   * Lookup322: up_data_structs::CreateRefungibleExMultipleOwners<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsCreateRefungibleExMultipleOwners: {
+    users: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>',
+    properties: 'Vec<UpDataStructsProperty>'
+  },
+  /**
+   * Lookup323: pallet_configuration::pallet::Call<T>
+   **/
+  PalletConfigurationCall: {
+    _enum: {
+      set_weight_to_fee_coefficient_override: {
+        coeff: 'Option<u64>',
+      },
+      set_min_gas_price_override: {
+        coeff: 'Option<u64>',
+      },
+      __Unused2: 'Null',
+      set_app_promotion_configuration_override: {
+        configuration: 'PalletConfigurationAppPromotionConfiguration',
+      },
+      set_collator_selection_desired_collators: {
+        max: 'Option<u32>',
+      },
+      set_collator_selection_license_bond: {
+        amount: 'Option<u128>',
+      },
+      set_collator_selection_kick_threshold: {
+        threshold: 'Option<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup325: pallet_configuration::AppPromotionConfiguration<BlockNumber>
+   **/
+  PalletConfigurationAppPromotionConfiguration: {
+    recalculationInterval: 'Option<u32>',
+    pendingInterval: 'Option<u32>',
+    intervalIncome: 'Option<Perbill>',
+    maxStakersPerCalculation: 'Option<u8>'
+  },
+  /**
+   * Lookup330: pallet_structure::pallet::Call<T>
+   **/
+  PalletStructureCall: 'Null',
+  /**
+   * Lookup331: pallet_app_promotion::pallet::Call<T>
+   **/
+  PalletAppPromotionCall: {
+    _enum: {
+      set_admin_address: {
+        admin: 'PalletEvmAccountBasicCrossAccountIdRepr',
+      },
+      stake: {
+        amount: 'u128',
+      },
+      unstake_all: 'Null',
+      sponsor_collection: {
+        collectionId: 'u32',
+      },
+      stop_sponsoring_collection: {
+        collectionId: 'u32',
+      },
+      sponsor_contract: {
+        contractId: 'H160',
+      },
+      stop_sponsoring_contract: {
+        contractId: 'H160',
+      },
+      payout_stakers: {
+        stakersNumber: 'Option<u8>',
+      },
+      unstake_partial: {
+        amount: 'u128',
+      },
+      force_unstake: {
+        pendingBlocks: 'Vec<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup333: pallet_foreign_assets::module::Call<T>
+   **/
+  PalletForeignAssetsModuleCall: {
+    _enum: {
+      register_foreign_asset: {
+        owner: 'AccountId32',
+        location: 'StagingXcmVersionedMultiLocation',
+        metadata: 'PalletForeignAssetsModuleAssetMetadata',
+      },
+      update_foreign_asset: {
+        foreignAssetId: 'u32',
+        location: 'StagingXcmVersionedMultiLocation',
+        metadata: 'PalletForeignAssetsModuleAssetMetadata'
+      }
+    }
+  },
+  /**
+   * Lookup334: pallet_foreign_assets::module::AssetMetadata<Balance>
+   **/
+  PalletForeignAssetsModuleAssetMetadata: {
+    name: 'Bytes',
+    symbol: 'Bytes',
+    decimals: 'u8',
+    minimalBalance: 'u128'
+  },
+  /**
+   * Lookup337: pallet_evm::pallet::Call<T>
+   **/
+  PalletEvmCall: {
+    _enum: {
+      withdraw: {
+        address: 'H160',
+        value: 'u128',
+      },
+      call: {
+        source: 'H160',
+        target: 'H160',
+        input: 'Bytes',
+        value: 'U256',
+        gasLimit: 'u64',
+        maxFeePerGas: 'U256',
+        maxPriorityFeePerGas: 'Option<U256>',
+        nonce: 'Option<U256>',
+        accessList: 'Vec<(H160,Vec<H256>)>',
+      },
+      create: {
+        source: 'H160',
+        init: 'Bytes',
+        value: 'U256',
+        gasLimit: 'u64',
+        maxFeePerGas: 'U256',
+        maxPriorityFeePerGas: 'Option<U256>',
+        nonce: 'Option<U256>',
+        accessList: 'Vec<(H160,Vec<H256>)>',
+      },
+      create2: {
+        source: 'H160',
+        init: 'Bytes',
+        salt: 'H256',
+        value: 'U256',
+        gasLimit: 'u64',
+        maxFeePerGas: 'U256',
+        maxPriorityFeePerGas: 'Option<U256>',
+        nonce: 'Option<U256>',
+        accessList: 'Vec<(H160,Vec<H256>)>'
+      }
+    }
+  },
+  /**
+   * Lookup344: pallet_ethereum::pallet::Call<T>
+   **/
+  PalletEthereumCall: {
+    _enum: {
+      transact: {
+        transaction: 'EthereumTransactionTransactionV2'
+      }
+    }
+  },
+  /**
+   * Lookup345: ethereum::transaction::TransactionV2
+   **/
+  EthereumTransactionTransactionV2: {
+    _enum: {
+      Legacy: 'EthereumTransactionLegacyTransaction',
+      EIP2930: 'EthereumTransactionEip2930Transaction',
+      EIP1559: 'EthereumTransactionEip1559Transaction'
+    }
+  },
+  /**
+   * Lookup346: ethereum::transaction::LegacyTransaction
+   **/
+  EthereumTransactionLegacyTransaction: {
+    nonce: 'U256',
+    gasPrice: 'U256',
+    gasLimit: 'U256',
+    action: 'EthereumTransactionTransactionAction',
+    value: 'U256',
+    input: 'Bytes',
+    signature: 'EthereumTransactionTransactionSignature'
+  },
+  /**
+   * Lookup347: ethereum::transaction::TransactionAction
+   **/
+  EthereumTransactionTransactionAction: {
+    _enum: {
+      Call: 'H160',
+      Create: 'Null'
+    }
+  },
+  /**
+   * Lookup348: ethereum::transaction::TransactionSignature
+   **/
+  EthereumTransactionTransactionSignature: {
+    v: 'u64',
+    r: 'H256',
+    s: 'H256'
+  },
+  /**
+   * Lookup350: ethereum::transaction::EIP2930Transaction
+   **/
+  EthereumTransactionEip2930Transaction: {
+    chainId: 'u64',
+    nonce: 'U256',
+    gasPrice: 'U256',
+    gasLimit: 'U256',
+    action: 'EthereumTransactionTransactionAction',
+    value: 'U256',
+    input: 'Bytes',
+    accessList: 'Vec<EthereumTransactionAccessListItem>',
+    oddYParity: 'bool',
+    r: 'H256',
+    s: 'H256'
+  },
+  /**
+   * Lookup352: ethereum::transaction::AccessListItem
+   **/
+  EthereumTransactionAccessListItem: {
+    address: 'H160',
+    storageKeys: 'Vec<H256>'
+  },
+  /**
+   * Lookup353: ethereum::transaction::EIP1559Transaction
+   **/
+  EthereumTransactionEip1559Transaction: {
+    chainId: 'u64',
+    nonce: 'U256',
+    maxPriorityFeePerGas: 'U256',
+    maxFeePerGas: 'U256',
+    gasLimit: 'U256',
+    action: 'EthereumTransactionTransactionAction',
+    value: 'U256',
+    input: 'Bytes',
+    accessList: 'Vec<EthereumTransactionAccessListItem>',
+    oddYParity: 'bool',
+    r: 'H256',
+    s: 'H256'
+  },
+  /**
+   * Lookup354: pallet_evm_contract_helpers::pallet::Call<T>
+   **/
+  PalletEvmContractHelpersCall: {
+    _enum: {
+      migrate_from_self_sponsoring: {
+        addresses: 'Vec<H160>'
+      }
+    }
+  },
+  /**
+   * Lookup356: pallet_evm_migration::pallet::Call<T>
+   **/
+  PalletEvmMigrationCall: {
+    _enum: {
+      begin: {
+        address: 'H160',
+      },
+      set_data: {
+        address: 'H160',
+        data: 'Vec<(H256,H256)>',
+      },
+      finish: {
+        address: 'H160',
+        code: 'Bytes',
+      },
+      insert_eth_logs: {
+        logs: 'Vec<EthereumLog>',
+      },
+      insert_events: {
+        events: 'Vec<Bytes>',
+      },
+      remove_rmrk_data: 'Null'
+    }
+  },
+  /**
+   * Lookup360: ethereum::log::Log
+   **/
+  EthereumLog: {
+    address: 'H160',
+    topics: 'Vec<H256>',
+    data: 'Bytes'
+  },
+  /**
+   * Lookup361: pallet_maintenance::pallet::Call<T>
+   **/
+  PalletMaintenanceCall: {
+    _enum: ['enable', 'disable']
+  },
+  /**
+   * Lookup362: pallet_utility::pallet::Call<T>
+   **/
+  PalletUtilityCall: {
+    _enum: {
+      batch: {
+        calls: 'Vec<Call>',
+      },
+      as_derivative: {
+        index: 'u16',
+        call: 'Call',
+      },
+      batch_all: {
+        calls: 'Vec<Call>',
+      },
+      dispatch_as: {
+        asOrigin: 'OpalRuntimeOriginCaller',
+        call: 'Call',
+      },
+      force_batch: {
+        calls: 'Vec<Call>',
+      },
+      with_weight: {
+        call: 'Call',
+        weight: 'SpWeightsWeightV2Weight'
+      }
+    }
+  },
+  /**
+   * Lookup364: pallet_test_utils::pallet::Call<T>
+   **/
+  PalletTestUtilsCall: {
+    _enum: {
+      enable: 'Null',
+      set_test_value: {
+        value: 'u32',
+      },
+      set_test_value_and_rollback: {
+        value: 'u32',
+      },
+      inc_test_value: 'Null',
+      just_take_fee: 'Null',
+      batch_all: {
+        calls: 'Vec<Call>'
+      }
+    }
+  },
+  /**
+   * Lookup366: pallet_scheduler::pallet::Event<T>
+   **/
+  PalletSchedulerEvent: {
+    _enum: {
+      Scheduled: {
+        when: 'u32',
+        index: 'u32',
+      },
+      Canceled: {
+        when: 'u32',
+        index: 'u32',
+      },
+      Dispatched: {
+        task: '(u32,u32)',
+        id: 'Option<[u8;32]>',
+        result: 'Result<Null, SpRuntimeDispatchError>',
+      },
+      CallUnavailable: {
+        task: '(u32,u32)',
+        id: 'Option<[u8;32]>',
+      },
+      PeriodicFailed: {
+        task: '(u32,u32)',
+        id: 'Option<[u8;32]>',
+      },
+      PermanentlyOverweight: {
+        task: '(u32,u32)',
+        id: 'Option<[u8;32]>'
+      }
+    }
+  },
+  /**
+   * Lookup367: cumulus_pallet_xcmp_queue::pallet::Event<T>
+   **/
+  CumulusPalletXcmpQueueEvent: {
+    _enum: {
+      Success: {
+        messageHash: '[u8;32]',
+        messageId: '[u8;32]',
+        weight: 'SpWeightsWeightV2Weight',
+      },
+      Fail: {
+        messageHash: '[u8;32]',
+        messageId: '[u8;32]',
+        error: 'StagingXcmV3TraitsError',
+        weight: 'SpWeightsWeightV2Weight',
+      },
+      BadVersion: {
+        messageHash: '[u8;32]',
+      },
+      BadFormat: {
+        messageHash: '[u8;32]',
+      },
+      XcmpMessageSent: {
+        messageHash: '[u8;32]',
+      },
+      OverweightEnqueued: {
+        sender: 'u32',
+        sentAt: 'u32',
+        index: 'u64',
+        required: 'SpWeightsWeightV2Weight',
+      },
+      OverweightServiced: {
+        index: 'u64',
+        used: 'SpWeightsWeightV2Weight'
+      }
+    }
+  },
+  /**
+   * Lookup368: pallet_xcm::pallet::Event<T>
+   **/
+  PalletXcmEvent: {
+    _enum: {
+      Attempted: {
+        outcome: 'StagingXcmV3TraitsOutcome',
+      },
+      Sent: {
+        origin: 'StagingXcmV3MultiLocation',
+        destination: 'StagingXcmV3MultiLocation',
+        message: 'StagingXcmV3Xcm',
+        messageId: '[u8;32]',
+      },
+      UnexpectedResponse: {
+        origin: 'StagingXcmV3MultiLocation',
+        queryId: 'u64',
+      },
+      ResponseReady: {
+        queryId: 'u64',
+        response: 'StagingXcmV3Response',
+      },
+      Notified: {
+        queryId: 'u64',
+        palletIndex: 'u8',
+        callIndex: 'u8',
+      },
+      NotifyOverweight: {
+        queryId: 'u64',
+        palletIndex: 'u8',
+        callIndex: 'u8',
+        actualWeight: 'SpWeightsWeightV2Weight',
+        maxBudgetedWeight: 'SpWeightsWeightV2Weight',
+      },
+      NotifyDispatchError: {
+        queryId: 'u64',
+        palletIndex: 'u8',
+        callIndex: 'u8',
+      },
+      NotifyDecodeFailed: {
+        queryId: 'u64',
+        palletIndex: 'u8',
+        callIndex: 'u8',
+      },
+      InvalidResponder: {
+        origin: 'StagingXcmV3MultiLocation',
+        queryId: 'u64',
+        expectedLocation: 'Option<StagingXcmV3MultiLocation>',
+      },
+      InvalidResponderVersion: {
+        origin: 'StagingXcmV3MultiLocation',
+        queryId: 'u64',
+      },
+      ResponseTaken: {
+        queryId: 'u64',
+      },
+      AssetsTrapped: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+        origin: 'StagingXcmV3MultiLocation',
+        assets: 'StagingXcmVersionedMultiAssets',
+      },
+      VersionChangeNotified: {
+        destination: 'StagingXcmV3MultiLocation',
+        result: 'u32',
+        cost: 'StagingXcmV3MultiassetMultiAssets',
+        messageId: '[u8;32]',
+      },
+      SupportedVersionChanged: {
+        location: 'StagingXcmV3MultiLocation',
+        version: 'u32',
+      },
+      NotifyTargetSendFail: {
+        location: 'StagingXcmV3MultiLocation',
+        queryId: 'u64',
+        error: 'StagingXcmV3TraitsError',
+      },
+      NotifyTargetMigrationFail: {
+        location: 'StagingXcmVersionedMultiLocation',
+        queryId: 'u64',
+      },
+      InvalidQuerierVersion: {
+        origin: 'StagingXcmV3MultiLocation',
+        queryId: 'u64',
+      },
+      InvalidQuerier: {
+        origin: 'StagingXcmV3MultiLocation',
+        queryId: 'u64',
+        expectedQuerier: 'StagingXcmV3MultiLocation',
+        maybeActualQuerier: 'Option<StagingXcmV3MultiLocation>',
+      },
+      VersionNotifyStarted: {
+        destination: 'StagingXcmV3MultiLocation',
+        cost: 'StagingXcmV3MultiassetMultiAssets',
+        messageId: '[u8;32]',
+      },
+      VersionNotifyRequested: {
+        destination: 'StagingXcmV3MultiLocation',
+        cost: 'StagingXcmV3MultiassetMultiAssets',
+        messageId: '[u8;32]',
+      },
+      VersionNotifyUnrequested: {
+        destination: 'StagingXcmV3MultiLocation',
+        cost: 'StagingXcmV3MultiassetMultiAssets',
+        messageId: '[u8;32]',
+      },
+      FeesPaid: {
+        paying: 'StagingXcmV3MultiLocation',
+        fees: 'StagingXcmV3MultiassetMultiAssets',
+      },
+      AssetsClaimed: {
+        _alias: {
+          hash_: 'hash',
+        },
+        hash_: 'H256',
+        origin: 'StagingXcmV3MultiLocation',
+        assets: 'StagingXcmVersionedMultiAssets'
+      }
+    }
+  },
+  /**
+   * Lookup369: staging_xcm::v3::traits::Outcome
+   **/
+  StagingXcmV3TraitsOutcome: {
+    _enum: {
+      Complete: 'SpWeightsWeightV2Weight',
+      Incomplete: '(SpWeightsWeightV2Weight,StagingXcmV3TraitsError)',
+      Error: 'StagingXcmV3TraitsError'
+    }
+  },
+  /**
+   * Lookup370: cumulus_pallet_xcm::pallet::Event<T>
+   **/
+  CumulusPalletXcmEvent: {
+    _enum: {
+      InvalidFormat: '[u8;32]',
+      UnsupportedVersion: '[u8;32]',
+      ExecutedDownward: '([u8;32],StagingXcmV3TraitsOutcome)'
+    }
+  },
+  /**
+   * Lookup371: cumulus_pallet_dmp_queue::pallet::Event<T>
+   **/
+  CumulusPalletDmpQueueEvent: {
+    _enum: {
+      InvalidFormat: {
+        messageHash: '[u8;32]',
+      },
+      UnsupportedVersion: {
+        messageHash: '[u8;32]',
+      },
+      ExecutedDownward: {
+        messageHash: '[u8;32]',
+        messageId: '[u8;32]',
+        outcome: 'StagingXcmV3TraitsOutcome',
+      },
+      WeightExhausted: {
+        messageHash: '[u8;32]',
+        messageId: '[u8;32]',
+        remainingWeight: 'SpWeightsWeightV2Weight',
+        requiredWeight: 'SpWeightsWeightV2Weight',
+      },
+      OverweightEnqueued: {
+        messageHash: '[u8;32]',
+        messageId: '[u8;32]',
+        overweightIndex: 'u64',
+        requiredWeight: 'SpWeightsWeightV2Weight',
+      },
+      OverweightServiced: {
+        overweightIndex: 'u64',
+        weightUsed: 'SpWeightsWeightV2Weight',
+      },
+      MaxMessagesExhausted: {
+        messageHash: '[u8;32]'
+      }
+    }
+  },
+  /**
+   * Lookup372: pallet_configuration::pallet::Event<T>
+   **/
+  PalletConfigurationEvent: {
+    _enum: {
+      NewDesiredCollators: {
+        desiredCollators: 'Option<u32>',
+      },
+      NewCollatorLicenseBond: {
+        bondCost: 'Option<u128>',
+      },
+      NewCollatorKickThreshold: {
+        lengthInBlocks: 'Option<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup373: pallet_common::pallet::Event<T>
+   **/
+  PalletCommonEvent: {
+    _enum: {
+      CollectionCreated: '(u32,u8,AccountId32)',
+      CollectionDestroyed: 'u32',
+      ItemCreated: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
+      ItemDestroyed: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
+      Transfer: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
+      Approved: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
+      ApprovedForAll: '(u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,bool)',
+      CollectionPropertySet: '(u32,Bytes)',
+      CollectionPropertyDeleted: '(u32,Bytes)',
+      TokenPropertySet: '(u32,u32,Bytes)',
+      TokenPropertyDeleted: '(u32,u32,Bytes)',
+      PropertyPermissionSet: '(u32,Bytes)',
+      AllowListAddressAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
+      AllowListAddressRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
+      CollectionAdminAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
+      CollectionAdminRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
+      CollectionLimitSet: 'u32',
+      CollectionOwnerChanged: '(u32,AccountId32)',
+      CollectionPermissionSet: 'u32',
+      CollectionSponsorSet: '(u32,AccountId32)',
+      SponsorshipConfirmed: '(u32,AccountId32)',
+      CollectionSponsorRemoved: 'u32'
+    }
+  },
+  /**
+   * Lookup374: pallet_structure::pallet::Event<T>
+   **/
+  PalletStructureEvent: {
+    _enum: {
+      Executed: 'Result<Null, SpRuntimeDispatchError>'
+    }
+  },
+  /**
+   * Lookup375: pallet_app_promotion::pallet::Event<T>
+   **/
+  PalletAppPromotionEvent: {
+    _enum: {
+      StakingRecalculation: '(AccountId32,u128,u128)',
+      Stake: '(AccountId32,u128)',
+      Unstake: '(AccountId32,u128)',
+      SetAdmin: 'AccountId32'
+    }
+  },
+  /**
+   * Lookup376: pallet_foreign_assets::module::Event<T>
+   **/
+  PalletForeignAssetsModuleEvent: {
+    _enum: {
+      ForeignAssetRegistered: {
+        assetId: 'u32',
+        assetAddress: 'StagingXcmV3MultiLocation',
+        metadata: 'PalletForeignAssetsModuleAssetMetadata',
+      },
+      ForeignAssetUpdated: {
+        assetId: 'u32',
+        assetAddress: 'StagingXcmV3MultiLocation',
+        metadata: 'PalletForeignAssetsModuleAssetMetadata',
+      },
+      AssetRegistered: {
+        assetId: 'PalletForeignAssetsAssetId',
+        metadata: 'PalletForeignAssetsModuleAssetMetadata',
+      },
+      AssetUpdated: {
+        assetId: 'PalletForeignAssetsAssetId',
+        metadata: 'PalletForeignAssetsModuleAssetMetadata'
+      }
+    }
+  },
+  /**
+   * Lookup377: pallet_evm::pallet::Event<T>
+   **/
+  PalletEvmEvent: {
+    _enum: {
+      Log: {
+        log: 'EthereumLog',
+      },
+      Created: {
+        address: 'H160',
+      },
+      CreatedFailed: {
+        address: 'H160',
+      },
+      Executed: {
+        address: 'H160',
+      },
+      ExecutedFailed: {
+        address: 'H160'
+      }
+    }
+  },
+  /**
+   * Lookup378: pallet_ethereum::pallet::Event
+   **/
+  PalletEthereumEvent: {
+    _enum: {
+      Executed: {
+        from: 'H160',
+        to: 'H160',
+        transactionHash: 'H256',
+        exitReason: 'EvmCoreErrorExitReason',
+        extraData: 'Bytes'
+      }
+    }
+  },
+  /**
+   * Lookup379: evm_core::error::ExitReason
+   **/
+  EvmCoreErrorExitReason: {
+    _enum: {
+      Succeed: 'EvmCoreErrorExitSucceed',
+      Error: 'EvmCoreErrorExitError',
+      Revert: 'EvmCoreErrorExitRevert',
+      Fatal: 'EvmCoreErrorExitFatal'
+    }
+  },
+  /**
+   * Lookup380: evm_core::error::ExitSucceed
+   **/
+  EvmCoreErrorExitSucceed: {
+    _enum: ['Stopped', 'Returned', 'Suicided']
+  },
+  /**
+   * Lookup381: evm_core::error::ExitError
+   **/
+  EvmCoreErrorExitError: {
+    _enum: {
+      StackUnderflow: 'Null',
+      StackOverflow: 'Null',
+      InvalidJump: 'Null',
+      InvalidRange: 'Null',
+      DesignatedInvalid: 'Null',
+      CallTooDeep: 'Null',
+      CreateCollision: 'Null',
+      CreateContractLimit: 'Null',
+      OutOfOffset: 'Null',
+      OutOfGas: 'Null',
+      OutOfFund: 'Null',
+      PCUnderflow: 'Null',
+      CreateEmpty: 'Null',
+      Other: 'Text',
+      MaxNonce: 'Null',
+      InvalidCode: 'u8'
+    }
+  },
+  /**
+   * Lookup385: evm_core::error::ExitRevert
+   **/
+  EvmCoreErrorExitRevert: {
+    _enum: ['Reverted']
+  },
+  /**
+   * Lookup386: evm_core::error::ExitFatal
+   **/
+  EvmCoreErrorExitFatal: {
+    _enum: {
+      NotSupported: 'Null',
+      UnhandledInterrupt: 'Null',
+      CallErrorAsFatal: 'EvmCoreErrorExitError',
+      Other: 'Text'
+    }
+  },
+  /**
+   * Lookup387: pallet_evm_contract_helpers::pallet::Event<T>
+   **/
+  PalletEvmContractHelpersEvent: {
+    _enum: {
+      ContractSponsorSet: '(H160,AccountId32)',
+      ContractSponsorshipConfirmed: '(H160,AccountId32)',
+      ContractSponsorRemoved: 'H160'
+    }
+  },
+  /**
+   * Lookup388: pallet_evm_migration::pallet::Event<T>
+   **/
+  PalletEvmMigrationEvent: {
+    _enum: ['TestEvent']
+  },
+  /**
+   * Lookup389: pallet_maintenance::pallet::Event<T>
+   **/
+  PalletMaintenanceEvent: {
+    _enum: ['MaintenanceEnabled', 'MaintenanceDisabled']
+  },
+  /**
+   * Lookup390: pallet_utility::pallet::Event
+   **/
+  PalletUtilityEvent: {
+    _enum: {
+      BatchInterrupted: {
+        index: 'u32',
+        error: 'SpRuntimeDispatchError',
+      },
+      BatchCompleted: 'Null',
+      BatchCompletedWithErrors: 'Null',
+      ItemCompleted: 'Null',
+      ItemFailed: {
+        error: 'SpRuntimeDispatchError',
+      },
+      DispatchedAs: {
+        result: 'Result<Null, SpRuntimeDispatchError>'
+      }
+    }
+  },
+  /**
+   * Lookup391: pallet_test_utils::pallet::Event<T>
+   **/
+  PalletTestUtilsEvent: {
+    _enum: ['ValueIsSet', 'ShouldRollback', 'BatchCompleted']
+  },
+  /**
+   * Lookup392: frame_system::Phase
+   **/
+  FrameSystemPhase: {
+    _enum: {
+      ApplyExtrinsic: 'u32',
+      Finalization: 'Null',
+      Initialization: 'Null'
+    }
+  },
+  /**
+   * Lookup394: frame_system::LastRuntimeUpgradeInfo
+   **/
+  FrameSystemLastRuntimeUpgradeInfo: {
+    specVersion: 'Compact<u32>',
+    specName: 'Text'
+  },
+  /**
+   * Lookup395: frame_system::limits::BlockWeights
+   **/
+  FrameSystemLimitsBlockWeights: {
+    baseBlock: 'SpWeightsWeightV2Weight',
+    maxBlock: 'SpWeightsWeightV2Weight',
+    perClass: 'FrameSupportDispatchPerDispatchClassWeightsPerClass'
+  },
+  /**
+   * Lookup396: frame_support::dispatch::PerDispatchClass<frame_system::limits::WeightsPerClass>
+   **/
+  FrameSupportDispatchPerDispatchClassWeightsPerClass: {
+    normal: 'FrameSystemLimitsWeightsPerClass',
+    operational: 'FrameSystemLimitsWeightsPerClass',
+    mandatory: 'FrameSystemLimitsWeightsPerClass'
+  },
+  /**
+   * Lookup397: frame_system::limits::WeightsPerClass
+   **/
+  FrameSystemLimitsWeightsPerClass: {
+    baseExtrinsic: 'SpWeightsWeightV2Weight',
+    maxExtrinsic: 'Option<SpWeightsWeightV2Weight>',
+    maxTotal: 'Option<SpWeightsWeightV2Weight>',
+    reserved: 'Option<SpWeightsWeightV2Weight>'
+  },
+  /**
+   * Lookup399: frame_system::limits::BlockLength
+   **/
+  FrameSystemLimitsBlockLength: {
+    max: 'FrameSupportDispatchPerDispatchClassU32'
+  },
+  /**
+   * Lookup400: frame_support::dispatch::PerDispatchClass<T>
+   **/
+  FrameSupportDispatchPerDispatchClassU32: {
+    normal: 'u32',
+    operational: 'u32',
+    mandatory: 'u32'
+  },
+  /**
+   * Lookup401: sp_weights::RuntimeDbWeight
+   **/
+  SpWeightsRuntimeDbWeight: {
+    read: 'u64',
+    write: 'u64'
+  },
+  /**
+   * Lookup402: sp_version::RuntimeVersion
+   **/
+  SpVersionRuntimeVersion: {
+    specName: 'Text',
+    implName: 'Text',
+    authoringVersion: 'u32',
+    specVersion: 'u32',
+    implVersion: 'u32',
+    apis: 'Vec<([u8;8],u32)>',
+    transactionVersion: 'u32',
+    stateVersion: 'u8'
+  },
+  /**
+   * Lookup406: frame_system::pallet::Error<T>
+   **/
+  FrameSystemError: {
+    _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered']
+  },
+  /**
+   * Lookup408: cumulus_pallet_parachain_system::unincluded_segment::Ancestor<primitive_types::H256>
+   **/
+  CumulusPalletParachainSystemUnincludedSegmentAncestor: {
+    usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth',
+    paraHeadHash: 'Option<H256>',
+    consumedGoAheadSignal: 'Option<PolkadotPrimitivesV5UpgradeGoAhead>'
+  },
+  /**
+   * Lookup409: cumulus_pallet_parachain_system::unincluded_segment::UsedBandwidth
+   **/
+  CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: {
+    umpMsgCount: 'u32',
+    umpTotalBytes: 'u32',
+    hrmpOutgoing: 'BTreeMap<u32, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate>'
+  },
+  /**
+   * Lookup411: cumulus_pallet_parachain_system::unincluded_segment::HrmpChannelUpdate
+   **/
+  CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: {
+    msgCount: 'u32',
+    totalBytes: 'u32'
+  },
+  /**
+   * Lookup415: polkadot_primitives::v5::UpgradeGoAhead
+   **/
+  PolkadotPrimitivesV5UpgradeGoAhead: {
+    _enum: ['Abort', 'GoAhead']
+  },
+  /**
+   * Lookup416: cumulus_pallet_parachain_system::unincluded_segment::SegmentTracker<primitive_types::H256>
+   **/
+  CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: {
+    usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth',
+    hrmpWatermark: 'Option<u32>',
+    consumedGoAheadSignal: 'Option<PolkadotPrimitivesV5UpgradeGoAhead>'
+  },
+  /**
+   * Lookup418: polkadot_primitives::v5::UpgradeRestriction
+   **/
+  PolkadotPrimitivesV5UpgradeRestriction: {
+    _enum: ['Present']
+  },
+  /**
+   * Lookup419: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot
+   **/
+  CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: {
+    dmqMqcHead: 'H256',
+    relayDispatchQueueRemainingCapacity: 'CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity',
+    ingressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>',
+    egressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>'
+  },
+  /**
+   * Lookup420: cumulus_pallet_parachain_system::relay_state_snapshot::RelayDispatchQueueRemainingCapacity
+   **/
+  CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: {
+    remainingCount: 'u32',
+    remainingSize: 'u32'
+  },
+  /**
+   * Lookup423: polkadot_primitives::v5::AbridgedHrmpChannel
+   **/
+  PolkadotPrimitivesV5AbridgedHrmpChannel: {
+    maxCapacity: 'u32',
+    maxTotalSize: 'u32',
+    maxMessageSize: 'u32',
+    msgCount: 'u32',
+    totalSize: 'u32',
+    mqcHead: 'Option<H256>'
+  },
+  /**
+   * Lookup424: polkadot_primitives::v5::AbridgedHostConfiguration
+   **/
+  PolkadotPrimitivesV5AbridgedHostConfiguration: {
+    maxCodeSize: 'u32',
+    maxHeadDataSize: 'u32',
+    maxUpwardQueueCount: 'u32',
+    maxUpwardQueueSize: 'u32',
+    maxUpwardMessageSize: 'u32',
+    maxUpwardMessageNumPerCandidate: 'u32',
+    hrmpMaxMessageNumPerCandidate: 'u32',
+    validationUpgradeCooldown: 'u32',
+    validationUpgradeDelay: 'u32',
+    asyncBackingParams: 'PolkadotPrimitivesVstagingAsyncBackingParams'
+  },
+  /**
+   * Lookup425: polkadot_primitives::vstaging::AsyncBackingParams
+   **/
+  PolkadotPrimitivesVstagingAsyncBackingParams: {
+    maxCandidateDepth: 'u32',
+    allowedAncestryLen: 'u32'
+  },
+  /**
+   * Lookup431: polkadot_core_primitives::OutboundHrmpMessage<polkadot_parachain_primitives::primitives::Id>
+   **/
+  PolkadotCorePrimitivesOutboundHrmpMessage: {
+    recipient: 'u32',
+    data: 'Bytes'
+  },
+  /**
+   * Lookup432: cumulus_pallet_parachain_system::CodeUpgradeAuthorization<T>
+   **/
+  CumulusPalletParachainSystemCodeUpgradeAuthorization: {
+    codeHash: 'H256',
+    checkVersion: 'bool'
+  },
+  /**
+   * Lookup433: cumulus_pallet_parachain_system::pallet::Error<T>
+   **/
+  CumulusPalletParachainSystemError: {
+    _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized']
+  },
+  /**
+   * Lookup435: pallet_collator_selection::pallet::Error<T>
+   **/
+  PalletCollatorSelectionError: {
+    _enum: ['TooManyCandidates', 'Unknown', 'Permission', 'AlreadyHoldingLicense', 'NoLicense', 'AlreadyCandidate', 'NotCandidate', 'TooManyInvulnerables', 'TooFewInvulnerables', 'AlreadyInvulnerable', 'NotInvulnerable', 'NoAssociatedValidatorId', 'ValidatorNotRegistered']
+  },
+  /**
+   * Lookup439: sp_core::crypto::KeyTypeId
+   **/
+  SpCoreCryptoKeyTypeId: '[u8;4]',
+  /**
+   * Lookup440: pallet_session::pallet::Error<T>
+   **/
+  PalletSessionError: {
+    _enum: ['InvalidProof', 'NoAssociatedValidatorId', 'DuplicatedKey', 'NoKeys', 'NoAccount']
+  },
+  /**
+   * Lookup446: pallet_balances::types::BalanceLock<Balance>
+   **/
+  PalletBalancesBalanceLock: {
+    id: '[u8;8]',
+    amount: 'u128',
+    reasons: 'PalletBalancesReasons'
+  },
+  /**
+   * Lookup447: pallet_balances::types::Reasons
+   **/
+  PalletBalancesReasons: {
+    _enum: ['Fee', 'Misc', 'All']
+  },
+  /**
+   * Lookup450: pallet_balances::types::ReserveData<ReserveIdentifier, Balance>
+   **/
+  PalletBalancesReserveData: {
+    id: '[u8;16]',
+    amount: 'u128'
+  },
+  /**
+   * Lookup454: opal_runtime::RuntimeHoldReason
+   **/
+  OpalRuntimeRuntimeHoldReason: {
+    _enum: {
+      __Unused0: 'Null',
+      __Unused1: 'Null',
+      __Unused2: 'Null',
+      __Unused3: 'Null',
+      __Unused4: 'Null',
+      __Unused5: 'Null',
+      __Unused6: 'Null',
+      __Unused7: 'Null',
+      __Unused8: 'Null',
+      __Unused9: 'Null',
+      __Unused10: 'Null',
+      __Unused11: 'Null',
+      __Unused12: 'Null',
+      __Unused13: 'Null',
+      __Unused14: 'Null',
+      __Unused15: 'Null',
+      __Unused16: 'Null',
+      __Unused17: 'Null',
+      __Unused18: 'Null',
+      __Unused19: 'Null',
+      __Unused20: 'Null',
+      __Unused21: 'Null',
+      __Unused22: 'Null',
+      CollatorSelection: 'PalletCollatorSelectionHoldReason'
+    }
+  },
+  /**
+   * Lookup455: pallet_collator_selection::pallet::HoldReason
+   **/
+  PalletCollatorSelectionHoldReason: {
+    _enum: ['LicenseBond']
+  },
+  /**
+   * Lookup458: pallet_balances::types::IdAmount<Id, Balance>
+   **/
+  PalletBalancesIdAmount: {
+    id: '[u8;16]',
+    amount: 'u128'
+  },
+  /**
+   * Lookup460: pallet_balances::pallet::Error<T, I>
+   **/
+  PalletBalancesError: {
+    _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'Expendability', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves', 'TooManyHolds', 'TooManyFreezes']
+  },
+  /**
+   * Lookup462: pallet_transaction_payment::Releases
+   **/
+  PalletTransactionPaymentReleases: {
+    _enum: ['V1Ancient', 'V2']
+  },
+  /**
+   * Lookup463: pallet_treasury::Proposal<sp_core::crypto::AccountId32, Balance>
+   **/
+  PalletTreasuryProposal: {
+    proposer: 'AccountId32',
+    value: 'u128',
+    beneficiary: 'AccountId32',
+    bond: 'u128'
+  },
+  /**
+   * Lookup466: frame_support::PalletId
+   **/
+  FrameSupportPalletId: '[u8;8]',
+  /**
+   * Lookup467: pallet_treasury::pallet::Error<T, I>
+   **/
+  PalletTreasuryError: {
+    _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved']
+  },
+  /**
+   * Lookup468: pallet_sudo::pallet::Error<T>
+   **/
+  PalletSudoError: {
+    _enum: ['RequireSudo']
+  },
+  /**
+   * Lookup470: orml_vesting::module::Error<T>
+   **/
+  OrmlVestingModuleError: {
+    _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded']
+  },
+  /**
+   * Lookup471: orml_xtokens::module::Error<T>
+   **/
+  OrmlXtokensModuleError: {
+    _enum: ['AssetHasNoReserve', 'NotCrossChainTransfer', 'InvalidDest', 'NotCrossChainTransferableCurrency', 'UnweighableMessage', 'XcmExecutionFailed', 'CannotReanchor', 'InvalidAncestry', 'InvalidAsset', 'DestinationNotInvertible', 'BadVersion', 'DistinctReserveForAssetAndFee', 'ZeroFee', 'ZeroAmount', 'TooManyAssetsBeingSent', 'AssetIndexNonExistent', 'FeeNotEnough', 'NotSupportedMultiLocation', 'MinXcmFeeNotDefined']
+  },
+  /**
+   * Lookup474: orml_tokens::BalanceLock<Balance>
+   **/
+  OrmlTokensBalanceLock: {
+    id: '[u8;8]',
+    amount: 'u128'
+  },
+  /**
+   * Lookup476: orml_tokens::AccountData<Balance>
+   **/
+  OrmlTokensAccountData: {
+    free: 'u128',
+    reserved: 'u128',
+    frozen: 'u128'
+  },
+  /**
+   * Lookup478: orml_tokens::ReserveData<ReserveIdentifier, Balance>
+   **/
+  OrmlTokensReserveData: {
+    id: 'Null',
+    amount: 'u128'
+  },
+  /**
+   * Lookup480: orml_tokens::module::Error<T>
+   **/
+  OrmlTokensModuleError: {
+    _enum: ['BalanceTooLow', 'AmountIntoBalanceFailed', 'LiquidityRestrictions', 'MaxLocksExceeded', 'KeepAlive', 'ExistentialDeposit', 'DeadAccount', 'TooManyReserves']
+  },
+  /**
+   * Lookup485: pallet_identity::types::RegistrarInfo<Balance, sp_core::crypto::AccountId32>
+   **/
+  PalletIdentityRegistrarInfo: {
+    account: 'AccountId32',
+    fee: 'u128',
+    fields: 'PalletIdentityBitFlags'
+  },
+  /**
+   * Lookup487: pallet_identity::pallet::Error<T>
+   **/
+  PalletIdentityError: {
+    _enum: ['TooManySubAccounts', 'NotFound', 'NotNamed', 'EmptyIndex', 'FeeChanged', 'NoIdentity', 'StickyJudgement', 'JudgementGiven', 'InvalidJudgement', 'InvalidIndex', 'InvalidTarget', 'TooManyFields', 'TooManyRegistrars', 'AlreadyClaimed', 'NotSub', 'NotOwned', 'JudgementForDifferentIdentity', 'JudgementPaymentFailed']
+  },
+  /**
+   * Lookup488: pallet_preimage::RequestStatus<sp_core::crypto::AccountId32, Balance>
+   **/
+  PalletPreimageRequestStatus: {
+    _enum: {
+      Unrequested: {
+        deposit: '(AccountId32,u128)',
+        len: 'u32',
+      },
+      Requested: {
+        deposit: 'Option<(AccountId32,u128)>',
+        count: 'u32',
+        len: 'Option<u32>'
+      }
+    }
+  },
+  /**
+   * Lookup493: pallet_preimage::pallet::Error<T>
+   **/
+  PalletPreimageError: {
+    _enum: ['TooBig', 'AlreadyNoted', 'NotAuthorized', 'NotNoted', 'Requested', 'NotRequested']
+  },
+  /**
+   * Lookup499: pallet_democracy::types::ReferendumInfo<BlockNumber, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance>
+   **/
+  PalletDemocracyReferendumInfo: {
+    _enum: {
+      Ongoing: 'PalletDemocracyReferendumStatus',
+      Finished: {
+        approved: 'bool',
+        end: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup500: pallet_democracy::types::ReferendumStatus<BlockNumber, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance>
+   **/
+  PalletDemocracyReferendumStatus: {
+    end: 'u32',
+    proposal: 'FrameSupportPreimagesBounded',
+    threshold: 'PalletDemocracyVoteThreshold',
+    delay: 'u32',
+    tally: 'PalletDemocracyTally'
+  },
+  /**
+   * Lookup501: pallet_democracy::types::Tally<Balance>
+   **/
+  PalletDemocracyTally: {
+    ayes: 'u128',
+    nays: 'u128',
+    turnout: 'u128'
+  },
+  /**
+   * Lookup502: pallet_democracy::vote::Voting<Balance, sp_core::crypto::AccountId32, BlockNumber, MaxVotes>
+   **/
+  PalletDemocracyVoteVoting: {
+    _enum: {
+      Direct: {
+        votes: 'Vec<(u32,PalletDemocracyVoteAccountVote)>',
+        delegations: 'PalletDemocracyDelegations',
+        prior: 'PalletDemocracyVotePriorLock',
+      },
+      Delegating: {
+        balance: 'u128',
+        target: 'AccountId32',
+        conviction: 'PalletDemocracyConviction',
+        delegations: 'PalletDemocracyDelegations',
+        prior: 'PalletDemocracyVotePriorLock'
+      }
+    }
+  },
+  /**
+   * Lookup506: pallet_democracy::types::Delegations<Balance>
+   **/
+  PalletDemocracyDelegations: {
+    votes: 'u128',
+    capital: 'u128'
+  },
+  /**
+   * Lookup507: pallet_democracy::vote::PriorLock<BlockNumber, Balance>
+   **/
+  PalletDemocracyVotePriorLock: '(u32,u128)',
+  /**
+   * Lookup510: pallet_democracy::pallet::Error<T>
+   **/
+  PalletDemocracyError: {
+    _enum: ['ValueLow', 'ProposalMissing', 'AlreadyCanceled', 'DuplicateProposal', 'ProposalBlacklisted', 'NotSimpleMajority', 'InvalidHash', 'NoProposal', 'AlreadyVetoed', 'ReferendumInvalid', 'NoneWaiting', 'NotVoter', 'NoPermission', 'AlreadyDelegating', 'InsufficientFunds', 'NotDelegating', 'VotesExist', 'InstantNotAllowed', 'Nonsense', 'WrongUpperBound', 'MaxVotesReached', 'TooMany', 'VotingPeriodLow', 'PreimageNotExist']
+  },
+  /**
+   * Lookup512: pallet_collective::Votes<sp_core::crypto::AccountId32, BlockNumber>
+   **/
+  PalletCollectiveVotes: {
+    index: 'u32',
+    threshold: 'u32',
+    ayes: 'Vec<AccountId32>',
+    nays: 'Vec<AccountId32>',
+    end: 'u32'
+  },
+  /**
+   * Lookup513: pallet_collective::pallet::Error<T, I>
+   **/
+  PalletCollectiveError: {
+    _enum: ['NotMember', 'DuplicateProposal', 'ProposalMissing', 'WrongIndex', 'DuplicateVote', 'AlreadyInitialized', 'TooEarly', 'TooManyProposals', 'WrongProposalWeight', 'WrongProposalLength', 'PrimeAccountNotMember']
+  },
+  /**
+   * Lookup517: pallet_membership::pallet::Error<T, I>
+   **/
+  PalletMembershipError: {
+    _enum: ['AlreadyMember', 'NotMember', 'TooManyMembers']
+  },
+  /**
+   * Lookup520: pallet_ranked_collective::MemberRecord
+   **/
+  PalletRankedCollectiveMemberRecord: {
+    rank: 'u16'
+  },
+  /**
+   * Lookup525: pallet_ranked_collective::pallet::Error<T, I>
+   **/
+  PalletRankedCollectiveError: {
+    _enum: ['AlreadyMember', 'NotMember', 'NotPolling', 'Ongoing', 'NoneRemaining', 'Corruption', 'RankTooLow', 'InvalidWitness', 'NoPermission']
+  },
+  /**
+   * Lookup526: pallet_referenda::types::ReferendumInfo<TrackId, opal_runtime::OriginCaller, Moment, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance, pallet_ranked_collective::Tally<T, I, M>, sp_core::crypto::AccountId32, ScheduleAddress>
+   **/
+  PalletReferendaReferendumInfo: {
+    _enum: {
+      Ongoing: 'PalletReferendaReferendumStatus',
+      Approved: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
+      Rejected: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
+      Cancelled: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
+      TimedOut: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
+      Killed: 'u32'
+    }
+  },
+  /**
+   * Lookup527: pallet_referenda::types::ReferendumStatus<TrackId, opal_runtime::OriginCaller, Moment, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance, pallet_ranked_collective::Tally<T, I, M>, sp_core::crypto::AccountId32, ScheduleAddress>
+   **/
+  PalletReferendaReferendumStatus: {
+    track: 'u16',
+    origin: 'OpalRuntimeOriginCaller',
+    proposal: 'FrameSupportPreimagesBounded',
+    enactment: 'FrameSupportScheduleDispatchTime',
+    submitted: 'u32',
+    submissionDeposit: 'PalletReferendaDeposit',
+    decisionDeposit: 'Option<PalletReferendaDeposit>',
+    deciding: 'Option<PalletReferendaDecidingStatus>',
+    tally: 'PalletRankedCollectiveTally',
+    inQueue: 'bool',
+    alarm: 'Option<(u32,(u32,u32))>'
+  },
+  /**
+   * Lookup528: pallet_referenda::types::Deposit<sp_core::crypto::AccountId32, Balance>
+   **/
+  PalletReferendaDeposit: {
+    who: 'AccountId32',
+    amount: 'u128'
+  },
+  /**
+   * Lookup531: pallet_referenda::types::DecidingStatus<BlockNumber>
+   **/
+  PalletReferendaDecidingStatus: {
+    since: 'u32',
+    confirming: 'Option<u32>'
+  },
+  /**
+   * Lookup537: pallet_referenda::types::TrackInfo<Balance, Moment>
+   **/
+  PalletReferendaTrackInfo: {
+    name: 'Text',
+    maxDeciding: 'u32',
+    decisionDeposit: 'u128',
+    preparePeriod: 'u32',
+    decisionPeriod: 'u32',
+    confirmPeriod: 'u32',
+    minEnactmentPeriod: 'u32',
+    minApproval: 'PalletReferendaCurve',
+    minSupport: 'PalletReferendaCurve'
+  },
+  /**
+   * Lookup538: pallet_referenda::types::Curve
+   **/
+  PalletReferendaCurve: {
+    _enum: {
+      LinearDecreasing: {
+        length: 'Perbill',
+        floor: 'Perbill',
+        ceil: 'Perbill',
+      },
+      SteppedDecreasing: {
+        begin: 'Perbill',
+        end: 'Perbill',
+        step: 'Perbill',
+        period: 'Perbill',
+      },
+      Reciprocal: {
+        factor: 'i64',
+        xOffset: 'i64',
+        yOffset: 'i64'
+      }
+    }
+  },
+  /**
+   * Lookup541: pallet_referenda::pallet::Error<T, I>
+   **/
+  PalletReferendaError: {
+    _enum: ['NotOngoing', 'HasDeposit', 'BadTrack', 'Full', 'QueueEmpty', 'BadReferendum', 'NothingToDo', 'NoTrack', 'Unfinished', 'NoPermission', 'NoDeposit', 'BadStatus', 'PreimageNotExist']
+  },
+  /**
+   * Lookup544: pallet_scheduler::Scheduled<Name, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32>
+   **/
+  PalletSchedulerScheduled: {
+    maybeId: 'Option<[u8;32]>',
+    priority: 'u8',
+    call: 'FrameSupportPreimagesBounded',
+    maybePeriodic: 'Option<(u32,u32)>',
+    origin: 'OpalRuntimeOriginCaller'
+  },
+  /**
+   * Lookup546: pallet_scheduler::pallet::Error<T>
+   **/
+  PalletSchedulerError: {
+    _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange', 'Named']
+  },
+  /**
+   * Lookup548: cumulus_pallet_xcmp_queue::InboundChannelDetails
+   **/
+  CumulusPalletXcmpQueueInboundChannelDetails: {
+    sender: 'u32',
+    state: 'CumulusPalletXcmpQueueInboundState',
+    messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat)>'
+  },
+  /**
+   * Lookup549: cumulus_pallet_xcmp_queue::InboundState
+   **/
+  CumulusPalletXcmpQueueInboundState: {
+    _enum: ['Ok', 'Suspended']
+  },
+  /**
+   * Lookup552: polkadot_parachain_primitives::primitives::XcmpMessageFormat
+   **/
+  PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: {
+    _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals']
+  },
+  /**
+   * Lookup555: cumulus_pallet_xcmp_queue::OutboundChannelDetails
+   **/
+  CumulusPalletXcmpQueueOutboundChannelDetails: {
+    recipient: 'u32',
+    state: 'CumulusPalletXcmpQueueOutboundState',
+    signalsExist: 'bool',
+    firstIndex: 'u16',
+    lastIndex: 'u16'
+  },
+  /**
+   * Lookup556: cumulus_pallet_xcmp_queue::OutboundState
+   **/
+  CumulusPalletXcmpQueueOutboundState: {
+    _enum: ['Ok', 'Suspended']
+  },
+  /**
+   * Lookup558: cumulus_pallet_xcmp_queue::QueueConfigData
+   **/
+  CumulusPalletXcmpQueueQueueConfigData: {
+    suspendThreshold: 'u32',
+    dropThreshold: 'u32',
+    resumeThreshold: 'u32',
+    thresholdWeight: 'SpWeightsWeightV2Weight',
+    weightRestrictDecay: 'SpWeightsWeightV2Weight',
+    xcmpMaxIndividualWeight: 'SpWeightsWeightV2Weight'
+  },
+  /**
+   * Lookup560: cumulus_pallet_xcmp_queue::pallet::Error<T>
+   **/
+  CumulusPalletXcmpQueueError: {
+    _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit']
+  },
+  /**
+   * Lookup561: pallet_xcm::pallet::QueryStatus<BlockNumber>
+   **/
+  PalletXcmQueryStatus: {
+    _enum: {
+      Pending: {
+        responder: 'StagingXcmVersionedMultiLocation',
+        maybeMatchQuerier: 'Option<StagingXcmVersionedMultiLocation>',
+        maybeNotify: 'Option<(u8,u8)>',
+        timeout: 'u32',
+      },
+      VersionNotifier: {
+        origin: 'StagingXcmVersionedMultiLocation',
+        isActive: 'bool',
+      },
+      Ready: {
+        response: 'StagingXcmVersionedResponse',
+        at: 'u32'
+      }
+    }
+  },
+  /**
+   * Lookup565: staging_xcm::VersionedResponse
+   **/
+  StagingXcmVersionedResponse: {
+    _enum: {
+      __Unused0: 'Null',
+      __Unused1: 'Null',
+      V2: 'StagingXcmV2Response',
+      V3: 'StagingXcmV3Response'
+    }
+  },
+  /**
+   * Lookup571: pallet_xcm::pallet::VersionMigrationStage
+   **/
+  PalletXcmVersionMigrationStage: {
+    _enum: {
+      MigrateSupportedVersion: 'Null',
+      MigrateVersionNotifiers: 'Null',
+      NotifyCurrentTargets: 'Option<Bytes>',
+      MigrateAndNotifyOldTargets: 'Null'
+    }
+  },
+  /**
+   * Lookup574: staging_xcm::VersionedAssetId
+   **/
+  StagingXcmVersionedAssetId: {
+    _enum: {
+      __Unused0: 'Null',
+      __Unused1: 'Null',
+      __Unused2: 'Null',
+      V3: 'StagingXcmV3MultiassetAssetId'
+    }
+  },
+  /**
+   * Lookup575: pallet_xcm::pallet::RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers>
+   **/
+  PalletXcmRemoteLockedFungibleRecord: {
+    amount: 'u128',
+    owner: 'StagingXcmVersionedMultiLocation',
+    locker: 'StagingXcmVersionedMultiLocation',
+    consumers: 'Vec<(Null,u128)>'
+  },
+  /**
+   * Lookup582: pallet_xcm::pallet::Error<T>
+   **/
+  PalletXcmError: {
+    _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed', 'InvalidAsset', 'LowBalance', 'TooManyLocks', 'AccountNotSovereign', 'FeesNotMet', 'LockNotFound', 'InUse']
+  },
+  /**
+   * Lookup583: cumulus_pallet_xcm::pallet::Error<T>
+   **/
+  CumulusPalletXcmError: 'Null',
+  /**
+   * Lookup584: cumulus_pallet_dmp_queue::ConfigData
+   **/
+  CumulusPalletDmpQueueConfigData: {
+    maxIndividual: 'SpWeightsWeightV2Weight'
+  },
+  /**
+   * Lookup585: cumulus_pallet_dmp_queue::PageIndexData
+   **/
+  CumulusPalletDmpQueuePageIndexData: {
+    beginUsed: 'u32',
+    endUsed: 'u32',
+    overweightCount: 'u64'
+  },
+  /**
+   * Lookup588: cumulus_pallet_dmp_queue::pallet::Error<T>
+   **/
+  CumulusPalletDmpQueueError: {
+    _enum: ['Unknown', 'OverLimit']
+  },
+  /**
+   * Lookup592: pallet_unique::pallet::Error<T>
+   **/
+  PalletUniqueError: {
+    _enum: ['CollectionDecimalPointLimitExceeded', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection']
+  },
+  /**
+   * Lookup593: pallet_configuration::pallet::Error<T>
+   **/
+  PalletConfigurationError: {
+    _enum: ['InconsistentConfiguration']
+  },
+  /**
+   * Lookup594: up_data_structs::Collection<sp_core::crypto::AccountId32>
+   **/
+  UpDataStructsCollection: {
+    owner: 'AccountId32',
+    mode: 'UpDataStructsCollectionMode',
+    name: 'Vec<u16>',
+    description: 'Vec<u16>',
+    tokenPrefix: 'Bytes',
+    sponsorship: 'UpDataStructsSponsorshipStateAccountId32',
+    limits: 'UpDataStructsCollectionLimits',
+    permissions: 'UpDataStructsCollectionPermissions',
+    flags: '[u8;1]'
+  },
+  /**
+   * Lookup595: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
+   **/
+  UpDataStructsSponsorshipStateAccountId32: {
+    _enum: {
+      Disabled: 'Null',
+      Unconfirmed: 'AccountId32',
+      Confirmed: 'AccountId32'
+    }
+  },
+  /**
+   * Lookup596: up_data_structs::Properties
+   **/
+  UpDataStructsProperties: {
+    map: 'UpDataStructsPropertiesMapBoundedVec',
+    consumedSpace: 'u32',
+    reserved: 'u32'
+  },
+  /**
+   * Lookup597: up_data_structs::PropertiesMap<bounded_collections::bounded_vec::BoundedVec<T, S>>
+   **/
+  UpDataStructsPropertiesMapBoundedVec: 'BTreeMap<Bytes, Bytes>',
+  /**
+   * Lookup602: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
+   **/
+  UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap<Bytes, UpDataStructsPropertyPermission>',
+  /**
+   * Lookup609: up_data_structs::CollectionStats
+   **/
+  UpDataStructsCollectionStats: {
+    created: 'u32',
+    destroyed: 'u32',
+    alive: 'u32'
+  },
+  /**
+   * Lookup610: up_data_structs::TokenChild
+   **/
+  UpDataStructsTokenChild: {
+    token: 'u32',
+    collection: 'u32'
+  },
+  /**
+   * Lookup611: PhantomType::up_data_structs<T>
+   **/
+  PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,UpPovEstimateRpcPovInfo);0]',
+  /**
+   * Lookup613: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsTokenData: {
+    properties: 'Vec<UpDataStructsProperty>',
+    owner: 'Option<PalletEvmAccountBasicCrossAccountIdRepr>',
+    pieces: 'u128'
+  },
+  /**
+   * Lookup614: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
+   **/
+  UpDataStructsRpcCollection: {
+    owner: 'AccountId32',
+    mode: 'UpDataStructsCollectionMode',
+    name: 'Vec<u16>',
+    description: 'Vec<u16>',
+    tokenPrefix: 'Bytes',
+    sponsorship: 'UpDataStructsSponsorshipStateAccountId32',
+    limits: 'UpDataStructsCollectionLimits',
+    permissions: 'UpDataStructsCollectionPermissions',
+    tokenPropertyPermissions: 'Vec<UpDataStructsPropertyKeyPermission>',
+    properties: 'Vec<UpDataStructsProperty>',
+    readOnly: 'bool',
+    flags: 'UpDataStructsRpcCollectionFlags'
+  },
+  /**
+   * Lookup615: up_data_structs::RpcCollectionFlags
+   **/
+  UpDataStructsRpcCollectionFlags: {
+    foreign: 'bool',
+    erc721metadata: 'bool'
+  },
+  /**
+   * Lookup616: up_pov_estimate_rpc::PovInfo
+   **/
+  UpPovEstimateRpcPovInfo: {
+    proofSize: 'u64',
+    compactProofSize: 'u64',
+    compressedProofSize: 'u64',
+    results: 'Vec<Result<Result<Null, SpRuntimeDispatchError>, SpRuntimeTransactionValidityTransactionValidityError>>',
+    keyValues: 'Vec<UpPovEstimateRpcTrieKeyValue>'
+  },
+  /**
+   * Lookup619: sp_runtime::transaction_validity::TransactionValidityError
+   **/
+  SpRuntimeTransactionValidityTransactionValidityError: {
+    _enum: {
+      Invalid: 'SpRuntimeTransactionValidityInvalidTransaction',
+      Unknown: 'SpRuntimeTransactionValidityUnknownTransaction'
+    }
+  },
+  /**
+   * Lookup620: sp_runtime::transaction_validity::InvalidTransaction
+   **/
+  SpRuntimeTransactionValidityInvalidTransaction: {
+    _enum: {
+      Call: 'Null',
+      Payment: 'Null',
+      Future: 'Null',
+      Stale: 'Null',
+      BadProof: 'Null',
+      AncientBirthBlock: 'Null',
+      ExhaustsResources: 'Null',
+      Custom: 'u8',
+      BadMandatory: 'Null',
+      MandatoryValidation: 'Null',
+      BadSigner: 'Null'
+    }
+  },
+  /**
+   * Lookup621: sp_runtime::transaction_validity::UnknownTransaction
+   **/
+  SpRuntimeTransactionValidityUnknownTransaction: {
+    _enum: {
+      CannotLookup: 'Null',
+      NoUnsignedValidator: 'Null',
+      Custom: 'u8'
+    }
+  },
+  /**
+   * Lookup623: up_pov_estimate_rpc::TrieKeyValue
+   **/
+  UpPovEstimateRpcTrieKeyValue: {
+    key: 'Bytes',
+    value: 'Bytes'
+  },
+  /**
+   * Lookup625: pallet_common::pallet::Error<T>
+   **/
+  PalletCommonError: {
+    _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsNotEthMirror', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal', 'ConfirmSponsorshipFail', 'UserIsNotCollectionAdmin', 'FungibleItemsHaveNoId']
+  },
+  /**
+   * Lookup627: pallet_fungible::pallet::Error<T>
+   **/
+  PalletFungibleError: {
+    _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed', 'SettingAllowanceForAllNotAllowed', 'FungibleTokensAreAlwaysValid']
+  },
+  /**
+   * Lookup632: pallet_refungible::pallet::Error<T>
+   **/
+  PalletRefungibleError: {
+    _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed']
+  },
+  /**
+   * Lookup633: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  PalletNonfungibleItemData: {
+    owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
+  },
+  /**
+   * Lookup635: up_data_structs::PropertyScope
+   **/
+  UpDataStructsPropertyScope: {
+    _enum: ['None', 'Rmrk']
+  },
+  /**
+   * Lookup638: pallet_nonfungible::pallet::Error<T>
+   **/
+  PalletNonfungibleError: {
+    _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren']
+  },
+  /**
+   * Lookup639: pallet_structure::pallet::Error<T>
+   **/
+  PalletStructureError: {
+    _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound', 'CantNestTokenUnderCollection']
+  },
+  /**
+   * Lookup644: pallet_app_promotion::pallet::Error<T>
+   **/
+  PalletAppPromotionError: {
+    _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'InsufficientStakedBalance', 'InconsistencyState']
+  },
+  /**
+   * Lookup645: pallet_foreign_assets::module::Error<T>
+   **/
+  PalletForeignAssetsModuleError: {
+    _enum: ['BadLocation', 'MultiLocationExisted', 'AssetIdNotExists', 'AssetIdExisted']
+  },
+  /**
+   * Lookup646: pallet_evm::CodeMetadata
+   **/
+  PalletEvmCodeMetadata: {
+    _alias: {
+      size_: 'size',
+      hash_: 'hash'
+    },
+    size_: 'u64',
+    hash_: 'H256'
+  },
+  /**
+   * Lookup648: pallet_evm::pallet::Error<T>
+   **/
+  PalletEvmError: {
+    _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce', 'GasLimitTooLow', 'GasLimitTooHigh', 'Undefined', 'Reentrancy', 'TransactionMustComeFromEOA']
+  },
+  /**
+   * Lookup651: fp_rpc::TransactionStatus
+   **/
+  FpRpcTransactionStatus: {
+    transactionHash: 'H256',
+    transactionIndex: 'u32',
+    from: 'H160',
+    to: 'Option<H160>',
+    contractAddress: 'Option<H160>',
+    logs: 'Vec<EthereumLog>',
+    logsBloom: 'EthbloomBloom'
+  },
+  /**
+   * Lookup653: ethbloom::Bloom
+   **/
+  EthbloomBloom: '[u8;256]',
+  /**
+   * Lookup655: ethereum::receipt::ReceiptV3
+   **/
+  EthereumReceiptReceiptV3: {
+    _enum: {
+      Legacy: 'EthereumReceiptEip658ReceiptData',
+      EIP2930: 'EthereumReceiptEip658ReceiptData',
+      EIP1559: 'EthereumReceiptEip658ReceiptData'
+    }
+  },
+  /**
+   * Lookup656: ethereum::receipt::EIP658ReceiptData
+   **/
+  EthereumReceiptEip658ReceiptData: {
+    statusCode: 'u8',
+    usedGas: 'U256',
+    logsBloom: 'EthbloomBloom',
+    logs: 'Vec<EthereumLog>'
+  },
+  /**
+   * Lookup657: ethereum::block::Block<ethereum::transaction::TransactionV2>
+   **/
+  EthereumBlock: {
+    header: 'EthereumHeader',
+    transactions: 'Vec<EthereumTransactionTransactionV2>',
+    ommers: 'Vec<EthereumHeader>'
+  },
+  /**
+   * Lookup658: ethereum::header::Header
+   **/
+  EthereumHeader: {
+    parentHash: 'H256',
+    ommersHash: 'H256',
+    beneficiary: 'H160',
+    stateRoot: 'H256',
+    transactionsRoot: 'H256',
+    receiptsRoot: 'H256',
+    logsBloom: 'EthbloomBloom',
+    difficulty: 'U256',
+    number: 'U256',
+    gasLimit: 'U256',
+    gasUsed: 'U256',
+    timestamp: 'u64',
+    extraData: 'Bytes',
+    mixHash: 'H256',
+    nonce: 'EthereumTypesHashH64'
+  },
+  /**
+   * Lookup659: ethereum_types::hash::H64
+   **/
+  EthereumTypesHashH64: '[u8;8]',
+  /**
+   * Lookup664: pallet_ethereum::pallet::Error<T>
+   **/
+  PalletEthereumError: {
+    _enum: ['InvalidSignature', 'PreLogExists']
+  },
+  /**
+   * Lookup665: pallet_evm_coder_substrate::pallet::Error<T>
+   **/
+  PalletEvmCoderSubstrateError: {
+    _enum: ['OutOfGas', 'OutOfFund']
+  },
+  /**
+   * Lookup666: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
+   **/
+  UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: {
+    _enum: {
+      Disabled: 'Null',
+      Unconfirmed: 'PalletEvmAccountBasicCrossAccountIdRepr',
+      Confirmed: 'PalletEvmAccountBasicCrossAccountIdRepr'
+    }
+  },
+  /**
+   * Lookup667: pallet_evm_contract_helpers::SponsoringModeT
+   **/
+  PalletEvmContractHelpersSponsoringModeT: {
+    _enum: ['Disabled', 'Allowlisted', 'Generous']
+  },
+  /**
+   * Lookup673: pallet_evm_contract_helpers::pallet::Error<T>
+   **/
+  PalletEvmContractHelpersError: {
+    _enum: ['NoPermission', 'NoPendingSponsor', 'TooManyMethodsHaveSponsoredLimit']
+  },
+  /**
+   * Lookup674: pallet_evm_migration::pallet::Error<T>
+   **/
+  PalletEvmMigrationError: {
+    _enum: ['AccountNotEmpty', 'AccountIsNotMigrating', 'BadEvent']
+  },
+  /**
+   * Lookup675: pallet_maintenance::pallet::Error<T>
+   **/
+  PalletMaintenanceError: 'Null',
+  /**
+   * Lookup676: pallet_utility::pallet::Error<T>
+   **/
+  PalletUtilityError: {
+    _enum: ['TooManyCalls']
+  },
+  /**
+   * Lookup677: pallet_test_utils::pallet::Error<T>
+   **/
+  PalletTestUtilsError: {
+    _enum: ['TestPalletDisabled', 'TriggerRollback']
+  },
+  /**
+   * Lookup679: sp_runtime::MultiSignature
+   **/
+  SpRuntimeMultiSignature: {
+    _enum: {
+      Ed25519: 'SpCoreEd25519Signature',
+      Sr25519: 'SpCoreSr25519Signature',
+      Ecdsa: 'SpCoreEcdsaSignature'
+    }
+  },
+  /**
+   * Lookup680: sp_core::ed25519::Signature
+   **/
+  SpCoreEd25519Signature: '[u8;64]',
+  /**
+   * Lookup682: sp_core::sr25519::Signature
+   **/
+  SpCoreSr25519Signature: '[u8;64]',
+  /**
+   * Lookup683: sp_core::ecdsa::Signature
+   **/
+  SpCoreEcdsaSignature: '[u8;65]',
+  /**
+   * Lookup686: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
+   **/
+  FrameSystemExtensionsCheckSpecVersion: 'Null',
+  /**
+   * Lookup687: frame_system::extensions::check_tx_version::CheckTxVersion<T>
+   **/
+  FrameSystemExtensionsCheckTxVersion: 'Null',
+  /**
+   * Lookup688: frame_system::extensions::check_genesis::CheckGenesis<T>
+   **/
+  FrameSystemExtensionsCheckGenesis: 'Null',
+  /**
+   * Lookup691: frame_system::extensions::check_nonce::CheckNonce<T>
+   **/
+  FrameSystemExtensionsCheckNonce: 'Compact<u32>',
+  /**
+   * Lookup692: frame_system::extensions::check_weight::CheckWeight<T>
+   **/
+  FrameSystemExtensionsCheckWeight: 'Null',
+  /**
+   * Lookup693: opal_runtime::runtime_common::maintenance::CheckMaintenance
+   **/
+  OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: 'Null',
+  /**
+   * Lookup694: opal_runtime::runtime_common::identity::DisableIdentityCalls
+   **/
+  OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: 'Null',
+  /**
+   * Lookup695: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
+   **/
+  PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact<u128>',
+  /**
+   * Lookup696: opal_runtime::Runtime
+   **/
+  OpalRuntimeRuntime: 'Null',
+  /**
+   * Lookup697: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
+   **/
+  PalletEthereumFakeTransactionFinalizer: 'Null'
+};
modifiedjs-packages/types/package.jsondiffbeforeafterboth
--- a/js-packages/types/package.json
+++ b/js-packages/types/package.json
@@ -9,8 +9,7 @@
     "type": "module",
     "version": "1.0.0",
     "main": "index.js",
-    "dependencies": {
-        "rxjs": "^7.8.1",
-        "tslib": "^2.6.2"
+    "devDependencies": {
+        "@polkadot/typegen": "^10.10.1"
     }
 }
addedjs-packages/types/povinfo/definitions.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/povinfo/definitions.ts
@@ -0,0 +1,40 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+type RpcParam = {
+  name: string;
+  type: string;
+  isOptional?: true;
+};
+
+const atParam = {name: 'at', type: 'Hash', isOptional: true};
+
+const fun = (description: string, params: RpcParam[], type: string) => ({
+  description,
+  params: [...params, atParam],
+  type,
+});
+
+export default {
+  types: {},
+  rpc: {
+    estimateExtrinsicPoV: fun(
+      'Estimate PoV size of encoded signed extrinsics',
+      [{name: 'encodedXt', type: 'Vec<Bytes>'}],
+      'UpPovEstimateRpcPovInfo',
+    ),
+  },
+};
addedjs-packages/types/povinfo/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/povinfo/index.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export * from './types.js';
addedjs-packages/types/povinfo/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/povinfo/types.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export type PHANTOM_POVINFO = 'povinfo';
deletedjs-packages/types/src/.gitignorediffbeforeafterboth
--- a/js-packages/types/src/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-metadata.json
deletedjs-packages/types/src/appPromotion/definitions.tsdiffbeforeafterboth
--- a/js-packages/types/src/appPromotion/definitions.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-type RpcParam = {
-  name: string;
-  type: string;
-  isOptional?: true;
-};
-
-const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr';
-
-const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE});
-const atParam = {name: 'at', type: 'Hash', isOptional: true};
-
-const fun = (description: string, params: RpcParam[], type: string) => ({
-  description,
-  params: [...params, atParam],
-  type,
-});
-
-export default {
-  types: {},
-  rpc: {
-    totalStaked: fun(
-      'Returns the total amount of staked tokens',
-      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
-      'u128',
-    ),
-    totalStakedPerBlock: fun(
-      'Returns the total amount of staked tokens per block when staked',
-      [crossAccountParam('staker')],
-      'Vec<(u32, u128)>',
-    ),
-    pendingUnstake: fun(
-      'Returns the total amount of unstaked tokens',
-      [{name: 'staker', type: CROSS_ACCOUNT_ID_TYPE, isOptional: true}],
-      'u128',
-    ),
-    pendingUnstakePerBlock: fun(
-      'Returns the total amount of unstaked tokens per block',
-      [crossAccountParam('staker')],
-      'Vec<(u32, u128)>',
-    ),
-  },
-};
deletedjs-packages/types/src/appPromotion/index.tsdiffbeforeafterboth
--- a/js-packages/types/src/appPromotion/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export * from './types.js';
deletedjs-packages/types/src/appPromotion/types.tsdiffbeforeafterboth
--- a/js-packages/types/src/appPromotion/types.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export type PHANTOM_APPPROMOTION = 'appPromotion';
deletedjs-packages/types/src/augment-api-consts.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api-consts.ts
+++ /dev/null
@@ -1,552 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/api-base/types/consts';
-
-import type { ApiTypes, AugmentedConst } from '@polkadot/api-base/types';
-import type { Option, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
-import type { Codec, ITuple } from '@polkadot/types-codec/types';
-import type { H160, Perbill, Permill } from '@polkadot/types/interfaces/runtime';
-import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, PalletReferendaTrackInfo, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, UpDataStructsCollectionLimits } from '@polkadot/types/lookup';
-
-export type __AugmentedConst<ApiType extends ApiTypes> = AugmentedConst<ApiType>;
-
-declare module '@polkadot/api-base/types/consts' {
-  interface AugmentedConsts<ApiType extends ApiTypes> {
-    appPromotion: {
-      /**
-       * Freeze identifier used by the pallet
-       **/
-      freezeIdentifier: U8aFixed & AugmentedConst<ApiType>;
-      /**
-       * Rate of return for interval in blocks defined in `RecalculationInterval`.
-       **/
-      intervalIncome: Perbill & AugmentedConst<ApiType>;
-      /**
-       * Decimals for the `Currency`.
-       **/
-      nominal: u128 & AugmentedConst<ApiType>;
-      /**
-       * The app's pallet id, used for deriving its sovereign account address.
-       **/
-      palletId: FrameSupportPalletId & AugmentedConst<ApiType>;
-      /**
-       * In parachain blocks.
-       **/
-      pendingInterval: u32 & AugmentedConst<ApiType>;
-      /**
-       * In relay blocks.
-       **/
-      recalculationInterval: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    balances: {
-      /**
-       * The minimum amount required to keep an account open. MUST BE GREATER THAN ZERO!
-       * 
-       * If you *really* need it to be zero, you can enable the feature `insecure_zero_ed` for
-       * this pallet. However, you do so at your own risk: this will open up a major DoS vector.
-       * In case you have multiple sources of provider references, you may also get unexpected
-       * behaviour if you set this to zero.
-       * 
-       * Bottom line: Do yourself a favour and make it at least one!
-       **/
-      existentialDeposit: u128 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of individual freeze locks that can exist on an account at any time.
-       **/
-      maxFreezes: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of holds that can exist on an account at any time.
-       **/
-      maxHolds: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of locks that should exist on an account.
-       * Not strictly enforced, but used for weight estimation.
-       **/
-      maxLocks: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of named reserves that can exist on an account.
-       **/
-      maxReserves: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    common: {
-      /**
-       * Maximum admins per collection.
-       **/
-      collectionAdminsLimit: u32 & AugmentedConst<ApiType>;
-      /**
-       * Set price to create a collection.
-       **/
-      collectionCreationPrice: u128 & AugmentedConst<ApiType>;
-      /**
-       * Address under which the CollectionHelper contract would be available.
-       **/
-      contractAddress: H160 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    configuration: {
-      appPromotionDailyRate: Perbill & AugmentedConst<ApiType>;
-      dayRelayBlocks: u32 & AugmentedConst<ApiType>;
-      defaultCollatorSelectionKickThreshold: u32 & AugmentedConst<ApiType>;
-      defaultCollatorSelectionLicenseBond: u128 & AugmentedConst<ApiType>;
-      defaultCollatorSelectionMaxCollators: u32 & AugmentedConst<ApiType>;
-      defaultMinGasPrice: u64 & AugmentedConst<ApiType>;
-      defaultWeightToFeeCoefficient: u64 & AugmentedConst<ApiType>;
-      maxXcmAllowedLocations: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    council: {
-      /**
-       * The maximum weight of a dispatch call that can be proposed and executed.
-       **/
-      maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    democracy: {
-      /**
-       * Period in blocks where an external proposal may not be re-submitted after being vetoed.
-       **/
-      cooloffPeriod: u32 & AugmentedConst<ApiType>;
-      /**
-       * The period between a proposal being approved and enacted.
-       * 
-       * It should generally be a little more than the unstake period to ensure that
-       * voting stakers have an opportunity to remove themselves from the system in the case
-       * where they are on the losing side of a vote.
-       **/
-      enactmentPeriod: u32 & AugmentedConst<ApiType>;
-      /**
-       * Minimum voting period allowed for a fast-track referendum.
-       **/
-      fastTrackVotingPeriod: u32 & AugmentedConst<ApiType>;
-      /**
-       * Indicator for whether an emergency origin is even allowed to happen. Some chains may
-       * want to set this permanently to `false`, others may want to condition it on things such
-       * as an upgrade having happened recently.
-       **/
-      instantAllowed: bool & AugmentedConst<ApiType>;
-      /**
-       * How often (in blocks) new public referenda are launched.
-       **/
-      launchPeriod: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of items which can be blacklisted.
-       **/
-      maxBlacklisted: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of deposits a public proposal may have at any time.
-       **/
-      maxDeposits: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of public proposals that can exist at any time.
-       **/
-      maxProposals: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of votes for an account.
-       * 
-       * Also used to compute weight, an overly big value can
-       * lead to extrinsic with very big weight: see `delegate` for instance.
-       **/
-      maxVotes: u32 & AugmentedConst<ApiType>;
-      /**
-       * The minimum amount to be used as a deposit for a public referendum proposal.
-       **/
-      minimumDeposit: u128 & AugmentedConst<ApiType>;
-      /**
-       * The minimum period of vote locking.
-       * 
-       * It should be no shorter than enactment period to ensure that in the case of an approval,
-       * those successful voters are locked into the consequences that their votes entail.
-       **/
-      voteLockingPeriod: u32 & AugmentedConst<ApiType>;
-      /**
-       * How often (in blocks) to check for new votes.
-       **/
-      votingPeriod: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    evmContractHelpers: {
-      /**
-       * Address, under which magic contract will be available
-       **/
-      contractAddress: H160 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    fellowshipReferenda: {
-      /**
-       * Quantization level for the referendum wakeup scheduler. A higher number will result in
-       * fewer storage reads/writes needed for smaller voters, but also result in delays to the
-       * automatic referendum status changes. Explicit servicing instructions are unaffected.
-       **/
-      alarmInterval: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maximum size of the referendum queue for a single track.
-       **/
-      maxQueued: u32 & AugmentedConst<ApiType>;
-      /**
-       * The minimum amount to be used as a deposit for a public referendum proposal.
-       **/
-      submissionDeposit: u128 & AugmentedConst<ApiType>;
-      /**
-       * Information concerning the different referendum tracks.
-       **/
-      tracks: Vec<ITuple<[u16, PalletReferendaTrackInfo]>> & AugmentedConst<ApiType>;
-      /**
-       * The number of blocks after submission that a referendum must begin being decided by.
-       * Once this passes, then anyone may cancel the referendum.
-       **/
-      undecidingTimeout: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    identity: {
-      /**
-       * The amount held on deposit for a registered identity
-       **/
-      basicDeposit: u128 & AugmentedConst<ApiType>;
-      /**
-       * The amount held on deposit per additional field for a registered identity.
-       **/
-      fieldDeposit: u128 & AugmentedConst<ApiType>;
-      /**
-       * Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O
-       * required to access an identity, but can be pretty high.
-       **/
-      maxAdditionalFields: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maxmimum number of registrars allowed in the system. Needed to bound the complexity
-       * of, e.g., updating judgements.
-       **/
-      maxRegistrars: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of sub-accounts allowed per identified account.
-       **/
-      maxSubAccounts: u32 & AugmentedConst<ApiType>;
-      /**
-       * The amount held on deposit for a registered subaccount. This should account for the fact
-       * that one storage item's value will increase by the size of an account ID, and there will
-       * be another trie item whose value is the size of an account ID plus 32 bytes.
-       **/
-      subAccountDeposit: u128 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    inflation: {
-      /**
-       * Number of blocks that pass between treasury balance updates due to inflation
-       **/
-      inflationBlockInterval: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    scheduler: {
-      /**
-       * The maximum weight that may be scheduled per block for any dispatchables.
-       **/
-      maximumWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of scheduled calls in the queue for a single block.
-       * 
-       * NOTE:
-       * + Dependent pallets' benchmarks might require a higher limit for the setting. Set a
-       * higher limit under `runtime-benchmarks` feature.
-       **/
-      maxScheduledPerBlock: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    stateTrieMigration: {
-      /**
-       * Maximal number of bytes that a key can have.
-       * 
-       * FRAME itself does not limit the key length.
-       * The concrete value must therefore depend on your storage usage.
-       * A [`frame_support::storage::StorageNMap`] for example can have an arbitrary number of
-       * keys which are then hashed and concatenated, resulting in arbitrarily long keys.
-       * 
-       * Use the *state migration RPC* to retrieve the length of the longest key in your
-       * storage: <https://github.com/paritytech/substrate/issues/11642>
-       * 
-       * The migration will halt with a `Halted` event if this value is too small.
-       * Since there is no real penalty from over-estimating, it is advised to use a large
-       * value. The default is 512 byte.
-       * 
-       * Some key lengths for reference:
-       * - [`frame_support::storage::StorageValue`]: 32 byte
-       * - [`frame_support::storage::StorageMap`]: 64 byte
-       * - [`frame_support::storage::StorageDoubleMap`]: 96 byte
-       * 
-       * For more info see
-       * <https://www.shawntabrizi.com/substrate/querying-substrate-storage-via-rpc/>
-       **/
-      maxKeyLen: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    system: {
-      /**
-       * Maximum number of block number to block hash mappings to keep (oldest pruned first).
-       **/
-      blockHashCount: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum length of a block (in bytes).
-       **/
-      blockLength: FrameSystemLimitsBlockLength & AugmentedConst<ApiType>;
-      /**
-       * Block & extrinsics weights: base values and limits.
-       **/
-      blockWeights: FrameSystemLimitsBlockWeights & AugmentedConst<ApiType>;
-      /**
-       * The weight of runtime database operations the runtime can invoke.
-       **/
-      dbWeight: SpWeightsRuntimeDbWeight & AugmentedConst<ApiType>;
-      /**
-       * The designated SS58 prefix of this chain.
-       * 
-       * This replaces the "ss58Format" property declared in the chain spec. Reason is
-       * that the runtime should know about the prefix in order to make use of it as
-       * an identifier of the chain.
-       **/
-      ss58Prefix: u16 & AugmentedConst<ApiType>;
-      /**
-       * Get the chain's current version.
-       **/
-      version: SpVersionRuntimeVersion & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    technicalCommittee: {
-      /**
-       * The maximum weight of a dispatch call that can be proposed and executed.
-       **/
-      maxProposalWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    timestamp: {
-      /**
-       * The minimum period between blocks. Beware that this is different to the *expected*
-       * period that the block production apparatus provides. Your chosen consensus system will
-       * generally work with this to determine a sensible block time. e.g. For Aura, it will be
-       * double this period on default settings.
-       **/
-      minimumPeriod: u64 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    tokens: {
-      maxLocks: u32 & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of named reserves that can exist on an account.
-       **/
-      maxReserves: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    transactionPayment: {
-      /**
-       * A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their
-       * `priority`
-       * 
-       * This value is multipled by the `final_fee` to obtain a "virtual tip" that is later
-       * added to a tip component in regular `priority` calculations.
-       * It means that a `Normal` transaction can front-run a similarly-sized `Operational`
-       * extrinsic (with no tip), by including a tip value greater than the virtual tip.
-       * 
-       * ```rust,ignore
-       * // For `Normal`
-       * let priority = priority_calc(tip);
-       * 
-       * // For `Operational`
-       * let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier;
-       * let priority = priority_calc(tip + virtual_tip);
-       * ```
-       * 
-       * Note that since we use `final_fee` the multiplier applies also to the regular `tip`
-       * sent with the transaction. So, not only does the transaction get a priority bump based
-       * on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational`
-       * transactions.
-       **/
-      operationalFeeMultiplier: u8 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    treasury: {
-      /**
-       * Percentage of spare funds (if any) that are burnt per spend period.
-       **/
-      burn: Permill & AugmentedConst<ApiType>;
-      /**
-       * The maximum number of approvals that can wait in the spending queue.
-       * 
-       * NOTE: This parameter is also used within the Bounties Pallet extension if enabled.
-       **/
-      maxApprovals: u32 & AugmentedConst<ApiType>;
-      /**
-       * The treasury's pallet id, used for deriving its sovereign account ID.
-       **/
-      palletId: FrameSupportPalletId & AugmentedConst<ApiType>;
-      /**
-       * Fraction of a proposal's value that should be bonded in order to place the proposal.
-       * An accepted proposal gets these back. A rejected proposal does not.
-       **/
-      proposalBond: Permill & AugmentedConst<ApiType>;
-      /**
-       * Maximum amount of funds that should be placed in a deposit for making a proposal.
-       **/
-      proposalBondMaximum: Option<u128> & AugmentedConst<ApiType>;
-      /**
-       * Minimum amount of funds that should be placed in a deposit for making a proposal.
-       **/
-      proposalBondMinimum: u128 & AugmentedConst<ApiType>;
-      /**
-       * Period between successive spends.
-       **/
-      spendPeriod: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    unique: {
-      /**
-       * Maximum admins per collection.
-       **/
-      collectionAdminsLimit: u32 & AugmentedConst<ApiType>;
-      /**
-       * Default FT collection limit.
-       **/
-      ftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
-      /**
-       * Maximal length of a collection description.
-       **/
-      maxCollectionDescriptionLength: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maximal length of a collection name.
-       **/
-      maxCollectionNameLength: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maximum size for all collection properties.
-       **/
-      maxCollectionPropertiesSize: u32 & AugmentedConst<ApiType>;
-      /**
-       * A maximum number of token properties.
-       **/
-      maxPropertiesPerItem: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maximal length of a property key.
-       **/
-      maxPropertyKeyLength: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maximal length of a property value.
-       **/
-      maxPropertyValueLength: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maximal length of a token prefix.
-       **/
-      maxTokenPrefixLength: u32 & AugmentedConst<ApiType>;
-      /**
-       * Maximum size of all token properties.
-       **/
-      maxTokenPropertiesSize: u32 & AugmentedConst<ApiType>;
-      /**
-       * A maximum number of levels of depth in the token nesting tree.
-       **/
-      nestingBudget: u32 & AugmentedConst<ApiType>;
-      /**
-       * Default NFT collection limit.
-       **/
-      nftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
-      /**
-       * Default RFT collection limit.
-       **/
-      rftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    utility: {
-      /**
-       * The limit on the number of batched calls.
-       **/
-      batchedCallsLimit: u32 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    vesting: {
-      /**
-       * The minimum amount transferred to call `vested_transfer`.
-       **/
-      minVestedTransfer: u128 & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-    xTokens: {
-      /**
-       * Base XCM weight.
-       * 
-       * The actually weight for an XCM message is `T::BaseXcmWeight +
-       * T::Weigher::weight(&msg)`.
-       **/
-      baseXcmWeight: SpWeightsWeightV2Weight & AugmentedConst<ApiType>;
-      /**
-       * Self chain location.
-       **/
-      selfLocation: StagingXcmV3MultiLocation & AugmentedConst<ApiType>;
-      /**
-       * Generic const
-       **/
-      [key: string]: Codec;
-    };
-  } // AugmentedConsts
-} // declare module
deletedjs-packages/types/src/augment-api-errors.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api-errors.ts
+++ /dev/null
@@ -1,1521 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/api-base/types/errors';
-
-import type { ApiTypes, AugmentedError } from '@polkadot/api-base/types';
-
-export type __AugmentedError<ApiType extends ApiTypes> = AugmentedError<ApiType>;
-
-declare module '@polkadot/api-base/types/errors' {
-  interface AugmentedErrors<ApiType extends ApiTypes> {
-    appPromotion: {
-      /**
-       * Error due to action requiring admin to be set.
-       **/
-      AdminNotSet: AugmentedError<ApiType>;
-      /**
-       * Errors caused by incorrect state of a staker in context of the pallet.
-       **/
-      InconsistencyState: AugmentedError<ApiType>;
-      /**
-       * Errors caused by insufficient staked balance.
-       **/
-      InsufficientStakedBalance: AugmentedError<ApiType>;
-      /**
-       * No permission to perform an action.
-       **/
-      NoPermission: AugmentedError<ApiType>;
-      /**
-       * Insufficient funds to perform an action.
-       **/
-      NotSufficientFunds: AugmentedError<ApiType>;
-      /**
-       * Occurs when a pending unstake cannot be added in this block. PENDING_LIMIT_PER_BLOCK` limits exceeded.
-       **/
-      PendingForBlockOverflow: AugmentedError<ApiType>;
-      /**
-       * The error is due to the fact that the collection/contract must already be sponsored in order to perform the action.
-       **/
-      SponsorNotSet: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    balances: {
-      /**
-       * Beneficiary account must pre-exist.
-       **/
-      DeadAccount: AugmentedError<ApiType>;
-      /**
-       * Value too low to create account due to existential deposit.
-       **/
-      ExistentialDeposit: AugmentedError<ApiType>;
-      /**
-       * A vesting schedule already exists for this account.
-       **/
-      ExistingVestingSchedule: AugmentedError<ApiType>;
-      /**
-       * Transfer/payment would kill account.
-       **/
-      Expendability: AugmentedError<ApiType>;
-      /**
-       * Balance too low to send value.
-       **/
-      InsufficientBalance: AugmentedError<ApiType>;
-      /**
-       * Account liquidity restrictions prevent withdrawal.
-       **/
-      LiquidityRestrictions: AugmentedError<ApiType>;
-      /**
-       * Number of freezes exceed `MaxFreezes`.
-       **/
-      TooManyFreezes: AugmentedError<ApiType>;
-      /**
-       * Number of holds exceed `MaxHolds`.
-       **/
-      TooManyHolds: AugmentedError<ApiType>;
-      /**
-       * Number of named reserves exceed `MaxReserves`.
-       **/
-      TooManyReserves: AugmentedError<ApiType>;
-      /**
-       * Vesting balance too high to send value.
-       **/
-      VestingBalance: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    collatorSelection: {
-      /**
-       * User is already a candidate
-       **/
-      AlreadyCandidate: AugmentedError<ApiType>;
-      /**
-       * User already holds license to collate
-       **/
-      AlreadyHoldingLicense: AugmentedError<ApiType>;
-      /**
-       * User is already an Invulnerable
-       **/
-      AlreadyInvulnerable: AugmentedError<ApiType>;
-      /**
-       * Account has no associated validator ID
-       **/
-      NoAssociatedValidatorId: AugmentedError<ApiType>;
-      /**
-       * User does not hold a license to collate
-       **/
-      NoLicense: AugmentedError<ApiType>;
-      /**
-       * User is not a candidate
-       **/
-      NotCandidate: AugmentedError<ApiType>;
-      /**
-       * User is not an Invulnerable
-       **/
-      NotInvulnerable: AugmentedError<ApiType>;
-      /**
-       * Permission issue
-       **/
-      Permission: AugmentedError<ApiType>;
-      /**
-       * Too few invulnerables
-       **/
-      TooFewInvulnerables: AugmentedError<ApiType>;
-      /**
-       * Too many candidates
-       **/
-      TooManyCandidates: AugmentedError<ApiType>;
-      /**
-       * Too many invulnerables
-       **/
-      TooManyInvulnerables: AugmentedError<ApiType>;
-      /**
-       * Unknown error
-       **/
-      Unknown: AugmentedError<ApiType>;
-      /**
-       * Validator ID is not yet registered
-       **/
-      ValidatorNotRegistered: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    common: {
-      /**
-       * Account token limit exceeded per collection
-       **/
-      AccountTokenLimitExceeded: AugmentedError<ApiType>;
-      /**
-       * Only spending from eth mirror could be approved
-       **/
-      AddressIsNotEthMirror: AugmentedError<ApiType>;
-      /**
-       * Can't transfer tokens to ethereum zero address
-       **/
-      AddressIsZero: AugmentedError<ApiType>;
-      /**
-       * Address is not in allow list.
-       **/
-      AddressNotInAllowlist: AugmentedError<ApiType>;
-      /**
-       * Requested value is more than the approved
-       **/
-      ApprovedValueTooLow: AugmentedError<ApiType>;
-      /**
-       * Tried to approve more than owned
-       **/
-      CantApproveMoreThanOwned: AugmentedError<ApiType>;
-      /**
-       * Destroying only empty collections is allowed
-       **/
-      CantDestroyNotEmptyCollection: AugmentedError<ApiType>;
-      /**
-       * Exceeded max admin count
-       **/
-      CollectionAdminCountExceeded: AugmentedError<ApiType>;
-      /**
-       * Collection description can not be longer than 255 char.
-       **/
-      CollectionDescriptionLimitExceeded: AugmentedError<ApiType>;
-      /**
-       * Tried to store more data than allowed in collection field
-       **/
-      CollectionFieldSizeExceeded: AugmentedError<ApiType>;
-      /**
-       * Tried to access an external collection with an internal API
-       **/
-      CollectionIsExternal: AugmentedError<ApiType>;
-      /**
-       * Tried to access an internal collection with an external API
-       **/
-      CollectionIsInternal: AugmentedError<ApiType>;
-      /**
-       * Collection limit bounds per collection exceeded
-       **/
-      CollectionLimitBoundsExceeded: AugmentedError<ApiType>;
-      /**
-       * Collection name can not be longer than 63 char.
-       **/
-      CollectionNameLimitExceeded: AugmentedError<ApiType>;
-      /**
-       * This collection does not exist.
-       **/
-      CollectionNotFound: AugmentedError<ApiType>;
-      /**
-       * Collection token limit exceeded
-       **/
-      CollectionTokenLimitExceeded: AugmentedError<ApiType>;
-      /**
-       * Token prefix can not be longer than 15 char.
-       **/
-      CollectionTokenPrefixLimitExceeded: AugmentedError<ApiType>;
-      /**
-       * This address is not set as sponsor, use setCollectionSponsor first.
-       **/
-      ConfirmSponsorshipFail: AugmentedError<ApiType>;
-      /**
-       * Empty property keys are forbidden
-       **/
-      EmptyPropertyKey: AugmentedError<ApiType>;
-      /**
-       * Fungible tokens hold no ID, and the default value of TokenId for a fungible collection is 0.
-       **/
-      FungibleItemsHaveNoId: AugmentedError<ApiType>;
-      /**
-       * Only ASCII letters, digits, and symbols `_`, `-`, and `.` are allowed
-       **/
-      InvalidCharacterInPropertyKey: AugmentedError<ApiType>;
-      /**
-       * Metadata flag frozen
-       **/
-      MetadataFlagFrozen: AugmentedError<ApiType>;
-      /**
-       * Sender parameter and item owner must be equal.
-       **/
-      MustBeTokenOwner: AugmentedError<ApiType>;
-      /**
-       * No permission to perform action
-       **/
-      NoPermission: AugmentedError<ApiType>;
-      /**
-       * Tried to store more property data than allowed
-       **/
-      NoSpaceForProperty: AugmentedError<ApiType>;
-      /**
-       * Insufficient funds to perform an action
-       **/
-      NotSufficientFounds: AugmentedError<ApiType>;
-      /**
-       * Tried to enable permissions which are only permitted to be disabled
-       **/
-      OwnerPermissionsCantBeReverted: AugmentedError<ApiType>;
-      /**
-       * Property key is too long
-       **/
-      PropertyKeyIsTooLong: AugmentedError<ApiType>;
-      /**
-       * Tried to store more property keys than allowed
-       **/
-      PropertyLimitReached: AugmentedError<ApiType>;
-      /**
-       * Collection is not in mint mode.
-       **/
-      PublicMintingNotAllowed: AugmentedError<ApiType>;
-      /**
-       * Only tokens from specific collections may nest tokens under this one
-       **/
-      SourceCollectionIsNotAllowedToNest: AugmentedError<ApiType>;
-      /**
-       * Item does not exist
-       **/
-      TokenNotFound: AugmentedError<ApiType>;
-      /**
-       * Item is balance not enough
-       **/
-      TokenValueTooLow: AugmentedError<ApiType>;
-      /**
-       * Total collections bound exceeded.
-       **/
-      TotalCollectionsLimitExceeded: AugmentedError<ApiType>;
-      /**
-       * Collection settings not allowing items transferring
-       **/
-      TransferNotAllowed: AugmentedError<ApiType>;
-      /**
-       * The operation is not supported
-       **/
-      UnsupportedOperation: AugmentedError<ApiType>;
-      /**
-       * User does not satisfy the nesting rule
-       **/
-      UserIsNotAllowedToNest: AugmentedError<ApiType>;
-      /**
-       * The user is not an administrator.
-       **/
-      UserIsNotCollectionAdmin: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    configuration: {
-      InconsistentConfiguration: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    council: {
-      /**
-       * Members are already initialized!
-       **/
-      AlreadyInitialized: AugmentedError<ApiType>;
-      /**
-       * Duplicate proposals not allowed
-       **/
-      DuplicateProposal: AugmentedError<ApiType>;
-      /**
-       * Duplicate vote ignored
-       **/
-      DuplicateVote: AugmentedError<ApiType>;
-      /**
-       * Account is not a member
-       **/
-      NotMember: AugmentedError<ApiType>;
-      /**
-       * Prime account is not a member
-       **/
-      PrimeAccountNotMember: AugmentedError<ApiType>;
-      /**
-       * Proposal must exist
-       **/
-      ProposalMissing: AugmentedError<ApiType>;
-      /**
-       * The close call was made too early, before the end of the voting.
-       **/
-      TooEarly: AugmentedError<ApiType>;
-      /**
-       * There can only be a maximum of `MaxProposals` active proposals.
-       **/
-      TooManyProposals: AugmentedError<ApiType>;
-      /**
-       * Mismatched index
-       **/
-      WrongIndex: AugmentedError<ApiType>;
-      /**
-       * The given length bound for the proposal was too low.
-       **/
-      WrongProposalLength: AugmentedError<ApiType>;
-      /**
-       * The given weight bound for the proposal was too low.
-       **/
-      WrongProposalWeight: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    councilMembership: {
-      /**
-       * Already a member.
-       **/
-      AlreadyMember: AugmentedError<ApiType>;
-      /**
-       * Not a member.
-       **/
-      NotMember: AugmentedError<ApiType>;
-      /**
-       * Too many members.
-       **/
-      TooManyMembers: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    cumulusXcm: {
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    democracy: {
-      /**
-       * Cannot cancel the same proposal twice
-       **/
-      AlreadyCanceled: AugmentedError<ApiType>;
-      /**
-       * The account is already delegating.
-       **/
-      AlreadyDelegating: AugmentedError<ApiType>;
-      /**
-       * Identity may not veto a proposal twice
-       **/
-      AlreadyVetoed: AugmentedError<ApiType>;
-      /**
-       * Proposal already made
-       **/
-      DuplicateProposal: AugmentedError<ApiType>;
-      /**
-       * The instant referendum origin is currently disallowed.
-       **/
-      InstantNotAllowed: AugmentedError<ApiType>;
-      /**
-       * Too high a balance was provided that the account cannot afford.
-       **/
-      InsufficientFunds: AugmentedError<ApiType>;
-      /**
-       * Invalid hash
-       **/
-      InvalidHash: AugmentedError<ApiType>;
-      /**
-       * Maximum number of votes reached.
-       **/
-      MaxVotesReached: AugmentedError<ApiType>;
-      /**
-       * No proposals waiting
-       **/
-      NoneWaiting: AugmentedError<ApiType>;
-      /**
-       * Delegation to oneself makes no sense.
-       **/
-      Nonsense: AugmentedError<ApiType>;
-      /**
-       * The actor has no permission to conduct the action.
-       **/
-      NoPermission: AugmentedError<ApiType>;
-      /**
-       * No external proposal
-       **/
-      NoProposal: AugmentedError<ApiType>;
-      /**
-       * The account is not currently delegating.
-       **/
-      NotDelegating: AugmentedError<ApiType>;
-      /**
-       * Next external proposal not simple majority
-       **/
-      NotSimpleMajority: AugmentedError<ApiType>;
-      /**
-       * The given account did not vote on the referendum.
-       **/
-      NotVoter: AugmentedError<ApiType>;
-      /**
-       * The preimage does not exist.
-       **/
-      PreimageNotExist: AugmentedError<ApiType>;
-      /**
-       * Proposal still blacklisted
-       **/
-      ProposalBlacklisted: AugmentedError<ApiType>;
-      /**
-       * Proposal does not exist
-       **/
-      ProposalMissing: AugmentedError<ApiType>;
-      /**
-       * Vote given for invalid referendum
-       **/
-      ReferendumInvalid: AugmentedError<ApiType>;
-      /**
-       * Maximum number of items reached.
-       **/
-      TooMany: AugmentedError<ApiType>;
-      /**
-       * Value too low
-       **/
-      ValueLow: AugmentedError<ApiType>;
-      /**
-       * The account currently has votes attached to it and the operation cannot succeed until
-       * these are removed, either through `unvote` or `reap_vote`.
-       **/
-      VotesExist: AugmentedError<ApiType>;
-      /**
-       * Voting period too low
-       **/
-      VotingPeriodLow: AugmentedError<ApiType>;
-      /**
-       * Invalid upper bound.
-       **/
-      WrongUpperBound: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    dmpQueue: {
-      /**
-       * The amount of weight given is possibly not enough for executing the message.
-       **/
-      OverLimit: AugmentedError<ApiType>;
-      /**
-       * The message index given is unknown.
-       **/
-      Unknown: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    ethereum: {
-      /**
-       * Signature is invalid.
-       **/
-      InvalidSignature: AugmentedError<ApiType>;
-      /**
-       * Pre-log is present, therefore transact is not allowed.
-       **/
-      PreLogExists: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    evm: {
-      /**
-       * Not enough balance to perform action
-       **/
-      BalanceLow: AugmentedError<ApiType>;
-      /**
-       * Calculating total fee overflowed
-       **/
-      FeeOverflow: AugmentedError<ApiType>;
-      /**
-       * Gas limit is too high.
-       **/
-      GasLimitTooHigh: AugmentedError<ApiType>;
-      /**
-       * Gas limit is too low.
-       **/
-      GasLimitTooLow: AugmentedError<ApiType>;
-      /**
-       * Gas price is too low.
-       **/
-      GasPriceTooLow: AugmentedError<ApiType>;
-      /**
-       * Nonce is invalid
-       **/
-      InvalidNonce: AugmentedError<ApiType>;
-      /**
-       * Calculating total payment overflowed
-       **/
-      PaymentOverflow: AugmentedError<ApiType>;
-      /**
-       * EVM reentrancy
-       **/
-      Reentrancy: AugmentedError<ApiType>;
-      /**
-       * EIP-3607,
-       **/
-      TransactionMustComeFromEOA: AugmentedError<ApiType>;
-      /**
-       * Undefined error.
-       **/
-      Undefined: AugmentedError<ApiType>;
-      /**
-       * Withdraw fee failed
-       **/
-      WithdrawFailed: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    evmCoderSubstrate: {
-      OutOfFund: AugmentedError<ApiType>;
-      OutOfGas: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    evmContractHelpers: {
-      /**
-       * No pending sponsor for contract.
-       **/
-      NoPendingSponsor: AugmentedError<ApiType>;
-      /**
-       * This method is only executable by contract owner
-       **/
-      NoPermission: AugmentedError<ApiType>;
-      /**
-       * Number of methods that sponsored limit is defined for exceeds maximum.
-       **/
-      TooManyMethodsHaveSponsoredLimit: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    evmMigration: {
-      /**
-       * Migration of this account is not yet started, or already finished.
-       **/
-      AccountIsNotMigrating: AugmentedError<ApiType>;
-      /**
-       * Can only migrate to empty address.
-       **/
-      AccountNotEmpty: AugmentedError<ApiType>;
-      /**
-       * Failed to decode event bytes
-       **/
-      BadEvent: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    fellowshipCollective: {
-      /**
-       * Account is already a member.
-       **/
-      AlreadyMember: AugmentedError<ApiType>;
-      /**
-       * Unexpected error in state.
-       **/
-      Corruption: AugmentedError<ApiType>;
-      /**
-       * The information provided is incorrect.
-       **/
-      InvalidWitness: AugmentedError<ApiType>;
-      /**
-       * There are no further records to be removed.
-       **/
-      NoneRemaining: AugmentedError<ApiType>;
-      /**
-       * The origin is not sufficiently privileged to do the operation.
-       **/
-      NoPermission: AugmentedError<ApiType>;
-      /**
-       * Account is not a member.
-       **/
-      NotMember: AugmentedError<ApiType>;
-      /**
-       * The given poll index is unknown or has closed.
-       **/
-      NotPolling: AugmentedError<ApiType>;
-      /**
-       * The given poll is still ongoing.
-       **/
-      Ongoing: AugmentedError<ApiType>;
-      /**
-       * The member's rank is too low to vote.
-       **/
-      RankTooLow: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    fellowshipReferenda: {
-      /**
-       * The referendum index provided is invalid in this context.
-       **/
-      BadReferendum: AugmentedError<ApiType>;
-      /**
-       * The referendum status is invalid for this operation.
-       **/
-      BadStatus: AugmentedError<ApiType>;
-      /**
-       * The track identifier given was invalid.
-       **/
-      BadTrack: AugmentedError<ApiType>;
-      /**
-       * There are already a full complement of referenda in progress for this track.
-       **/
-      Full: AugmentedError<ApiType>;
-      /**
-       * Referendum's decision deposit is already paid.
-       **/
-      HasDeposit: AugmentedError<ApiType>;
-      /**
-       * The deposit cannot be refunded since none was made.
-       **/
-      NoDeposit: AugmentedError<ApiType>;
-      /**
-       * The deposit refunder is not the depositor.
-       **/
-      NoPermission: AugmentedError<ApiType>;
-      /**
-       * There was nothing to do in the advancement.
-       **/
-      NothingToDo: AugmentedError<ApiType>;
-      /**
-       * Referendum is not ongoing.
-       **/
-      NotOngoing: AugmentedError<ApiType>;
-      /**
-       * No track exists for the proposal origin.
-       **/
-      NoTrack: AugmentedError<ApiType>;
-      /**
-       * The preimage does not exist.
-       **/
-      PreimageNotExist: AugmentedError<ApiType>;
-      /**
-       * The queue of the track is empty.
-       **/
-      QueueEmpty: AugmentedError<ApiType>;
-      /**
-       * Any deposit cannot be refunded until after the decision is over.
-       **/
-      Unfinished: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    foreignAssets: {
-      /**
-       * AssetId exists
-       **/
-      AssetIdExisted: AugmentedError<ApiType>;
-      /**
-       * AssetId not exists
-       **/
-      AssetIdNotExists: AugmentedError<ApiType>;
-      /**
-       * The given location could not be used (e.g. because it cannot be expressed in the
-       * desired version of XCM).
-       **/
-      BadLocation: AugmentedError<ApiType>;
-      /**
-       * MultiLocation existed
-       **/
-      MultiLocationExisted: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    fungible: {
-      /**
-       * Fungible token does not support nesting.
-       **/
-      FungibleDisallowsNesting: AugmentedError<ApiType>;
-      /**
-       * Tried to set data for fungible item.
-       **/
-      FungibleItemsDontHaveData: AugmentedError<ApiType>;
-      /**
-       * Only a fungible collection could be possibly broken; any fungible token is valid.
-       **/
-      FungibleTokensAreAlwaysValid: AugmentedError<ApiType>;
-      /**
-       * Not Fungible item data used to mint in Fungible collection.
-       **/
-      NotFungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
-      /**
-       * Setting allowance for all is not allowed.
-       **/
-      SettingAllowanceForAllNotAllowed: AugmentedError<ApiType>;
-      /**
-       * Setting item properties is not allowed.
-       **/
-      SettingPropertiesNotAllowed: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    identity: {
-      /**
-       * Account ID is already named.
-       **/
-      AlreadyClaimed: AugmentedError<ApiType>;
-      /**
-       * Empty index.
-       **/
-      EmptyIndex: AugmentedError<ApiType>;
-      /**
-       * Fee is changed.
-       **/
-      FeeChanged: AugmentedError<ApiType>;
-      /**
-       * The index is invalid.
-       **/
-      InvalidIndex: AugmentedError<ApiType>;
-      /**
-       * Invalid judgement.
-       **/
-      InvalidJudgement: AugmentedError<ApiType>;
-      /**
-       * The target is invalid.
-       **/
-      InvalidTarget: AugmentedError<ApiType>;
-      /**
-       * The provided judgement was for a different identity.
-       **/
-      JudgementForDifferentIdentity: AugmentedError<ApiType>;
-      /**
-       * Judgement given.
-       **/
-      JudgementGiven: AugmentedError<ApiType>;
-      /**
-       * Error that occurs when there is an issue paying for judgement.
-       **/
-      JudgementPaymentFailed: AugmentedError<ApiType>;
-      /**
-       * No identity found.
-       **/
-      NoIdentity: AugmentedError<ApiType>;
-      /**
-       * Account isn't found.
-       **/
-      NotFound: AugmentedError<ApiType>;
-      /**
-       * Account isn't named.
-       **/
-      NotNamed: AugmentedError<ApiType>;
-      /**
-       * Sub-account isn't owned by sender.
-       **/
-      NotOwned: AugmentedError<ApiType>;
-      /**
-       * Sender is not a sub-account.
-       **/
-      NotSub: AugmentedError<ApiType>;
-      /**
-       * Sticky judgement.
-       **/
-      StickyJudgement: AugmentedError<ApiType>;
-      /**
-       * Too many additional fields.
-       **/
-      TooManyFields: AugmentedError<ApiType>;
-      /**
-       * Maximum amount of registrars reached. Cannot add any more.
-       **/
-      TooManyRegistrars: AugmentedError<ApiType>;
-      /**
-       * Too many subs-accounts.
-       **/
-      TooManySubAccounts: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    maintenance: {
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    nonfungible: {
-      /**
-       * Unable to burn NFT with children
-       **/
-      CantBurnNftWithChildren: AugmentedError<ApiType>;
-      /**
-       * Used amount > 1 with NFT
-       **/
-      NonfungibleItemsHaveNoAmount: AugmentedError<ApiType>;
-      /**
-       * Not Nonfungible item data used to mint in Nonfungible collection.
-       **/
-      NotNonfungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    parachainSystem: {
-      /**
-       * The inherent which supplies the host configuration did not run this block.
-       **/
-      HostConfigurationNotAvailable: AugmentedError<ApiType>;
-      /**
-       * No code upgrade has been authorized.
-       **/
-      NothingAuthorized: AugmentedError<ApiType>;
-      /**
-       * No validation function upgrade is currently scheduled.
-       **/
-      NotScheduled: AugmentedError<ApiType>;
-      /**
-       * Attempt to upgrade validation function while existing upgrade pending.
-       **/
-      OverlappingUpgrades: AugmentedError<ApiType>;
-      /**
-       * Polkadot currently prohibits this parachain from upgrading its validation function.
-       **/
-      ProhibitedByPolkadot: AugmentedError<ApiType>;
-      /**
-       * The supplied validation function has compiled into a blob larger than Polkadot is
-       * willing to run.
-       **/
-      TooBig: AugmentedError<ApiType>;
-      /**
-       * The given code upgrade has not been authorized.
-       **/
-      Unauthorized: AugmentedError<ApiType>;
-      /**
-       * The inherent which supplies the validation data did not run this block.
-       **/
-      ValidationDataNotAvailable: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    polkadotXcm: {
-      /**
-       * The given account is not an identifiable sovereign account for any location.
-       **/
-      AccountNotSovereign: AugmentedError<ApiType>;
-      /**
-       * The location is invalid since it already has a subscription from us.
-       **/
-      AlreadySubscribed: AugmentedError<ApiType>;
-      /**
-       * The given location could not be used (e.g. because it cannot be expressed in the
-       * desired version of XCM).
-       **/
-      BadLocation: AugmentedError<ApiType>;
-      /**
-       * The version of the `Versioned` value used is not able to be interpreted.
-       **/
-      BadVersion: AugmentedError<ApiType>;
-      /**
-       * Could not re-anchor the assets to declare the fees for the destination chain.
-       **/
-      CannotReanchor: AugmentedError<ApiType>;
-      /**
-       * The destination `MultiLocation` provided cannot be inverted.
-       **/
-      DestinationNotInvertible: AugmentedError<ApiType>;
-      /**
-       * The assets to be sent are empty.
-       **/
-      Empty: AugmentedError<ApiType>;
-      /**
-       * The operation required fees to be paid which the initiator could not meet.
-       **/
-      FeesNotMet: AugmentedError<ApiType>;
-      /**
-       * The message execution fails the filter.
-       **/
-      Filtered: AugmentedError<ApiType>;
-      /**
-       * The unlock operation cannot succeed because there are still consumers of the lock.
-       **/
-      InUse: AugmentedError<ApiType>;
-      /**
-       * Invalid asset for the operation.
-       **/
-      InvalidAsset: AugmentedError<ApiType>;
-      /**
-       * Origin is invalid for sending.
-       **/
-      InvalidOrigin: AugmentedError<ApiType>;
-      /**
-       * A remote lock with the corresponding data could not be found.
-       **/
-      LockNotFound: AugmentedError<ApiType>;
-      /**
-       * The owner does not own (all) of the asset that they wish to do the operation on.
-       **/
-      LowBalance: AugmentedError<ApiType>;
-      /**
-       * The referenced subscription could not be found.
-       **/
-      NoSubscription: AugmentedError<ApiType>;
-      /**
-       * There was some other issue (i.e. not to do with routing) in sending the message.
-       * Perhaps a lack of space for buffering the message.
-       **/
-      SendFailure: AugmentedError<ApiType>;
-      /**
-       * Too many assets have been attempted for transfer.
-       **/
-      TooManyAssets: AugmentedError<ApiType>;
-      /**
-       * The asset owner has too many locks on the asset.
-       **/
-      TooManyLocks: AugmentedError<ApiType>;
-      /**
-       * The desired destination was unreachable, generally because there is a no way of routing
-       * to it.
-       **/
-      Unreachable: AugmentedError<ApiType>;
-      /**
-       * The message's weight could not be determined.
-       **/
-      UnweighableMessage: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    preimage: {
-      /**
-       * Preimage has already been noted on-chain.
-       **/
-      AlreadyNoted: AugmentedError<ApiType>;
-      /**
-       * The user is not authorized to perform this action.
-       **/
-      NotAuthorized: AugmentedError<ApiType>;
-      /**
-       * The preimage cannot be removed since it has not yet been noted.
-       **/
-      NotNoted: AugmentedError<ApiType>;
-      /**
-       * The preimage request cannot be removed since no outstanding requests exist.
-       **/
-      NotRequested: AugmentedError<ApiType>;
-      /**
-       * A preimage may not be removed when there are outstanding requests.
-       **/
-      Requested: AugmentedError<ApiType>;
-      /**
-       * Preimage is too large to store on-chain.
-       **/
-      TooBig: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    refungible: {
-      /**
-       * Not Refungible item data used to mint in Refungible collection.
-       **/
-      NotRefungibleDataUsedToMintFungibleCollectionToken: AugmentedError<ApiType>;
-      /**
-       * Refungible token can't nest other tokens.
-       **/
-      RefungibleDisallowsNesting: AugmentedError<ApiType>;
-      /**
-       * Refungible token can't be repartitioned by user who isn't owns all pieces.
-       **/
-      RepartitionWhileNotOwningAllPieces: AugmentedError<ApiType>;
-      /**
-       * Setting item properties is not allowed.
-       **/
-      SettingPropertiesNotAllowed: AugmentedError<ApiType>;
-      /**
-       * Maximum refungibility exceeded.
-       **/
-      WrongRefungiblePieces: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    scheduler: {
-      /**
-       * Failed to schedule a call
-       **/
-      FailedToSchedule: AugmentedError<ApiType>;
-      /**
-       * Attempt to use a non-named function on a named task.
-       **/
-      Named: AugmentedError<ApiType>;
-      /**
-       * Cannot find the scheduled call.
-       **/
-      NotFound: AugmentedError<ApiType>;
-      /**
-       * Reschedule failed because it does not change scheduled time.
-       **/
-      RescheduleNoChange: AugmentedError<ApiType>;
-      /**
-       * Given target block number is in the past.
-       **/
-      TargetBlockNumberInPast: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    session: {
-      /**
-       * Registered duplicate key.
-       **/
-      DuplicatedKey: AugmentedError<ApiType>;
-      /**
-       * Invalid ownership proof.
-       **/
-      InvalidProof: AugmentedError<ApiType>;
-      /**
-       * Key setting account is not live, so it's impossible to associate keys.
-       **/
-      NoAccount: AugmentedError<ApiType>;
-      /**
-       * No associated validator ID for account.
-       **/
-      NoAssociatedValidatorId: AugmentedError<ApiType>;
-      /**
-       * No keys are associated with this account.
-       **/
-      NoKeys: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    stateTrieMigration: {
-      /**
-       * Bad child root provided.
-       **/
-      BadChildRoot: AugmentedError<ApiType>;
-      /**
-       * Bad witness data provided.
-       **/
-      BadWitness: AugmentedError<ApiType>;
-      /**
-       * A key was longer than the configured maximum.
-       * 
-       * This means that the migration halted at the current [`Progress`] and
-       * can be resumed with a larger [`crate::Config::MaxKeyLen`] value.
-       * Retrying with the same [`crate::Config::MaxKeyLen`] value will not work.
-       * The value should only be increased to avoid a storage migration for the currently
-       * stored [`crate::Progress::LastKey`].
-       **/
-      KeyTooLong: AugmentedError<ApiType>;
-      /**
-       * Max signed limits not respected.
-       **/
-      MaxSignedLimits: AugmentedError<ApiType>;
-      /**
-       * submitter does not have enough funds.
-       **/
-      NotEnoughFunds: AugmentedError<ApiType>;
-      /**
-       * Signed migration is not allowed because the maximum limit is not set yet.
-       **/
-      SignedMigrationNotAllowed: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    structure: {
-      /**
-       * While nesting, reached the breadth limit of nesting, exceeding the provided budget.
-       **/
-      BreadthLimit: AugmentedError<ApiType>;
-      /**
-       * Tried to nest token under collection contract address, instead of token address
-       **/
-      CantNestTokenUnderCollection: AugmentedError<ApiType>;
-      /**
-       * While nesting, reached the depth limit of nesting, exceeding the provided budget.
-       **/
-      DepthLimit: AugmentedError<ApiType>;
-      /**
-       * While nesting, encountered an already checked account, detecting a loop.
-       **/
-      OuroborosDetected: AugmentedError<ApiType>;
-      /**
-       * Couldn't find the token owner that is itself a token.
-       **/
-      TokenNotFound: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    sudo: {
-      /**
-       * Sender must be the Sudo account
-       **/
-      RequireSudo: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    system: {
-      /**
-       * The origin filter prevent the call to be dispatched.
-       **/
-      CallFiltered: AugmentedError<ApiType>;
-      /**
-       * Failed to extract the runtime version from the new runtime.
-       * 
-       * Either calling `Core_version` or decoding `RuntimeVersion` failed.
-       **/
-      FailedToExtractRuntimeVersion: AugmentedError<ApiType>;
-      /**
-       * The name of specification does not match between the current runtime
-       * and the new runtime.
-       **/
-      InvalidSpecName: AugmentedError<ApiType>;
-      /**
-       * Suicide called when the account has non-default composite data.
-       **/
-      NonDefaultComposite: AugmentedError<ApiType>;
-      /**
-       * There is a non-zero reference count preventing the account from being purged.
-       **/
-      NonZeroRefCount: AugmentedError<ApiType>;
-      /**
-       * The specification version is not allowed to decrease between the current runtime
-       * and the new runtime.
-       **/
-      SpecVersionNeedsToIncrease: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    technicalCommittee: {
-      /**
-       * Members are already initialized!
-       **/
-      AlreadyInitialized: AugmentedError<ApiType>;
-      /**
-       * Duplicate proposals not allowed
-       **/
-      DuplicateProposal: AugmentedError<ApiType>;
-      /**
-       * Duplicate vote ignored
-       **/
-      DuplicateVote: AugmentedError<ApiType>;
-      /**
-       * Account is not a member
-       **/
-      NotMember: AugmentedError<ApiType>;
-      /**
-       * Prime account is not a member
-       **/
-      PrimeAccountNotMember: AugmentedError<ApiType>;
-      /**
-       * Proposal must exist
-       **/
-      ProposalMissing: AugmentedError<ApiType>;
-      /**
-       * The close call was made too early, before the end of the voting.
-       **/
-      TooEarly: AugmentedError<ApiType>;
-      /**
-       * There can only be a maximum of `MaxProposals` active proposals.
-       **/
-      TooManyProposals: AugmentedError<ApiType>;
-      /**
-       * Mismatched index
-       **/
-      WrongIndex: AugmentedError<ApiType>;
-      /**
-       * The given length bound for the proposal was too low.
-       **/
-      WrongProposalLength: AugmentedError<ApiType>;
-      /**
-       * The given weight bound for the proposal was too low.
-       **/
-      WrongProposalWeight: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    technicalCommitteeMembership: {
-      /**
-       * Already a member.
-       **/
-      AlreadyMember: AugmentedError<ApiType>;
-      /**
-       * Not a member.
-       **/
-      NotMember: AugmentedError<ApiType>;
-      /**
-       * Too many members.
-       **/
-      TooManyMembers: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    testUtils: {
-      TestPalletDisabled: AugmentedError<ApiType>;
-      TriggerRollback: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    tokens: {
-      /**
-       * Cannot convert Amount into Balance type
-       **/
-      AmountIntoBalanceFailed: AugmentedError<ApiType>;
-      /**
-       * The balance is too low
-       **/
-      BalanceTooLow: AugmentedError<ApiType>;
-      /**
-       * Beneficiary account must pre-exist
-       **/
-      DeadAccount: AugmentedError<ApiType>;
-      /**
-       * Value too low to create account due to existential deposit
-       **/
-      ExistentialDeposit: AugmentedError<ApiType>;
-      /**
-       * Transfer/payment would kill account
-       **/
-      KeepAlive: AugmentedError<ApiType>;
-      /**
-       * Failed because liquidity restrictions due to locking
-       **/
-      LiquidityRestrictions: AugmentedError<ApiType>;
-      /**
-       * Failed because the maximum locks was exceeded
-       **/
-      MaxLocksExceeded: AugmentedError<ApiType>;
-      TooManyReserves: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    treasury: {
-      /**
-       * The spend origin is valid but the amount it is allowed to spend is lower than the
-       * amount to be spent.
-       **/
-      InsufficientPermission: AugmentedError<ApiType>;
-      /**
-       * Proposer's balance is too low.
-       **/
-      InsufficientProposersBalance: AugmentedError<ApiType>;
-      /**
-       * No proposal or bounty at that index.
-       **/
-      InvalidIndex: AugmentedError<ApiType>;
-      /**
-       * Proposal has not been approved.
-       **/
-      ProposalNotApproved: AugmentedError<ApiType>;
-      /**
-       * Too many approvals in the queue.
-       **/
-      TooManyApprovals: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    unique: {
-      /**
-       * Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].
-       **/
-      CollectionDecimalPointLimitExceeded: AugmentedError<ApiType>;
-      /**
-       * Length of items properties must be greater than 0.
-       **/
-      EmptyArgument: AugmentedError<ApiType>;
-      /**
-       * Repertition is only supported by refungible collection.
-       **/
-      RepartitionCalledOnNonRefungibleCollection: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    utility: {
-      /**
-       * Too many calls batched.
-       **/
-      TooManyCalls: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    vesting: {
-      /**
-       * The vested transfer amount is too low
-       **/
-      AmountLow: AugmentedError<ApiType>;
-      /**
-       * Insufficient amount of balance to lock
-       **/
-      InsufficientBalanceToLock: AugmentedError<ApiType>;
-      /**
-       * Failed because the maximum vesting schedules was exceeded
-       **/
-      MaxVestingSchedulesExceeded: AugmentedError<ApiType>;
-      /**
-       * This account have too many vesting schedules
-       **/
-      TooManyVestingSchedules: AugmentedError<ApiType>;
-      /**
-       * Vesting period is zero
-       **/
-      ZeroVestingPeriod: AugmentedError<ApiType>;
-      /**
-       * Number of vests is zero
-       **/
-      ZeroVestingPeriodCount: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    xcmpQueue: {
-      /**
-       * Bad overweight index.
-       **/
-      BadOverweightIndex: AugmentedError<ApiType>;
-      /**
-       * Bad XCM data.
-       **/
-      BadXcm: AugmentedError<ApiType>;
-      /**
-       * Bad XCM origin.
-       **/
-      BadXcmOrigin: AugmentedError<ApiType>;
-      /**
-       * Failed to send XCM message.
-       **/
-      FailedToSend: AugmentedError<ApiType>;
-      /**
-       * Provided weight is possibly not enough to execute the message.
-       **/
-      WeightOverLimit: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-    xTokens: {
-      /**
-       * Asset has no reserve location.
-       **/
-      AssetHasNoReserve: AugmentedError<ApiType>;
-      /**
-       * The specified index does not exist in a MultiAssets struct.
-       **/
-      AssetIndexNonExistent: AugmentedError<ApiType>;
-      /**
-       * The version of the `Versioned` value used is not able to be
-       * interpreted.
-       **/
-      BadVersion: AugmentedError<ApiType>;
-      /**
-       * Could not re-anchor the assets to declare the fees for the
-       * destination chain.
-       **/
-      CannotReanchor: AugmentedError<ApiType>;
-      /**
-       * The destination `MultiLocation` provided cannot be inverted.
-       **/
-      DestinationNotInvertible: AugmentedError<ApiType>;
-      /**
-       * We tried sending distinct asset and fee but they have different
-       * reserve chains.
-       **/
-      DistinctReserveForAssetAndFee: AugmentedError<ApiType>;
-      /**
-       * Fee is not enough.
-       **/
-      FeeNotEnough: AugmentedError<ApiType>;
-      /**
-       * Could not get ancestry of asset reserve location.
-       **/
-      InvalidAncestry: AugmentedError<ApiType>;
-      /**
-       * The MultiAsset is invalid.
-       **/
-      InvalidAsset: AugmentedError<ApiType>;
-      /**
-       * Invalid transfer destination.
-       **/
-      InvalidDest: AugmentedError<ApiType>;
-      /**
-       * MinXcmFee not registered for certain reserve location
-       **/
-      MinXcmFeeNotDefined: AugmentedError<ApiType>;
-      /**
-       * Not cross-chain transfer.
-       **/
-      NotCrossChainTransfer: AugmentedError<ApiType>;
-      /**
-       * Currency is not cross-chain transferable.
-       **/
-      NotCrossChainTransferableCurrency: AugmentedError<ApiType>;
-      /**
-       * Not supported MultiLocation
-       **/
-      NotSupportedMultiLocation: AugmentedError<ApiType>;
-      /**
-       * The number of assets to be sent is over the maximum.
-       **/
-      TooManyAssetsBeingSent: AugmentedError<ApiType>;
-      /**
-       * The message's weight could not be determined.
-       **/
-      UnweighableMessage: AugmentedError<ApiType>;
-      /**
-       * XCM execution failed.
-       **/
-      XcmExecutionFailed: AugmentedError<ApiType>;
-      /**
-       * The transfering asset amount is zero.
-       **/
-      ZeroAmount: AugmentedError<ApiType>;
-      /**
-       * The fee is zero.
-       **/
-      ZeroFee: AugmentedError<ApiType>;
-      /**
-       * Generic error
-       **/
-      [key: string]: AugmentedError<ApiType>;
-    };
-  } // AugmentedErrors
-} // declare module
deletedjs-packages/types/src/augment-api-events.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api-events.ts
+++ /dev/null
@@ -1,1294 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/api-base/types/events';
-
-import type { ApiTypes, AugmentedEvent } from '@polkadot/api-base/types';
-import type { Bytes, Null, Option, Result, U8aFixed, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
-import type { ITuple } from '@polkadot/types-codec/types';
-import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime';
-import type { EthereumLog, EvmCoreErrorExitReason, FrameSupportDispatchDispatchInfo, FrameSupportPreimagesBounded, FrameSupportTokensMiscBalanceStatus, OrmlVestingVestingSchedule, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletDemocracyVoteThreshold, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletStateTrieMigrationError, PalletStateTrieMigrationMigrationCompute, SpRuntimeDispatchError, SpWeightsWeightV2Weight, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetMultiAssets, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3Xcm, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation } from '@polkadot/types/lookup';
-
-export type __AugmentedEvent<ApiType extends ApiTypes> = AugmentedEvent<ApiType>;
-
-declare module '@polkadot/api-base/types/events' {
-  interface AugmentedEvents<ApiType extends ApiTypes> {
-    appPromotion: {
-      /**
-       * The admin was set
-       * 
-       * # Arguments
-       * * AccountId: account address of the admin
-       **/
-      SetAdmin: AugmentedEvent<ApiType, [AccountId32]>;
-      /**
-       * Staking was performed
-       * 
-       * # Arguments
-       * * AccountId: account of the staker
-       * * Balance : staking amount
-       **/
-      Stake: AugmentedEvent<ApiType, [AccountId32, u128]>;
-      /**
-       * Staking recalculation was performed
-       * 
-       * # Arguments
-       * * AccountId: account of the staker.
-       * * Balance : recalculation base
-       * * Balance : total income
-       **/
-      StakingRecalculation: AugmentedEvent<ApiType, [AccountId32, u128, u128]>;
-      /**
-       * Unstaking was performed
-       * 
-       * # Arguments
-       * * AccountId: account of the staker
-       * * Balance : unstaking amount
-       **/
-      Unstake: AugmentedEvent<ApiType, [AccountId32, u128]>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    balances: {
-      /**
-       * A balance was set by root.
-       **/
-      BalanceSet: AugmentedEvent<ApiType, [who: AccountId32, free: u128], { who: AccountId32, free: u128 }>;
-      /**
-       * Some amount was burned from an account.
-       **/
-      Burned: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Some amount was deposited (e.g. for transaction fees).
-       **/
-      Deposit: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * An account was removed whose balance was non-zero but below ExistentialDeposit,
-       * resulting in an outright loss.
-       **/
-      DustLost: AugmentedEvent<ApiType, [account: AccountId32, amount: u128], { account: AccountId32, amount: u128 }>;
-      /**
-       * An account was created with some free balance.
-       **/
-      Endowed: AugmentedEvent<ApiType, [account: AccountId32, freeBalance: u128], { account: AccountId32, freeBalance: u128 }>;
-      /**
-       * Some balance was frozen.
-       **/
-      Frozen: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Total issuance was increased by `amount`, creating a credit to be balanced.
-       **/
-      Issued: AugmentedEvent<ApiType, [amount: u128], { amount: u128 }>;
-      /**
-       * Some balance was locked.
-       **/
-      Locked: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Some amount was minted into an account.
-       **/
-      Minted: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Total issuance was decreased by `amount`, creating a debt to be balanced.
-       **/
-      Rescinded: AugmentedEvent<ApiType, [amount: u128], { amount: u128 }>;
-      /**
-       * Some balance was reserved (moved from free to reserved).
-       **/
-      Reserved: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Some balance was moved from the reserve of the first account to the second account.
-       * Final argument indicates the destination balance type.
-       **/
-      ReserveRepatriated: AugmentedEvent<ApiType, [from: AccountId32, to: AccountId32, amount: u128, destinationStatus: FrameSupportTokensMiscBalanceStatus], { from: AccountId32, to: AccountId32, amount: u128, destinationStatus: FrameSupportTokensMiscBalanceStatus }>;
-      /**
-       * Some amount was restored into an account.
-       **/
-      Restored: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Some amount was removed from the account (e.g. for misbehavior).
-       **/
-      Slashed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Some amount was suspended from an account (it can be restored later).
-       **/
-      Suspended: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Some balance was thawed.
-       **/
-      Thawed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Transfer succeeded.
-       **/
-      Transfer: AugmentedEvent<ApiType, [from: AccountId32, to: AccountId32, amount: u128], { from: AccountId32, to: AccountId32, amount: u128 }>;
-      /**
-       * Some balance was unlocked.
-       **/
-      Unlocked: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Some balance was unreserved (moved from reserved to free).
-       **/
-      Unreserved: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * An account was upgraded.
-       **/
-      Upgraded: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
-      /**
-       * Some amount was withdrawn from the account (e.g. for transaction fees).
-       **/
-      Withdraw: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    collatorSelection: {
-      CandidateAdded: AugmentedEvent<ApiType, [accountId: AccountId32], { accountId: AccountId32 }>;
-      CandidateRemoved: AugmentedEvent<ApiType, [accountId: AccountId32], { accountId: AccountId32 }>;
-      InvulnerableAdded: AugmentedEvent<ApiType, [invulnerable: AccountId32], { invulnerable: AccountId32 }>;
-      InvulnerableRemoved: AugmentedEvent<ApiType, [invulnerable: AccountId32], { invulnerable: AccountId32 }>;
-      LicenseObtained: AugmentedEvent<ApiType, [accountId: AccountId32, deposit: u128], { accountId: AccountId32, deposit: u128 }>;
-      LicenseReleased: AugmentedEvent<ApiType, [accountId: AccountId32, depositReturned: u128], { accountId: AccountId32, depositReturned: u128 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    common: {
-      /**
-       * Address was added to the allow list.
-       **/
-      AllowListAddressAdded: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Address was removed from the allow list.
-       **/
-      AllowListAddressRemoved: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Amount pieces of token owned by `sender` was approved for `spender`.
-       **/
-      Approved: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-      /**
-       * A `sender` approves operations on all owned tokens for `spender`.
-       **/
-      ApprovedForAll: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
-      /**
-       * Collection admin was added.
-       **/
-      CollectionAdminAdded: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Collection admin was removed.
-       **/
-      CollectionAdminRemoved: AugmentedEvent<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * New collection was created
-       **/
-      CollectionCreated: AugmentedEvent<ApiType, [u32, u8, AccountId32]>;
-      /**
-       * New collection was destroyed
-       **/
-      CollectionDestroyed: AugmentedEvent<ApiType, [u32]>;
-      /**
-       * Collection limits were set.
-       **/
-      CollectionLimitSet: AugmentedEvent<ApiType, [u32]>;
-      /**
-       * Collection owned was changed.
-       **/
-      CollectionOwnerChanged: AugmentedEvent<ApiType, [u32, AccountId32]>;
-      /**
-       * Collection permissions were set.
-       **/
-      CollectionPermissionSet: AugmentedEvent<ApiType, [u32]>;
-      /**
-       * The property has been deleted.
-       **/
-      CollectionPropertyDeleted: AugmentedEvent<ApiType, [u32, Bytes]>;
-      /**
-       * The colletion property has been added or edited.
-       **/
-      CollectionPropertySet: AugmentedEvent<ApiType, [u32, Bytes]>;
-      /**
-       * Collection sponsor was removed.
-       **/
-      CollectionSponsorRemoved: AugmentedEvent<ApiType, [u32]>;
-      /**
-       * Collection sponsor was set.
-       **/
-      CollectionSponsorSet: AugmentedEvent<ApiType, [u32, AccountId32]>;
-      /**
-       * New item was created.
-       **/
-      ItemCreated: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-      /**
-       * Collection item was burned.
-       **/
-      ItemDestroyed: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-      /**
-       * The token property permission of a collection has been set.
-       **/
-      PropertyPermissionSet: AugmentedEvent<ApiType, [u32, Bytes]>;
-      /**
-       * New sponsor was confirm.
-       **/
-      SponsorshipConfirmed: AugmentedEvent<ApiType, [u32, AccountId32]>;
-      /**
-       * The token property has been deleted.
-       **/
-      TokenPropertyDeleted: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
-      /**
-       * The token property has been added or edited.
-       **/
-      TokenPropertySet: AugmentedEvent<ApiType, [u32, u32, Bytes]>;
-      /**
-       * Item was transferred
-       **/
-      Transfer: AugmentedEvent<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    configuration: {
-      NewCollatorKickThreshold: AugmentedEvent<ApiType, [lengthInBlocks: Option<u32>], { lengthInBlocks: Option<u32> }>;
-      NewCollatorLicenseBond: AugmentedEvent<ApiType, [bondCost: Option<u128>], { bondCost: Option<u128> }>;
-      NewDesiredCollators: AugmentedEvent<ApiType, [desiredCollators: Option<u32>], { desiredCollators: Option<u32> }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    council: {
-      /**
-       * A motion was approved by the required threshold.
-       **/
-      Approved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
-      /**
-       * A proposal was closed because its threshold was reached or after its duration was up.
-       **/
-      Closed: AugmentedEvent<ApiType, [proposalHash: H256, yes: u32, no: u32], { proposalHash: H256, yes: u32, no: u32 }>;
-      /**
-       * A motion was not approved by the required threshold.
-       **/
-      Disapproved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
-      /**
-       * A motion was executed; result will be `Ok` if it returned without error.
-       **/
-      Executed: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * A single member did some action; result will be `Ok` if it returned without error.
-       **/
-      MemberExecuted: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * A motion (given hash) has been proposed (by given account) with a threshold (given
-       * `MemberCount`).
-       **/
-      Proposed: AugmentedEvent<ApiType, [account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32], { account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32 }>;
-      /**
-       * A motion (given hash) has been voted on by given account, leaving
-       * a tally (yes votes and no votes given respectively as `MemberCount`).
-       **/
-      Voted: AugmentedEvent<ApiType, [account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32], { account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    councilMembership: {
-      /**
-       * Phantom member, never used.
-       **/
-      Dummy: AugmentedEvent<ApiType, []>;
-      /**
-       * One of the members' keys changed.
-       **/
-      KeyChanged: AugmentedEvent<ApiType, []>;
-      /**
-       * The given member was added; see the transaction for who.
-       **/
-      MemberAdded: AugmentedEvent<ApiType, []>;
-      /**
-       * The given member was removed; see the transaction for who.
-       **/
-      MemberRemoved: AugmentedEvent<ApiType, []>;
-      /**
-       * The membership was reset; see the transaction for who the new set is.
-       **/
-      MembersReset: AugmentedEvent<ApiType, []>;
-      /**
-       * Two members were swapped; see the transaction for who.
-       **/
-      MembersSwapped: AugmentedEvent<ApiType, []>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    cumulusXcm: {
-      /**
-       * Downward message executed with the given outcome.
-       * \[ id, outcome \]
-       **/
-      ExecutedDownward: AugmentedEvent<ApiType, [U8aFixed, StagingXcmV3TraitsOutcome]>;
-      /**
-       * Downward message is invalid XCM.
-       * \[ id \]
-       **/
-      InvalidFormat: AugmentedEvent<ApiType, [U8aFixed]>;
-      /**
-       * Downward message is unsupported version of XCM.
-       * \[ id \]
-       **/
-      UnsupportedVersion: AugmentedEvent<ApiType, [U8aFixed]>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    democracy: {
-      /**
-       * A proposal_hash has been blacklisted permanently.
-       **/
-      Blacklisted: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
-      /**
-       * A referendum has been cancelled.
-       **/
-      Cancelled: AugmentedEvent<ApiType, [refIndex: u32], { refIndex: u32 }>;
-      /**
-       * An account has delegated their vote to another account.
-       **/
-      Delegated: AugmentedEvent<ApiType, [who: AccountId32, target: AccountId32], { who: AccountId32, target: AccountId32 }>;
-      /**
-       * An external proposal has been tabled.
-       **/
-      ExternalTabled: AugmentedEvent<ApiType, []>;
-      /**
-       * Metadata for a proposal or a referendum has been cleared.
-       **/
-      MetadataCleared: AugmentedEvent<ApiType, [owner: PalletDemocracyMetadataOwner, hash_: H256], { owner: PalletDemocracyMetadataOwner, hash_: H256 }>;
-      /**
-       * Metadata for a proposal or a referendum has been set.
-       **/
-      MetadataSet: AugmentedEvent<ApiType, [owner: PalletDemocracyMetadataOwner, hash_: H256], { owner: PalletDemocracyMetadataOwner, hash_: H256 }>;
-      /**
-       * Metadata has been transferred to new owner.
-       **/
-      MetadataTransferred: AugmentedEvent<ApiType, [prevOwner: PalletDemocracyMetadataOwner, owner: PalletDemocracyMetadataOwner, hash_: H256], { prevOwner: PalletDemocracyMetadataOwner, owner: PalletDemocracyMetadataOwner, hash_: H256 }>;
-      /**
-       * A proposal has been rejected by referendum.
-       **/
-      NotPassed: AugmentedEvent<ApiType, [refIndex: u32], { refIndex: u32 }>;
-      /**
-       * A proposal has been approved by referendum.
-       **/
-      Passed: AugmentedEvent<ApiType, [refIndex: u32], { refIndex: u32 }>;
-      /**
-       * A proposal got canceled.
-       **/
-      ProposalCanceled: AugmentedEvent<ApiType, [propIndex: u32], { propIndex: u32 }>;
-      /**
-       * A motion has been proposed by a public account.
-       **/
-      Proposed: AugmentedEvent<ApiType, [proposalIndex: u32, deposit: u128], { proposalIndex: u32, deposit: u128 }>;
-      /**
-       * An account has secconded a proposal
-       **/
-      Seconded: AugmentedEvent<ApiType, [seconder: AccountId32, propIndex: u32], { seconder: AccountId32, propIndex: u32 }>;
-      /**
-       * A referendum has begun.
-       **/
-      Started: AugmentedEvent<ApiType, [refIndex: u32, threshold: PalletDemocracyVoteThreshold], { refIndex: u32, threshold: PalletDemocracyVoteThreshold }>;
-      /**
-       * A public proposal has been tabled for referendum vote.
-       **/
-      Tabled: AugmentedEvent<ApiType, [proposalIndex: u32, deposit: u128], { proposalIndex: u32, deposit: u128 }>;
-      /**
-       * An account has cancelled a previous delegation operation.
-       **/
-      Undelegated: AugmentedEvent<ApiType, [account: AccountId32], { account: AccountId32 }>;
-      /**
-       * An external proposal has been vetoed.
-       **/
-      Vetoed: AugmentedEvent<ApiType, [who: AccountId32, proposalHash: H256, until: u32], { who: AccountId32, proposalHash: H256, until: u32 }>;
-      /**
-       * An account has voted in a referendum
-       **/
-      Voted: AugmentedEvent<ApiType, [voter: AccountId32, refIndex: u32, vote: PalletDemocracyVoteAccountVote], { voter: AccountId32, refIndex: u32, vote: PalletDemocracyVoteAccountVote }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    dmpQueue: {
-      /**
-       * Downward message executed with the given outcome.
-       **/
-      ExecutedDownward: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, outcome: StagingXcmV3TraitsOutcome], { messageHash: U8aFixed, messageId: U8aFixed, outcome: StagingXcmV3TraitsOutcome }>;
-      /**
-       * Downward message is invalid XCM.
-       **/
-      InvalidFormat: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
-      /**
-       * The maximum number of downward messages was reached.
-       **/
-      MaxMessagesExhausted: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
-      /**
-       * Downward message is overweight and was placed in the overweight queue.
-       **/
-      OverweightEnqueued: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, overweightIndex: u64, requiredWeight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, overweightIndex: u64, requiredWeight: SpWeightsWeightV2Weight }>;
-      /**
-       * Downward message from the overweight queue was executed.
-       **/
-      OverweightServiced: AugmentedEvent<ApiType, [overweightIndex: u64, weightUsed: SpWeightsWeightV2Weight], { overweightIndex: u64, weightUsed: SpWeightsWeightV2Weight }>;
-      /**
-       * Downward message is unsupported version of XCM.
-       **/
-      UnsupportedVersion: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
-      /**
-       * The weight limit for handling downward messages was reached.
-       **/
-      WeightExhausted: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, remainingWeight: SpWeightsWeightV2Weight, requiredWeight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, remainingWeight: SpWeightsWeightV2Weight, requiredWeight: SpWeightsWeightV2Weight }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    ethereum: {
-      /**
-       * An ethereum transaction was successfully executed.
-       **/
-      Executed: AugmentedEvent<ApiType, [from: H160, to: H160, transactionHash: H256, exitReason: EvmCoreErrorExitReason, extraData: Bytes], { from: H160, to: H160, transactionHash: H256, exitReason: EvmCoreErrorExitReason, extraData: Bytes }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    evm: {
-      /**
-       * A contract has been created at given address.
-       **/
-      Created: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
-      /**
-       * A contract was attempted to be created, but the execution failed.
-       **/
-      CreatedFailed: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
-      /**
-       * A contract has been executed successfully with states applied.
-       **/
-      Executed: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
-      /**
-       * A contract has been executed with errors. States are reverted with only gas fees applied.
-       **/
-      ExecutedFailed: AugmentedEvent<ApiType, [address: H160], { address: H160 }>;
-      /**
-       * Ethereum events from contracts.
-       **/
-      Log: AugmentedEvent<ApiType, [log: EthereumLog], { log: EthereumLog }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    evmContractHelpers: {
-      /**
-       * Collection sponsor was removed.
-       **/
-      ContractSponsorRemoved: AugmentedEvent<ApiType, [H160]>;
-      /**
-       * Contract sponsor was set.
-       **/
-      ContractSponsorSet: AugmentedEvent<ApiType, [H160, AccountId32]>;
-      /**
-       * New sponsor was confirm.
-       **/
-      ContractSponsorshipConfirmed: AugmentedEvent<ApiType, [H160, AccountId32]>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    evmMigration: {
-      /**
-       * This event is used in benchmarking and can be used for tests
-       **/
-      TestEvent: AugmentedEvent<ApiType, []>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    fellowshipCollective: {
-      /**
-       * A member `who` has been added.
-       **/
-      MemberAdded: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
-      /**
-       * The member `who` of given `rank` has been removed from the collective.
-       **/
-      MemberRemoved: AugmentedEvent<ApiType, [who: AccountId32, rank: u16], { who: AccountId32, rank: u16 }>;
-      /**
-       * The member `who`se rank has been changed to the given `rank`.
-       **/
-      RankChanged: AugmentedEvent<ApiType, [who: AccountId32, rank: u16], { who: AccountId32, rank: u16 }>;
-      /**
-       * The member `who` has voted for the `poll` with the given `vote` leading to an updated
-       * `tally`.
-       **/
-      Voted: AugmentedEvent<ApiType, [who: AccountId32, poll: u32, vote: PalletRankedCollectiveVoteRecord, tally: PalletRankedCollectiveTally], { who: AccountId32, poll: u32, vote: PalletRankedCollectiveVoteRecord, tally: PalletRankedCollectiveTally }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    fellowshipReferenda: {
-      /**
-       * A referendum has been approved and its proposal has been scheduled.
-       **/
-      Approved: AugmentedEvent<ApiType, [index: u32], { index: u32 }>;
-      /**
-       * A referendum has been cancelled.
-       **/
-      Cancelled: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
-      ConfirmAborted: AugmentedEvent<ApiType, [index: u32], { index: u32 }>;
-      /**
-       * A referendum has ended its confirmation phase and is ready for approval.
-       **/
-      Confirmed: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
-      ConfirmStarted: AugmentedEvent<ApiType, [index: u32], { index: u32 }>;
-      /**
-       * The decision deposit has been placed.
-       **/
-      DecisionDepositPlaced: AugmentedEvent<ApiType, [index: u32, who: AccountId32, amount: u128], { index: u32, who: AccountId32, amount: u128 }>;
-      /**
-       * The decision deposit has been refunded.
-       **/
-      DecisionDepositRefunded: AugmentedEvent<ApiType, [index: u32, who: AccountId32, amount: u128], { index: u32, who: AccountId32, amount: u128 }>;
-      /**
-       * A referendum has moved into the deciding phase.
-       **/
-      DecisionStarted: AugmentedEvent<ApiType, [index: u32, track: u16, proposal: FrameSupportPreimagesBounded, tally: PalletRankedCollectiveTally], { index: u32, track: u16, proposal: FrameSupportPreimagesBounded, tally: PalletRankedCollectiveTally }>;
-      /**
-       * A deposit has been slashaed.
-       **/
-      DepositSlashed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * A referendum has been killed.
-       **/
-      Killed: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
-      /**
-       * Metadata for a referendum has been cleared.
-       **/
-      MetadataCleared: AugmentedEvent<ApiType, [index: u32, hash_: H256], { index: u32, hash_: H256 }>;
-      /**
-       * Metadata for a referendum has been set.
-       **/
-      MetadataSet: AugmentedEvent<ApiType, [index: u32, hash_: H256], { index: u32, hash_: H256 }>;
-      /**
-       * A proposal has been rejected by referendum.
-       **/
-      Rejected: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
-      /**
-       * The submission deposit has been refunded.
-       **/
-      SubmissionDepositRefunded: AugmentedEvent<ApiType, [index: u32, who: AccountId32, amount: u128], { index: u32, who: AccountId32, amount: u128 }>;
-      /**
-       * A referendum has been submitted.
-       **/
-      Submitted: AugmentedEvent<ApiType, [index: u32, track: u16, proposal: FrameSupportPreimagesBounded], { index: u32, track: u16, proposal: FrameSupportPreimagesBounded }>;
-      /**
-       * A referendum has been timed out without being decided.
-       **/
-      TimedOut: AugmentedEvent<ApiType, [index: u32, tally: PalletRankedCollectiveTally], { index: u32, tally: PalletRankedCollectiveTally }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    foreignAssets: {
-      /**
-       * The asset registered.
-       **/
-      AssetRegistered: AugmentedEvent<ApiType, [assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata }>;
-      /**
-       * The asset updated.
-       **/
-      AssetUpdated: AugmentedEvent<ApiType, [assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: PalletForeignAssetsAssetId, metadata: PalletForeignAssetsModuleAssetMetadata }>;
-      /**
-       * The foreign asset registered.
-       **/
-      ForeignAssetRegistered: AugmentedEvent<ApiType, [assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata }>;
-      /**
-       * The foreign asset updated.
-       **/
-      ForeignAssetUpdated: AugmentedEvent<ApiType, [assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata], { assetId: u32, assetAddress: StagingXcmV3MultiLocation, metadata: PalletForeignAssetsModuleAssetMetadata }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    identity: {
-      /**
-       * A number of identities and associated info were forcibly inserted.
-       **/
-      IdentitiesInserted: AugmentedEvent<ApiType, [amount: u32], { amount: u32 }>;
-      /**
-       * A number of identities and all associated info were forcibly removed.
-       **/
-      IdentitiesRemoved: AugmentedEvent<ApiType, [amount: u32], { amount: u32 }>;
-      /**
-       * A name was cleared, and the given balance returned.
-       **/
-      IdentityCleared: AugmentedEvent<ApiType, [who: AccountId32, deposit: u128], { who: AccountId32, deposit: u128 }>;
-      /**
-       * A name was removed and the given balance slashed.
-       **/
-      IdentityKilled: AugmentedEvent<ApiType, [who: AccountId32, deposit: u128], { who: AccountId32, deposit: u128 }>;
-      /**
-       * A name was set or reset (which will remove all judgements).
-       **/
-      IdentitySet: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
-      /**
-       * A judgement was given by a registrar.
-       **/
-      JudgementGiven: AugmentedEvent<ApiType, [target: AccountId32, registrarIndex: u32], { target: AccountId32, registrarIndex: u32 }>;
-      /**
-       * A judgement was asked from a registrar.
-       **/
-      JudgementRequested: AugmentedEvent<ApiType, [who: AccountId32, registrarIndex: u32], { who: AccountId32, registrarIndex: u32 }>;
-      /**
-       * A judgement request was retracted.
-       **/
-      JudgementUnrequested: AugmentedEvent<ApiType, [who: AccountId32, registrarIndex: u32], { who: AccountId32, registrarIndex: u32 }>;
-      /**
-       * A registrar was added.
-       **/
-      RegistrarAdded: AugmentedEvent<ApiType, [registrarIndex: u32], { registrarIndex: u32 }>;
-      /**
-       * A number of identities were forcibly updated with new sub-identities.
-       **/
-      SubIdentitiesInserted: AugmentedEvent<ApiType, [amount: u32], { amount: u32 }>;
-      /**
-       * A sub-identity was added to an identity and the deposit paid.
-       **/
-      SubIdentityAdded: AugmentedEvent<ApiType, [sub: AccountId32, main: AccountId32, deposit: u128], { sub: AccountId32, main: AccountId32, deposit: u128 }>;
-      /**
-       * A sub-identity was removed from an identity and the deposit freed.
-       **/
-      SubIdentityRemoved: AugmentedEvent<ApiType, [sub: AccountId32, main: AccountId32, deposit: u128], { sub: AccountId32, main: AccountId32, deposit: u128 }>;
-      /**
-       * A sub-identity was cleared, and the given deposit repatriated from the
-       * main identity account to the sub-identity account.
-       **/
-      SubIdentityRevoked: AugmentedEvent<ApiType, [sub: AccountId32, main: AccountId32, deposit: u128], { sub: AccountId32, main: AccountId32, deposit: u128 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    maintenance: {
-      MaintenanceDisabled: AugmentedEvent<ApiType, []>;
-      MaintenanceEnabled: AugmentedEvent<ApiType, []>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    parachainSystem: {
-      /**
-       * Downward messages were processed using the given weight.
-       **/
-      DownwardMessagesProcessed: AugmentedEvent<ApiType, [weightUsed: SpWeightsWeightV2Weight, dmqHead: H256], { weightUsed: SpWeightsWeightV2Weight, dmqHead: H256 }>;
-      /**
-       * Some downward messages have been received and will be processed.
-       **/
-      DownwardMessagesReceived: AugmentedEvent<ApiType, [count: u32], { count: u32 }>;
-      /**
-       * An upgrade has been authorized.
-       **/
-      UpgradeAuthorized: AugmentedEvent<ApiType, [codeHash: H256], { codeHash: H256 }>;
-      /**
-       * An upward message was sent to the relay chain.
-       **/
-      UpwardMessageSent: AugmentedEvent<ApiType, [messageHash: Option<U8aFixed>], { messageHash: Option<U8aFixed> }>;
-      /**
-       * The validation function was applied as of the contained relay chain block number.
-       **/
-      ValidationFunctionApplied: AugmentedEvent<ApiType, [relayChainBlockNum: u32], { relayChainBlockNum: u32 }>;
-      /**
-       * The relay-chain aborted the upgrade process.
-       **/
-      ValidationFunctionDiscarded: AugmentedEvent<ApiType, []>;
-      /**
-       * The validation function has been scheduled to apply.
-       **/
-      ValidationFunctionStored: AugmentedEvent<ApiType, []>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    polkadotXcm: {
-      /**
-       * Some assets have been claimed from an asset trap
-       **/
-      AssetsClaimed: AugmentedEvent<ApiType, [hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets], { hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets }>;
-      /**
-       * Some assets have been placed in an asset trap.
-       **/
-      AssetsTrapped: AugmentedEvent<ApiType, [hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets], { hash_: H256, origin: StagingXcmV3MultiLocation, assets: StagingXcmVersionedMultiAssets }>;
-      /**
-       * Execution of an XCM message was attempted.
-       **/
-      Attempted: AugmentedEvent<ApiType, [outcome: StagingXcmV3TraitsOutcome], { outcome: StagingXcmV3TraitsOutcome }>;
-      /**
-       * Fees were paid from a location for an operation (often for using `SendXcm`).
-       **/
-      FeesPaid: AugmentedEvent<ApiType, [paying: StagingXcmV3MultiLocation, fees: StagingXcmV3MultiassetMultiAssets], { paying: StagingXcmV3MultiLocation, fees: StagingXcmV3MultiassetMultiAssets }>;
-      /**
-       * Expected query response has been received but the querier location of the response does
-       * not match the expected. The query remains registered for a later, valid, response to
-       * be received and acted upon.
-       **/
-      InvalidQuerier: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64, expectedQuerier: StagingXcmV3MultiLocation, maybeActualQuerier: Option<StagingXcmV3MultiLocation>], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedQuerier: StagingXcmV3MultiLocation, maybeActualQuerier: Option<StagingXcmV3MultiLocation> }>;
-      /**
-       * Expected query response has been received but the expected querier location placed in
-       * storage by this runtime previously cannot be decoded. The query remains registered.
-       * 
-       * This is unexpected (since a location placed in storage in a previously executing
-       * runtime should be readable prior to query timeout) and dangerous since the possibly
-       * valid response will be dropped. Manual governance intervention is probably going to be
-       * needed.
-       **/
-      InvalidQuerierVersion: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64], { origin: StagingXcmV3MultiLocation, queryId: u64 }>;
-      /**
-       * Expected query response has been received but the origin location of the response does
-       * not match that expected. The query remains registered for a later, valid, response to
-       * be received and acted upon.
-       **/
-      InvalidResponder: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64, expectedLocation: Option<StagingXcmV3MultiLocation>], { origin: StagingXcmV3MultiLocation, queryId: u64, expectedLocation: Option<StagingXcmV3MultiLocation> }>;
-      /**
-       * Expected query response has been received but the expected origin location placed in
-       * storage by this runtime previously cannot be decoded. The query remains registered.
-       * 
-       * This is unexpected (since a location placed in storage in a previously executing
-       * runtime should be readable prior to query timeout) and dangerous since the possibly
-       * valid response will be dropped. Manual governance intervention is probably going to be
-       * needed.
-       **/
-      InvalidResponderVersion: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64], { origin: StagingXcmV3MultiLocation, queryId: u64 }>;
-      /**
-       * Query response has been received and query is removed. The registered notification has
-       * been dispatched and executed successfully.
-       **/
-      Notified: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8], { queryId: u64, palletIndex: u8, callIndex: u8 }>;
-      /**
-       * Query response has been received and query is removed. The dispatch was unable to be
-       * decoded into a `Call`; this might be due to dispatch function having a signature which
-       * is not `(origin, QueryId, Response)`.
-       **/
-      NotifyDecodeFailed: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8], { queryId: u64, palletIndex: u8, callIndex: u8 }>;
-      /**
-       * Query response has been received and query is removed. There was a general error with
-       * dispatching the notification call.
-       **/
-      NotifyDispatchError: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8], { queryId: u64, palletIndex: u8, callIndex: u8 }>;
-      /**
-       * Query response has been received and query is removed. The registered notification
-       * could not be dispatched because the dispatch weight is greater than the maximum weight
-       * originally budgeted by this runtime for the query result.
-       **/
-      NotifyOverweight: AugmentedEvent<ApiType, [queryId: u64, palletIndex: u8, callIndex: u8, actualWeight: SpWeightsWeightV2Weight, maxBudgetedWeight: SpWeightsWeightV2Weight], { queryId: u64, palletIndex: u8, callIndex: u8, actualWeight: SpWeightsWeightV2Weight, maxBudgetedWeight: SpWeightsWeightV2Weight }>;
-      /**
-       * A given location which had a version change subscription was dropped owing to an error
-       * migrating the location to our new XCM format.
-       **/
-      NotifyTargetMigrationFail: AugmentedEvent<ApiType, [location: StagingXcmVersionedMultiLocation, queryId: u64], { location: StagingXcmVersionedMultiLocation, queryId: u64 }>;
-      /**
-       * A given location which had a version change subscription was dropped owing to an error
-       * sending the notification to it.
-       **/
-      NotifyTargetSendFail: AugmentedEvent<ApiType, [location: StagingXcmV3MultiLocation, queryId: u64, error: StagingXcmV3TraitsError], { location: StagingXcmV3MultiLocation, queryId: u64, error: StagingXcmV3TraitsError }>;
-      /**
-       * Query response has been received and is ready for taking with `take_response`. There is
-       * no registered notification call.
-       **/
-      ResponseReady: AugmentedEvent<ApiType, [queryId: u64, response: StagingXcmV3Response], { queryId: u64, response: StagingXcmV3Response }>;
-      /**
-       * Received query response has been read and removed.
-       **/
-      ResponseTaken: AugmentedEvent<ApiType, [queryId: u64], { queryId: u64 }>;
-      /**
-       * A XCM message was sent.
-       **/
-      Sent: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, destination: StagingXcmV3MultiLocation, message: StagingXcmV3Xcm, messageId: U8aFixed], { origin: StagingXcmV3MultiLocation, destination: StagingXcmV3MultiLocation, message: StagingXcmV3Xcm, messageId: U8aFixed }>;
-      /**
-       * The supported version of a location has been changed. This might be through an
-       * automatic notification or a manual intervention.
-       **/
-      SupportedVersionChanged: AugmentedEvent<ApiType, [location: StagingXcmV3MultiLocation, version: u32], { location: StagingXcmV3MultiLocation, version: u32 }>;
-      /**
-       * Query response received which does not match a registered query. This may be because a
-       * matching query was never registered, it may be because it is a duplicate response, or
-       * because the query timed out.
-       **/
-      UnexpectedResponse: AugmentedEvent<ApiType, [origin: StagingXcmV3MultiLocation, queryId: u64], { origin: StagingXcmV3MultiLocation, queryId: u64 }>;
-      /**
-       * An XCM version change notification message has been attempted to be sent.
-       * 
-       * The cost of sending it (borne by the chain) is included.
-       **/
-      VersionChangeNotified: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, result: u32, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, result: u32, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
-      /**
-       * We have requested that a remote chain send us XCM version change notifications.
-       **/
-      VersionNotifyRequested: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
-      /**
-       * A remote has requested XCM version change notification from us and we have honored it.
-       * A version information message is sent to them and its cost is included.
-       **/
-      VersionNotifyStarted: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
-      /**
-       * We have requested that a remote chain stops sending us XCM version change
-       * notifications.
-       **/
-      VersionNotifyUnrequested: AugmentedEvent<ApiType, [destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed], { destination: StagingXcmV3MultiLocation, cost: StagingXcmV3MultiassetMultiAssets, messageId: U8aFixed }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    preimage: {
-      /**
-       * A preimage has ben cleared.
-       **/
-      Cleared: AugmentedEvent<ApiType, [hash_: H256], { hash_: H256 }>;
-      /**
-       * A preimage has been noted.
-       **/
-      Noted: AugmentedEvent<ApiType, [hash_: H256], { hash_: H256 }>;
-      /**
-       * A preimage has been requested.
-       **/
-      Requested: AugmentedEvent<ApiType, [hash_: H256], { hash_: H256 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    scheduler: {
-      /**
-       * The call for the provided hash was not found so the task has been aborted.
-       **/
-      CallUnavailable: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed> }>;
-      /**
-       * Canceled some task.
-       **/
-      Canceled: AugmentedEvent<ApiType, [when: u32, index: u32], { when: u32, index: u32 }>;
-      /**
-       * Dispatched some task.
-       **/
-      Dispatched: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>, result: Result<Null, SpRuntimeDispatchError>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed>, result: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * The given task was unable to be renewed since the agenda is full at that block.
-       **/
-      PeriodicFailed: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed> }>;
-      /**
-       * The given task can never be executed since it is overweight.
-       **/
-      PermanentlyOverweight: AugmentedEvent<ApiType, [task: ITuple<[u32, u32]>, id: Option<U8aFixed>], { task: ITuple<[u32, u32]>, id: Option<U8aFixed> }>;
-      /**
-       * Scheduled some task.
-       **/
-      Scheduled: AugmentedEvent<ApiType, [when: u32, index: u32], { when: u32, index: u32 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    session: {
-      /**
-       * New session has happened. Note that the argument is the session index, not the
-       * block number as the type might suggest.
-       **/
-      NewSession: AugmentedEvent<ApiType, [sessionIndex: u32], { sessionIndex: u32 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    stateTrieMigration: {
-      /**
-       * The auto migration task finished.
-       **/
-      AutoMigrationFinished: AugmentedEvent<ApiType, []>;
-      /**
-       * Migration got halted due to an error or miss-configuration.
-       **/
-      Halted: AugmentedEvent<ApiType, [error: PalletStateTrieMigrationError], { error: PalletStateTrieMigrationError }>;
-      /**
-       * Given number of `(top, child)` keys were migrated respectively, with the given
-       * `compute`.
-       **/
-      Migrated: AugmentedEvent<ApiType, [top: u32, child: u32, compute: PalletStateTrieMigrationMigrationCompute], { top: u32, child: u32, compute: PalletStateTrieMigrationMigrationCompute }>;
-      /**
-       * Some account got slashed by the given amount.
-       **/
-      Slashed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    structure: {
-      /**
-       * Executed call on behalf of the token.
-       **/
-      Executed: AugmentedEvent<ApiType, [Result<Null, SpRuntimeDispatchError>]>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    sudo: {
-      /**
-       * The \[sudoer\] just switched identity; the old key is supplied if one existed.
-       **/
-      KeyChanged: AugmentedEvent<ApiType, [oldSudoer: Option<AccountId32>], { oldSudoer: Option<AccountId32> }>;
-      /**
-       * A sudo just took place. \[result\]
-       **/
-      Sudid: AugmentedEvent<ApiType, [sudoResult: Result<Null, SpRuntimeDispatchError>], { sudoResult: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * A sudo just took place. \[result\]
-       **/
-      SudoAsDone: AugmentedEvent<ApiType, [sudoResult: Result<Null, SpRuntimeDispatchError>], { sudoResult: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    system: {
-      /**
-       * `:code` was updated.
-       **/
-      CodeUpdated: AugmentedEvent<ApiType, []>;
-      /**
-       * An extrinsic failed.
-       **/
-      ExtrinsicFailed: AugmentedEvent<ApiType, [dispatchError: SpRuntimeDispatchError, dispatchInfo: FrameSupportDispatchDispatchInfo], { dispatchError: SpRuntimeDispatchError, dispatchInfo: FrameSupportDispatchDispatchInfo }>;
-      /**
-       * An extrinsic completed successfully.
-       **/
-      ExtrinsicSuccess: AugmentedEvent<ApiType, [dispatchInfo: FrameSupportDispatchDispatchInfo], { dispatchInfo: FrameSupportDispatchDispatchInfo }>;
-      /**
-       * An account was reaped.
-       **/
-      KilledAccount: AugmentedEvent<ApiType, [account: AccountId32], { account: AccountId32 }>;
-      /**
-       * A new account was created.
-       **/
-      NewAccount: AugmentedEvent<ApiType, [account: AccountId32], { account: AccountId32 }>;
-      /**
-       * On on-chain remark happened.
-       **/
-      Remarked: AugmentedEvent<ApiType, [sender: AccountId32, hash_: H256], { sender: AccountId32, hash_: H256 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    technicalCommittee: {
-      /**
-       * A motion was approved by the required threshold.
-       **/
-      Approved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
-      /**
-       * A proposal was closed because its threshold was reached or after its duration was up.
-       **/
-      Closed: AugmentedEvent<ApiType, [proposalHash: H256, yes: u32, no: u32], { proposalHash: H256, yes: u32, no: u32 }>;
-      /**
-       * A motion was not approved by the required threshold.
-       **/
-      Disapproved: AugmentedEvent<ApiType, [proposalHash: H256], { proposalHash: H256 }>;
-      /**
-       * A motion was executed; result will be `Ok` if it returned without error.
-       **/
-      Executed: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * A single member did some action; result will be `Ok` if it returned without error.
-       **/
-      MemberExecuted: AugmentedEvent<ApiType, [proposalHash: H256, result: Result<Null, SpRuntimeDispatchError>], { proposalHash: H256, result: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * A motion (given hash) has been proposed (by given account) with a threshold (given
-       * `MemberCount`).
-       **/
-      Proposed: AugmentedEvent<ApiType, [account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32], { account: AccountId32, proposalIndex: u32, proposalHash: H256, threshold: u32 }>;
-      /**
-       * A motion (given hash) has been voted on by given account, leaving
-       * a tally (yes votes and no votes given respectively as `MemberCount`).
-       **/
-      Voted: AugmentedEvent<ApiType, [account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32], { account: AccountId32, proposalHash: H256, voted: bool, yes: u32, no: u32 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    technicalCommitteeMembership: {
-      /**
-       * Phantom member, never used.
-       **/
-      Dummy: AugmentedEvent<ApiType, []>;
-      /**
-       * One of the members' keys changed.
-       **/
-      KeyChanged: AugmentedEvent<ApiType, []>;
-      /**
-       * The given member was added; see the transaction for who.
-       **/
-      MemberAdded: AugmentedEvent<ApiType, []>;
-      /**
-       * The given member was removed; see the transaction for who.
-       **/
-      MemberRemoved: AugmentedEvent<ApiType, []>;
-      /**
-       * The membership was reset; see the transaction for who the new set is.
-       **/
-      MembersReset: AugmentedEvent<ApiType, []>;
-      /**
-       * Two members were swapped; see the transaction for who.
-       **/
-      MembersSwapped: AugmentedEvent<ApiType, []>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    testUtils: {
-      BatchCompleted: AugmentedEvent<ApiType, []>;
-      ShouldRollback: AugmentedEvent<ApiType, []>;
-      ValueIsSet: AugmentedEvent<ApiType, []>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    tokens: {
-      /**
-       * A balance was set by root.
-       **/
-      BalanceSet: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, free: u128, reserved: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, free: u128, reserved: u128 }>;
-      /**
-       * Deposited some balance into an account
-       **/
-      Deposited: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      /**
-       * An account was removed whose balance was non-zero but below
-       * ExistentialDeposit, resulting in an outright loss.
-       **/
-      DustLost: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      /**
-       * An account was created with some free balance.
-       **/
-      Endowed: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      Issued: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, amount: u128], { currencyId: PalletForeignAssetsAssetId, amount: u128 }>;
-      /**
-       * Some free balance was locked.
-       **/
-      Locked: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      /**
-       * Some locked funds were unlocked
-       **/
-      LockRemoved: AugmentedEvent<ApiType, [lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32], { lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32 }>;
-      /**
-       * Some funds are locked
-       **/
-      LockSet: AugmentedEvent<ApiType, [lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { lockId: U8aFixed, currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      Rescinded: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, amount: u128], { currencyId: PalletForeignAssetsAssetId, amount: u128 }>;
-      /**
-       * Some balance was reserved (moved from free to reserved).
-       **/
-      Reserved: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      /**
-       * Some reserved balance was repatriated (moved from reserved to
-       * another account).
-       **/
-      ReserveRepatriated: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128, status: FrameSupportTokensMiscBalanceStatus], { currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128, status: FrameSupportTokensMiscBalanceStatus }>;
-      /**
-       * Some balances were slashed (e.g. due to mis-behavior)
-       **/
-      Slashed: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, freeAmount: u128, reservedAmount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, freeAmount: u128, reservedAmount: u128 }>;
-      /**
-       * The total issuance of an currency has been set
-       **/
-      TotalIssuanceSet: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, amount: u128], { currencyId: PalletForeignAssetsAssetId, amount: u128 }>;
-      /**
-       * Transfer succeeded.
-       **/
-      Transfer: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, from: AccountId32, to: AccountId32, amount: u128 }>;
-      /**
-       * Some locked balance was freed.
-       **/
-      Unlocked: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      /**
-       * Some balance was unreserved (moved from reserved to free).
-       **/
-      Unreserved: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      /**
-       * Some balances were withdrawn (e.g. pay for transaction fee)
-       **/
-      Withdrawn: AugmentedEvent<ApiType, [currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128], { currencyId: PalletForeignAssetsAssetId, who: AccountId32, amount: u128 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    transactionPayment: {
-      /**
-       * A transaction fee `actual_fee`, of which `tip` was added to the minimum inclusion fee,
-       * has been paid by `who`.
-       **/
-      TransactionFeePaid: AugmentedEvent<ApiType, [who: AccountId32, actualFee: u128, tip: u128], { who: AccountId32, actualFee: u128, tip: u128 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    treasury: {
-      /**
-       * Some funds have been allocated.
-       **/
-      Awarded: AugmentedEvent<ApiType, [proposalIndex: u32, award: u128, account: AccountId32], { proposalIndex: u32, award: u128, account: AccountId32 }>;
-      /**
-       * Some of our funds have been burnt.
-       **/
-      Burnt: AugmentedEvent<ApiType, [burntFunds: u128], { burntFunds: u128 }>;
-      /**
-       * Some funds have been deposited.
-       **/
-      Deposit: AugmentedEvent<ApiType, [value: u128], { value: u128 }>;
-      /**
-       * New proposal.
-       **/
-      Proposed: AugmentedEvent<ApiType, [proposalIndex: u32], { proposalIndex: u32 }>;
-      /**
-       * A proposal was rejected; funds were slashed.
-       **/
-      Rejected: AugmentedEvent<ApiType, [proposalIndex: u32, slashed: u128], { proposalIndex: u32, slashed: u128 }>;
-      /**
-       * Spending has finished; this is the amount that rolls over until next spend.
-       **/
-      Rollover: AugmentedEvent<ApiType, [rolloverBalance: u128], { rolloverBalance: u128 }>;
-      /**
-       * A new spend proposal has been approved.
-       **/
-      SpendApproved: AugmentedEvent<ApiType, [proposalIndex: u32, amount: u128, beneficiary: AccountId32], { proposalIndex: u32, amount: u128, beneficiary: AccountId32 }>;
-      /**
-       * We have ended a spend period and will now allocate funds.
-       **/
-      Spending: AugmentedEvent<ApiType, [budgetRemaining: u128], { budgetRemaining: u128 }>;
-      /**
-       * The inactive funds of the pallet have been updated.
-       **/
-      UpdatedInactive: AugmentedEvent<ApiType, [reactivated: u128, deactivated: u128], { reactivated: u128, deactivated: u128 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    utility: {
-      /**
-       * Batch of dispatches completed fully with no error.
-       **/
-      BatchCompleted: AugmentedEvent<ApiType, []>;
-      /**
-       * Batch of dispatches completed but has errors.
-       **/
-      BatchCompletedWithErrors: AugmentedEvent<ApiType, []>;
-      /**
-       * Batch of dispatches did not complete fully. Index of first failing dispatch given, as
-       * well as the error.
-       **/
-      BatchInterrupted: AugmentedEvent<ApiType, [index: u32, error: SpRuntimeDispatchError], { index: u32, error: SpRuntimeDispatchError }>;
-      /**
-       * A call was dispatched.
-       **/
-      DispatchedAs: AugmentedEvent<ApiType, [result: Result<Null, SpRuntimeDispatchError>], { result: Result<Null, SpRuntimeDispatchError> }>;
-      /**
-       * A single item within a Batch of dispatches has completed with no error.
-       **/
-      ItemCompleted: AugmentedEvent<ApiType, []>;
-      /**
-       * A single item within a Batch of dispatches has completed with error.
-       **/
-      ItemFailed: AugmentedEvent<ApiType, [error: SpRuntimeDispatchError], { error: SpRuntimeDispatchError }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    vesting: {
-      /**
-       * Claimed vesting.
-       **/
-      Claimed: AugmentedEvent<ApiType, [who: AccountId32, amount: u128], { who: AccountId32, amount: u128 }>;
-      /**
-       * Added new vesting schedule.
-       **/
-      VestingScheduleAdded: AugmentedEvent<ApiType, [from: AccountId32, to: AccountId32, vestingSchedule: OrmlVestingVestingSchedule], { from: AccountId32, to: AccountId32, vestingSchedule: OrmlVestingVestingSchedule }>;
-      /**
-       * Updated vesting schedules.
-       **/
-      VestingSchedulesUpdated: AugmentedEvent<ApiType, [who: AccountId32], { who: AccountId32 }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    xcmpQueue: {
-      /**
-       * Bad XCM format used.
-       **/
-      BadFormat: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
-      /**
-       * Bad XCM version used.
-       **/
-      BadVersion: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
-      /**
-       * Some XCM failed.
-       **/
-      Fail: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, error: StagingXcmV3TraitsError, weight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, error: StagingXcmV3TraitsError, weight: SpWeightsWeightV2Weight }>;
-      /**
-       * An XCM exceeded the individual message weight budget.
-       **/
-      OverweightEnqueued: AugmentedEvent<ApiType, [sender: u32, sentAt: u32, index: u64, required: SpWeightsWeightV2Weight], { sender: u32, sentAt: u32, index: u64, required: SpWeightsWeightV2Weight }>;
-      /**
-       * An XCM from the overweight queue was executed with the given actual weight used.
-       **/
-      OverweightServiced: AugmentedEvent<ApiType, [index: u64, used: SpWeightsWeightV2Weight], { index: u64, used: SpWeightsWeightV2Weight }>;
-      /**
-       * Some XCM was executed ok.
-       **/
-      Success: AugmentedEvent<ApiType, [messageHash: U8aFixed, messageId: U8aFixed, weight: SpWeightsWeightV2Weight], { messageHash: U8aFixed, messageId: U8aFixed, weight: SpWeightsWeightV2Weight }>;
-      /**
-       * An HRMP message was sent to a sibling parachain.
-       **/
-      XcmpMessageSent: AugmentedEvent<ApiType, [messageHash: U8aFixed], { messageHash: U8aFixed }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-    xTokens: {
-      /**
-       * Transferred `MultiAsset` with fee.
-       **/
-      TransferredMultiAssets: AugmentedEvent<ApiType, [sender: AccountId32, assets: StagingXcmV3MultiassetMultiAssets, fee: StagingXcmV3MultiAsset, dest: StagingXcmV3MultiLocation], { sender: AccountId32, assets: StagingXcmV3MultiassetMultiAssets, fee: StagingXcmV3MultiAsset, dest: StagingXcmV3MultiLocation }>;
-      /**
-       * Generic event
-       **/
-      [key: string]: AugmentedEvent<ApiType>;
-    };
-  } // AugmentedEvents
-} // declare module
deletedjs-packages/types/src/augment-api-query.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api-query.ts
+++ /dev/null
@@ -1,1442 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/api-base/types/storage';
-
-import type { ApiTypes, AugmentedQuery, QueryableStorageEntry } from '@polkadot/api-base/types';
-import type { Data } from '@polkadot/types';
-import type { BTreeMap, Bytes, Option, Struct, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
-import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
-import type { AccountId32, Call, H160, H256 } from '@polkadot/types/interfaces/runtime';
-import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportDispatchPerDispatchClassWeight, FrameSupportPreimagesBounded, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensReserveData, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesIdAmount, PalletBalancesReserveData, PalletCollectiveVotes, PalletConfigurationAppPromotionConfiguration, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCodeMetadata, PalletEvmContractHelpersSponsoringModeT, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletNonfungibleItemData, PalletPreimageRequestStatus, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveVoteRecord, PalletReferendaReferendumInfo, PalletSchedulerScheduled, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpRuntimeDigest, SpTrieStorageProof, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmVersionedAssetId, StagingXcmVersionedMultiLocation, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyScope, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild } from '@polkadot/types/lookup';
-import type { Observable } from '@polkadot/types/types';
-
-export type __AugmentedQuery<ApiType extends ApiTypes> = AugmentedQuery<ApiType, () => unknown>;
-export type __QueryableStorageEntry<ApiType extends ApiTypes> = QueryableStorageEntry<ApiType>;
-
-declare module '@polkadot/api-base/types/storage' {
-  interface AugmentedQueries<ApiType extends ApiTypes> {
-    appPromotion: {
-      /**
-       * Stores the `admin` account. Some extrinsics can only be executed if they were signed by `admin`.
-       **/
-      admin: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Pending unstake records for an `Account`.
-       * 
-       * * **Key** - Staker account.
-       * * **Value** - Amount of stakes.
-       **/
-      pendingUnstake: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<ITuple<[AccountId32, u128]>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Stores a key for record for which the revenue recalculation was performed.
-       * If `None`, then recalculation has not yet been performed or calculations have been completed for all stakers.
-       **/
-      previousCalculatedRecord: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[AccountId32, u32]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Stores the amount of tokens staked by account in the blocknumber.
-       * 
-       * * **Key1** - Staker account.
-       * * **Key2** - Relay block number when the stake was made.
-       * * **(Balance, BlockNumber)** - Balance of the stake.
-       * The number of the relay block in which we must perform the interest recalculation
-       **/
-      staked: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<ITuple<[u128, u32]>>, [AccountId32, u32]> & QueryableStorageEntry<ApiType, [AccountId32, u32]>;
-      /**
-       * Stores number of stake records for an `Account`.
-       * 
-       * * **Key** - Staker account.
-       * * **Value** - Amount of stakes.
-       **/
-      stakesPerAccount: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u8>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Stores the total staked amount.
-       **/
-      totalStaked: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    aura: {
-      /**
-       * The current authority set.
-       **/
-      authorities: AugmentedQuery<ApiType, () => Observable<Vec<SpConsensusAuraSr25519AppSr25519Public>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The current slot of this block.
-       * 
-       * This will be set in `on_initialize`.
-       **/
-      currentSlot: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    auraExt: {
-      /**
-       * Serves as cache for the authorities.
-       * 
-       * The authorities in AuRa are overwritten in `on_initialize` when we switch to a new session,
-       * but we require the old authorities to verify the seal when validating a PoV. This will
-       * always be updated to the latest AuRa authorities in `on_finalize`.
-       **/
-      authorities: AugmentedQuery<ApiType, () => Observable<Vec<SpConsensusAuraSr25519AppSr25519Public>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Current slot paired with a number of authored blocks.
-       * 
-       * Updated on each block initialization.
-       **/
-      slotInfo: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[u64, u32]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    authorship: {
-      /**
-       * Author of current block.
-       **/
-      author: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    balances: {
-      /**
-       * The Balances pallet example of storing the balance of an account.
-       * 
-       * # Example
-       * 
-       * ```nocompile
-       * impl pallet_balances::Config for Runtime {
-       * type AccountStore = StorageMapShim<Self::Account<Runtime>, frame_system::Provider<Runtime>, AccountId, Self::AccountData<Balance>>
-       * }
-       * ```
-       * 
-       * You can also store the balance of an account in the `System` pallet.
-       * 
-       * # Example
-       * 
-       * ```nocompile
-       * impl pallet_balances::Config for Runtime {
-       * type AccountStore = System
-       * }
-       * ```
-       * 
-       * But this comes with tradeoffs, storing account balances in the system pallet stores
-       * `frame_system` data alongside the account data contrary to storing account balances in the
-       * `Balances` pallet, which uses a `StorageMap` to store balances data only.
-       * NOTE: This is only used in the case that this pallet is used to store balances.
-       **/
-      account: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<PalletBalancesAccountData>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Freeze locks on account balances.
-       **/
-      freezes: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<PalletBalancesIdAmount>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Holds on account balances.
-       **/
-      holds: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<{
-    readonly id: OpalRuntimeRuntimeHoldReason;
-    readonly amount: u128;
-  } & Struct>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * The total units of outstanding deactivated balance in the system.
-       **/
-      inactiveIssuance: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Any liquidity locks on some account balances.
-       * NOTE: Should only be accessed when setting, changing and freeing a lock.
-       **/
-      locks: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<PalletBalancesBalanceLock>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Named reserves on some account balances.
-       **/
-      reserves: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<PalletBalancesReserveData>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * The total units issued in the system.
-       **/
-      totalIssuance: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    collatorSelection: {
-      /**
-       * The (community, limited) collation candidates.
-       **/
-      candidates: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The invulnerable, fixed collators.
-       **/
-      invulnerables: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Last block authored by collator.
-       **/
-      lastAuthoredBlock: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u32>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * The (community) collation license holders.
-       **/
-      licenseDepositOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<u128>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    common: {
-      /**
-       * Storage of the amount of collection admins.
-       **/
-      adminAmount: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Allowlisted collection users.
-       **/
-      allowlist: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Storage of collection info.
-       **/
-      collectionById: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<UpDataStructsCollection>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Storage of collection properties.
-       **/
-      collectionProperties: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsProperties>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Storage of token property permissions of a collection.
-       **/
-      collectionPropertyPermissions: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<UpDataStructsPropertiesMapPropertyPermission>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Storage of the count of created collections. Essentially contains the last collection ID.
-       **/
-      createdCollectionCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Storage of the count of deleted collections.
-       **/
-      destroyedCollectionCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Not used by code, exists only to provide some types to metadata.
-       **/
-      dummyStorageValue: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[UpDataStructsCollectionStats, u32, u32, UpDataStructsTokenChild, PhantomTypeUpDataStructs]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * List of collection admins.
-       **/
-      isAdmin: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    configuration: {
-      appPromomotionConfigurationOverride: AugmentedQuery<ApiType, () => Observable<PalletConfigurationAppPromotionConfiguration>, []> & QueryableStorageEntry<ApiType, []>;
-      collatorSelectionDesiredCollatorsOverride: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      collatorSelectionKickThresholdOverride: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      collatorSelectionLicenseBondOverride: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      minGasPriceOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      weightToFeeCoefficientOverride: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    council: {
-      /**
-       * The current members of the collective. This is stored sorted (just by value).
-       **/
-      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The prime member that helps determine the default vote behavior in case of absentations.
-       **/
-      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Proposals so far.
-       **/
-      proposalCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Actual proposal for a given hash, if it's current.
-       **/
-      proposalOf: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<Call>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * The hashes of the active proposals.
-       **/
-      proposals: AugmentedQuery<ApiType, () => Observable<Vec<H256>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Votes on a given proposal, if it is ongoing.
-       **/
-      voting: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<PalletCollectiveVotes>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    councilMembership: {
-      /**
-       * The current membership, stored as an ordered Vec.
-       **/
-      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The current prime member, if one exists.
-       **/
-      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    democracy: {
-      /**
-       * A record of who vetoed what. Maps proposal hash to a possible existent block number
-       * (until when it may not be resubmitted) and who vetoed it.
-       **/
-      blacklist: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<ITuple<[u32, Vec<AccountId32>]>>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * Record of all proposals that have been subject to emergency cancellation.
-       **/
-      cancellations: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<bool>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * Those who have locked a deposit.
-       * 
-       * TWOX-NOTE: Safe, as increasing integer keys are safe.
-       **/
-      depositOf: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<ITuple<[Vec<AccountId32>, u128]>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * True if the last referendum tabled was submitted externally. False if it was a public
-       * proposal.
-       **/
-      lastTabledWasExternal: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The lowest referendum index representing an unbaked referendum. Equal to
-       * `ReferendumCount` if there isn't a unbaked referendum.
-       **/
-      lowestUnbaked: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * General information concerning any proposal or referendum.
-       * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON
-       * dump or IPFS hash of a JSON file.
-       * 
-       * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove)
-       * large preimages.
-       **/
-      metadataOf: AugmentedQuery<ApiType, (arg: PalletDemocracyMetadataOwner | { External: any } | { Proposal: any } | { Referendum: any } | string | Uint8Array) => Observable<Option<H256>>, [PalletDemocracyMetadataOwner]> & QueryableStorageEntry<ApiType, [PalletDemocracyMetadataOwner]>;
-      /**
-       * The referendum to be tabled whenever it would be valid to table an external proposal.
-       * This happens when a referendum needs to be tabled and one of two conditions are met:
-       * - `LastTabledWasExternal` is `false`; or
-       * - `PublicProps` is empty.
-       **/
-      nextExternal: AugmentedQuery<ApiType, () => Observable<Option<ITuple<[FrameSupportPreimagesBounded, PalletDemocracyVoteThreshold]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The number of (public) proposals that have been made so far.
-       **/
-      publicPropCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The public proposals. Unsorted. The second item is the proposal.
-       **/
-      publicProps: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[u32, FrameSupportPreimagesBounded, AccountId32]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The next free referendum index, aka the number of referenda started so far.
-       **/
-      referendumCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Information concerning any given referendum.
-       * 
-       * TWOX-NOTE: SAFE as indexes are not under an attacker’s control.
-       **/
-      referendumInfoOf: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletDemocracyReferendumInfo>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * All votes for a particular voter. We store the balance for the number of votes that we
-       * have recorded. The second item is the total amount of delegations, that will be added.
-       * 
-       * TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway.
-       **/
-      votingOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<PalletDemocracyVoteVoting>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    dmpQueue: {
-      /**
-       * The configuration.
-       **/
-      configuration: AugmentedQuery<ApiType, () => Observable<CumulusPalletDmpQueueConfigData>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Counter for the related counted storage map
-       **/
-      counterForOverweight: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The overweight messages.
-       **/
-      overweight: AugmentedQuery<ApiType, (arg: u64 | AnyNumber | Uint8Array) => Observable<Option<ITuple<[u32, Bytes]>>>, [u64]> & QueryableStorageEntry<ApiType, [u64]>;
-      /**
-       * The page index.
-       **/
-      pageIndex: AugmentedQuery<ApiType, () => Observable<CumulusPalletDmpQueuePageIndexData>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The queue pages.
-       **/
-      pages: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<ITuple<[u32, Bytes]>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    ethereum: {
-      blockHash: AugmentedQuery<ApiType, (arg: U256 | AnyNumber | Uint8Array) => Observable<H256>, [U256]> & QueryableStorageEntry<ApiType, [U256]>;
-      /**
-       * The current Ethereum block.
-       **/
-      currentBlock: AugmentedQuery<ApiType, () => Observable<Option<EthereumBlock>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The current Ethereum receipts.
-       **/
-      currentReceipts: AugmentedQuery<ApiType, () => Observable<Option<Vec<EthereumReceiptReceiptV3>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The current transaction statuses.
-       **/
-      currentTransactionStatuses: AugmentedQuery<ApiType, () => Observable<Option<Vec<FpRpcTransactionStatus>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Injected transactions should have unique nonce, here we store current
-       **/
-      injectedNonce: AugmentedQuery<ApiType, () => Observable<U256>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Current building block's transactions and receipts.
-       **/
-      pending: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[EthereumTransactionTransactionV2, FpRpcTransactionStatus, EthereumReceiptReceiptV3]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    evm: {
-      accountCodes: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<Bytes>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      accountCodesMetadata: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<Option<PalletEvmCodeMetadata>>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      accountStorages: AugmentedQuery<ApiType, (arg1: H160 | string | Uint8Array, arg2: H256 | string | Uint8Array) => Observable<H256>, [H160, H256]> & QueryableStorageEntry<ApiType, [H160, H256]>;
-      /**
-       * Written on log, reset after transaction
-       * Should be empty between transactions
-       **/
-      currentLogs: AugmentedQuery<ApiType, () => Observable<Vec<EthereumLog>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    evmContractHelpers: {
-      /**
-       * Storage for users that allowed for sponsorship.
-       * 
-       * ### Usage
-       * Prefer to delete record from storage if user no more allowed for sponsorship.
-       * 
-       * * **Key1** - contract address.
-       * * **Key2** - user that allowed for sponsorship.
-       * * **Value** - allowance for sponsorship.
-       **/
-      allowlist: AugmentedQuery<ApiType, (arg1: H160 | string | Uint8Array, arg2: H160 | string | Uint8Array) => Observable<bool>, [H160, H160]> & QueryableStorageEntry<ApiType, [H160, H160]>;
-      /**
-       * Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.
-       * 
-       * ### Usage
-       * Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.
-       * 
-       * * **Key** - contract address.
-       * * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.
-       **/
-      allowlistEnabled: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<bool>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      /**
-       * Store owner for contract.
-       * 
-       * * **Key** - contract address.
-       * * **Value** - owner for contract.
-       **/
-      owner: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<H160>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      /**
-       * Deprecated: this storage is deprecated
-       **/
-      selfSponsoring: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<bool>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      sponsorBasket: AugmentedQuery<ApiType, (arg1: H160 | string | Uint8Array, arg2: H160 | string | Uint8Array) => Observable<Option<u32>>, [H160, H160]> & QueryableStorageEntry<ApiType, [H160, H160]>;
-      /**
-       * Store for contract sponsorship state.
-       * 
-       * * **Key** - contract address.
-       * * **Value** - sponsorship state.
-       **/
-      sponsoring: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<UpDataStructsSponsorshipStateBasicCrossAccountIdRepr>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      /**
-       * Storage for last sponsored block.
-       * 
-       * * **Key1** - contract address.
-       * * **Key2** - sponsored user address.
-       * * **Value** - last sponsored block number.
-       **/
-      sponsoringFeeLimit: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<BTreeMap<u32, U256>>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      /**
-       * Store for sponsoring mode.
-       * 
-       * ### Usage
-       * Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).
-       * 
-       * * **Key** - contract address.
-       * * **Value** - [`sponsoring mode`](SponsoringModeT).
-       **/
-      sponsoringMode: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<Option<PalletEvmContractHelpersSponsoringModeT>>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      /**
-       * Storage for sponsoring rate limit in blocks.
-       * 
-       * * **Key** - contract address.
-       * * **Value** - amount of sponsored blocks.
-       **/
-      sponsoringRateLimit: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<u32>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    evmMigration: {
-      migrationPending: AugmentedQuery<ApiType, (arg: H160 | string | Uint8Array) => Observable<bool>, [H160]> & QueryableStorageEntry<ApiType, [H160]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    fellowshipCollective: {
-      /**
-       * The index of each ranks's member into the group of members who have at least that rank.
-       **/
-      idToIndex: AugmentedQuery<ApiType, (arg1: u16 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u16, AccountId32]> & QueryableStorageEntry<ApiType, [u16, AccountId32]>;
-      /**
-       * The members in the collective by index. All indices in the range `0..MemberCount` will
-       * return `Some`, however a member's index is not guaranteed to remain unchanged over time.
-       **/
-      indexToId: AugmentedQuery<ApiType, (arg1: u16 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<AccountId32>>, [u16, u32]> & QueryableStorageEntry<ApiType, [u16, u32]>;
-      /**
-       * The number of members in the collective who have at least the rank according to the index
-       * of the vec.
-       **/
-      memberCount: AugmentedQuery<ApiType, (arg: u16 | AnyNumber | Uint8Array) => Observable<u32>, [u16]> & QueryableStorageEntry<ApiType, [u16]>;
-      /**
-       * The current members of the collective.
-       **/
-      members: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<PalletRankedCollectiveMemberRecord>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Votes on a given proposal, if it is ongoing.
-       **/
-      voting: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<PalletRankedCollectiveVoteRecord>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
-      votingCleanup: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<Bytes>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    fellowshipReferenda: {
-      /**
-       * The number of referenda being decided currently.
-       **/
-      decidingCount: AugmentedQuery<ApiType, (arg: u16 | AnyNumber | Uint8Array) => Observable<u32>, [u16]> & QueryableStorageEntry<ApiType, [u16]>;
-      /**
-       * The metadata is a general information concerning the referendum.
-       * The `PreimageHash` refers to the preimage of the `Preimages` provider which can be a JSON
-       * dump or IPFS hash of a JSON file.
-       * 
-       * Consider a garbage collection for a metadata of finished referendums to `unrequest` (remove)
-       * large preimages.
-       **/
-      metadataOf: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<H256>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * The next free referendum index, aka the number of referenda started so far.
-       **/
-      referendumCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Information concerning any given referendum.
-       **/
-      referendumInfoFor: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletReferendaReferendumInfo>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * The sorted list of referenda ready to be decided but not yet being decided, ordered by
-       * conviction-weighted approvals.
-       * 
-       * This should be empty if `DecidingCount` is less than `TrackInfo::max_deciding`.
-       **/
-      trackQueue: AugmentedQuery<ApiType, (arg: u16 | AnyNumber | Uint8Array) => Observable<Vec<ITuple<[u32, u32]>>>, [u16]> & QueryableStorageEntry<ApiType, [u16]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    foreignAssets: {
-      /**
-       * The storages for assets to fungible collection binding
-       * 
-       **/
-      assetBinding: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * The storages for AssetMetadatas.
-       * 
-       * AssetMetadatas: map AssetIds => Option<AssetMetadata>
-       **/
-      assetMetadatas: AugmentedQuery<ApiType, (arg: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<Option<PalletForeignAssetsModuleAssetMetadata>>, [PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [PalletForeignAssetsAssetId]>;
-      /**
-       * The storages for MultiLocations.
-       * 
-       * ForeignAssetLocations: map ForeignAssetId => Option<MultiLocation>
-       **/
-      foreignAssetLocations: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<StagingXcmV3MultiLocation>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * The storages for CurrencyIds.
-       * 
-       * LocationToCurrencyIds: map MultiLocation => Option<ForeignAssetId>
-       **/
-      locationToCurrencyIds: AugmentedQuery<ApiType, (arg: StagingXcmV3MultiLocation | { parents?: any; interior?: any } | string | Uint8Array) => Observable<Option<u32>>, [StagingXcmV3MultiLocation]> & QueryableStorageEntry<ApiType, [StagingXcmV3MultiLocation]>;
-      /**
-       * Next available Foreign AssetId ID.
-       * 
-       * NextForeignAssetId: ForeignAssetId
-       **/
-      nextForeignAssetId: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    fungible: {
-      /**
-       * Storage for assets delegated to a limited extent to other users.
-       **/
-      allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Amount of tokens owned by an account inside a collection.
-       **/
-      balance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Total amount of fungible tokens inside a collection.
-       **/
-      totalSupply: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u128>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    identity: {
-      /**
-       * Information that is pertinent to identify the entity behind an account.
-       * 
-       * TWOX-NOTE: OK ― `AccountId` is a secure hash.
-       **/
-      identityOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<PalletIdentityRegistration>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * The set of registrars. Not expected to get very big as can only be added through a
-       * special origin (likely a council motion).
-       * 
-       * The index into this can be cast to `RegistrarIndex` to get a valid value.
-       **/
-      registrars: AugmentedQuery<ApiType, () => Observable<Vec<Option<PalletIdentityRegistrarInfo>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Alternative "sub" identities of this account.
-       * 
-       * The first item is the deposit, the second is a vector of the accounts.
-       * 
-       * TWOX-NOTE: OK ― `AccountId` is a secure hash.
-       **/
-      subsOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<ITuple<[u128, Vec<AccountId32>]>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * The super-identity of an alternative "sub" identity together with its name, within that
-       * context. If the account is not some other account's sub-identity, then just `None`.
-       **/
-      superOf: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<ITuple<[AccountId32, Data]>>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    inflation: {
-      /**
-       * Current inflation for `InflationBlockInterval` number of blocks
-       **/
-      blockInflation: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Next target (relay) block when inflation will be applied
-       **/
-      nextInflationBlock: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Next target (relay) block when inflation is recalculated
-       **/
-      nextRecalculationBlock: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Relay block when inflation has started
-       **/
-      startBlock: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * starting year total issuance
-       **/
-      startingYearTotalIssuance: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    maintenance: {
-      enabled: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    nonfungible: {
-      /**
-       * Amount of tokens owned by an account in a collection.
-       **/
-      accountBalance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u32>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Allowance set by a token owner for another user to perform one of certain transactions on a token.
-       **/
-      allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletEvmAccountBasicCrossAccountIdRepr>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Operator set by a wallet owner that could perform certain transactions on all tokens in the wallet.
-       **/
-      collectionAllowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Used to enumerate tokens owned by account.
-       **/
-      owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
-      /**
-       * Custom data of a token that is serialized to bytes,
-       * primarily reserved for on-chain operations,
-       * normally obscured from the external users.
-       * 
-       * Auxiliary properties are slightly different from
-       * usual [`TokenProperties`] due to an unlimited number
-       * and separately stored and written-to key-value pairs.
-       * 
-       * Currently unused.
-       **/
-      tokenAuxProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: UpDataStructsPropertyScope | 'None' | 'Rmrk' | number | Uint8Array, arg4: Bytes | string | Uint8Array) => Observable<Option<Bytes>>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry<ApiType, [u32, u32, UpDataStructsPropertyScope, Bytes]>;
-      /**
-       * Used to enumerate token's children.
-       **/
-      tokenChildren: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable<bool>, [u32, u32, ITuple<[u32, u32]>]> & QueryableStorageEntry<ApiType, [u32, u32, ITuple<[u32, u32]>]>;
-      /**
-       * Token data, used to partially describe a token.
-       **/
-      tokenData: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletNonfungibleItemData>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Map of key-value pairs, describing the metadata of a token.
-       **/
-      tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<UpDataStructsProperties>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Amount of burnt tokens in a collection.
-       **/
-      tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Total amount of minted tokens in a collection.
-       **/
-      tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    parachainInfo: {
-      parachainId: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    parachainSystem: {
-      /**
-       * Storage field that keeps track of bandwidth used by the unincluded segment along with the
-       * latest the latest HRMP watermark. Used for limiting the acceptance of new blocks with
-       * respect to relay chain constraints.
-       **/
-      aggregatedUnincludedSegment: AugmentedQuery<ApiType, () => Observable<Option<CumulusPalletParachainSystemUnincludedSegmentSegmentTracker>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The number of HRMP messages we observed in `on_initialize` and thus used that number for
-       * announcing the weight of `on_initialize` and `on_finalize`.
-       **/
-      announcedHrmpMessagesPerCandidate: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The next authorized upgrade, if there is one.
-       **/
-      authorizedUpgrade: AugmentedQuery<ApiType, () => Observable<Option<CumulusPalletParachainSystemCodeUpgradeAuthorization>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * A custom head data that should be returned as result of `validate_block`.
-       * 
-       * See `Pallet::set_custom_validation_head_data` for more information.
-       **/
-      customValidationHeadData: AugmentedQuery<ApiType, () => Observable<Option<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Were the validation data set to notify the relay chain?
-       **/
-      didSetValidationCode: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The parachain host configuration that was obtained from the relay parent.
-       * 
-       * This field is meant to be updated each block with the validation data inherent. Therefore,
-       * before processing of the inherent, e.g. in `on_initialize` this data may be stale.
-       * 
-       * This data is also absent from the genesis.
-       **/
-      hostConfiguration: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5AbridgedHostConfiguration>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * HRMP messages that were sent in a block.
-       * 
-       * This will be cleared in `on_initialize` of each new block.
-       **/
-      hrmpOutboundMessages: AugmentedQuery<ApiType, () => Observable<Vec<PolkadotCorePrimitivesOutboundHrmpMessage>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * HRMP watermark that was set in a block.
-       * 
-       * This will be cleared in `on_initialize` of each new block.
-       **/
-      hrmpWatermark: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The last downward message queue chain head we have observed.
-       * 
-       * This value is loaded before and saved after processing inbound downward messages carried
-       * by the system inherent.
-       **/
-      lastDmqMqcHead: AugmentedQuery<ApiType, () => Observable<H256>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The message queue chain heads we have observed per each channel incoming channel.
-       * 
-       * This value is loaded before and saved after processing inbound downward messages carried
-       * by the system inherent.
-       **/
-      lastHrmpMqcHeads: AugmentedQuery<ApiType, () => Observable<BTreeMap<u32, H256>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The relay chain block number associated with the last parachain block.
-       **/
-      lastRelayChainBlockNumber: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Validation code that is set by the parachain and is to be communicated to collator and
-       * consequently the relay-chain.
-       * 
-       * This will be cleared in `on_initialize` of each new block if no other pallet already set
-       * the value.
-       **/
-      newValidationCode: AugmentedQuery<ApiType, () => Observable<Option<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Upward messages that are still pending and not yet send to the relay chain.
-       **/
-      pendingUpwardMessages: AugmentedQuery<ApiType, () => Observable<Vec<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * In case of a scheduled upgrade, this storage field contains the validation code to be
-       * applied.
-       * 
-       * As soon as the relay chain gives us the go-ahead signal, we will overwrite the
-       * [`:code`][sp_core::storage::well_known_keys::CODE] which will result the next block process
-       * with the new validation code. This concludes the upgrade process.
-       **/
-      pendingValidationCode: AugmentedQuery<ApiType, () => Observable<Bytes>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Number of downward messages processed in a block.
-       * 
-       * This will be cleared in `on_initialize` of each new block.
-       **/
-      processedDownwardMessages: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The state proof for the last relay parent block.
-       * 
-       * This field is meant to be updated each block with the validation data inherent. Therefore,
-       * before processing of the inherent, e.g. in `on_initialize` this data may be stale.
-       * 
-       * This data is also absent from the genesis.
-       **/
-      relayStateProof: AugmentedQuery<ApiType, () => Observable<Option<SpTrieStorageProof>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The snapshot of some state related to messaging relevant to the current parachain as per
-       * the relay parent.
-       * 
-       * This field is meant to be updated each block with the validation data inherent. Therefore,
-       * before processing of the inherent, e.g. in `on_initialize` this data may be stale.
-       * 
-       * This data is also absent from the genesis.
-       **/
-      relevantMessagingState: AugmentedQuery<ApiType, () => Observable<Option<CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The weight we reserve at the beginning of the block for processing DMP messages. This
-       * overrides the amount set in the Config trait.
-       **/
-      reservedDmpWeightOverride: AugmentedQuery<ApiType, () => Observable<Option<SpWeightsWeightV2Weight>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The weight we reserve at the beginning of the block for processing XCMP messages. This
-       * overrides the amount set in the Config trait.
-       **/
-      reservedXcmpWeightOverride: AugmentedQuery<ApiType, () => Observable<Option<SpWeightsWeightV2Weight>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Latest included block descendants the runtime accepted. In other words, these are
-       * ancestors of the currently executing block which have not been included in the observed
-       * relay-chain state.
-       * 
-       * The segment length is limited by the capacity returned from the [`ConsensusHook`] configured
-       * in the pallet.
-       **/
-      unincludedSegment: AugmentedQuery<ApiType, () => Observable<Vec<CumulusPalletParachainSystemUnincludedSegmentAncestor>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Optional upgrade go-ahead signal from the relay-chain.
-       * 
-       * This storage item is a mirror of the corresponding value for the current parachain from the
-       * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is
-       * set after the inherent.
-       **/
-      upgradeGoAhead: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5UpgradeGoAhead>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * An option which indicates if the relay-chain restricts signalling a validation code upgrade.
-       * In other words, if this is `Some` and [`NewValidationCode`] is `Some` then the produced
-       * candidate will be invalid.
-       * 
-       * This storage item is a mirror of the corresponding value for the current parachain from the
-       * relay-chain. This value is ephemeral which means it doesn't hit the storage. This value is
-       * set after the inherent.
-       **/
-      upgradeRestrictionSignal: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5UpgradeRestriction>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Upward messages that were sent in a block.
-       * 
-       * This will be cleared in `on_initialize` of each new block.
-       **/
-      upwardMessages: AugmentedQuery<ApiType, () => Observable<Vec<Bytes>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The [`PersistedValidationData`] set for this block.
-       * This value is expected to be set only once per block and it's never stored
-       * in the trie.
-       **/
-      validationData: AugmentedQuery<ApiType, () => Observable<Option<PolkadotPrimitivesV5PersistedValidationData>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    polkadotXcm: {
-      /**
-       * The existing asset traps.
-       * 
-       * Key is the blake2 256 hash of (origin, versioned `MultiAssets`) pair. Value is the number of
-       * times this pair has been trapped (usually just 1 if it exists at all).
-       **/
-      assetTraps: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<u32>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * The current migration's stage, if any.
-       **/
-      currentMigration: AugmentedQuery<ApiType, () => Observable<Option<PalletXcmVersionMigrationStage>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Fungible assets which we know are locked on this chain.
-       **/
-      lockedFungibles: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<Vec<ITuple<[u128, StagingXcmVersionedMultiLocation]>>>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * The ongoing queries.
-       **/
-      queries: AugmentedQuery<ApiType, (arg: u64 | AnyNumber | Uint8Array) => Observable<Option<PalletXcmQueryStatus>>, [u64]> & QueryableStorageEntry<ApiType, [u64]>;
-      /**
-       * The latest available query index.
-       **/
-      queryCounter: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Fungible assets which we know are locked on a remote chain.
-       **/
-      remoteLockedFungibles: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array, arg3: StagingXcmVersionedAssetId | { V3: any } | string | Uint8Array) => Observable<Option<PalletXcmRemoteLockedFungibleRecord>>, [u32, AccountId32, StagingXcmVersionedAssetId]> & QueryableStorageEntry<ApiType, [u32, AccountId32, StagingXcmVersionedAssetId]>;
-      /**
-       * Default version to encode XCM when latest version of destination is unknown. If `None`,
-       * then the destinations whose XCM version is unknown are considered unreachable.
-       **/
-      safeXcmVersion: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The Latest versions that we know various locations support.
-       **/
-      supportedVersion: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => Observable<Option<u32>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry<ApiType, [u32, StagingXcmVersionedMultiLocation]>;
-      /**
-       * Destinations whose latest XCM version we would like to know. Duplicates not allowed, and
-       * the `u32` counter is the number of times that a send to the destination has been attempted,
-       * which is used as a prioritization.
-       **/
-      versionDiscoveryQueue: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[StagingXcmVersionedMultiLocation, u32]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * All locations that we have requested version notifications from.
-       **/
-      versionNotifiers: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => Observable<Option<u64>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry<ApiType, [u32, StagingXcmVersionedMultiLocation]>;
-      /**
-       * The target locations that are subscribed to our version changes, as well as the most recent
-       * of our versions we informed them of.
-       **/
-      versionNotifyTargets: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => Observable<Option<ITuple<[u64, SpWeightsWeightV2Weight, u32]>>>, [u32, StagingXcmVersionedMultiLocation]> & QueryableStorageEntry<ApiType, [u32, StagingXcmVersionedMultiLocation]>;
-      /**
-       * Global suspension state of the XCM executor.
-       **/
-      xcmExecutionSuspended: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    preimage: {
-      preimageFor: AugmentedQuery<ApiType, (arg: ITuple<[H256, u32]> | [H256 | string | Uint8Array, u32 | AnyNumber | Uint8Array]) => Observable<Option<Bytes>>, [ITuple<[H256, u32]>]> & QueryableStorageEntry<ApiType, [ITuple<[H256, u32]>]>;
-      /**
-       * The request status of a given hash.
-       **/
-      statusFor: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<PalletPreimageRequestStatus>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    refungible: {
-      /**
-       * Amount of tokens (not pieces) partially owned by an account within a collection.
-       **/
-      accountBalance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u32>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Allowance set by a token owner for another user to perform one of certain transactions on a number of pieces of a token.
-       **/
-      allowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg4: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Amount of token pieces owned by account.
-       **/
-      balance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<u128>, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Spender set by a wallet owner that could perform certain transactions on all tokens in the wallet.
-       **/
-      collectionAllowance: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * Used to enumerate tokens owned by account.
-       **/
-      owned: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, arg3: u32 | AnyNumber | Uint8Array) => Observable<bool>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry<ApiType, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]>;
-      /**
-       * Amount of pieces a refungible token is split into.
-       **/
-      tokenProperties: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<UpDataStructsProperties>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Amount of tokens burnt in a collection.
-       **/
-      tokensBurnt: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Total amount of minted tokens in a collection.
-       **/
-      tokensMinted: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<u32>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Total amount of pieces for token
-       **/
-      totalSupply: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<u128>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    scheduler: {
-      /**
-       * Items to be executed, indexed by the block number that they should be executed on.
-       **/
-      agenda: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Vec<Option<PalletSchedulerScheduled>>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      incompleteSince: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Lookup from a name to the block number and index of the task.
-       * 
-       * For v3 -> v4 the previously unbounded identities are Blake2-256 hashed to form the v4
-       * identities.
-       **/
-      lookup: AugmentedQuery<ApiType, (arg: U8aFixed | string | Uint8Array) => Observable<Option<ITuple<[u32, u32]>>>, [U8aFixed]> & QueryableStorageEntry<ApiType, [U8aFixed]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    session: {
-      /**
-       * Current index of the session.
-       **/
-      currentIndex: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Indices of disabled validators.
-       * 
-       * The vec is always kept sorted so that we can find whether a given validator is
-       * disabled using binary search. It gets cleared when `on_session_ending` returns
-       * a new set of identities.
-       **/
-      disabledValidators: AugmentedQuery<ApiType, () => Observable<Vec<u32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The owner of a key. The key is the `KeyTypeId` + the encoded key.
-       **/
-      keyOwner: AugmentedQuery<ApiType, (arg: ITuple<[SpCoreCryptoKeyTypeId, Bytes]> | [SpCoreCryptoKeyTypeId | string | Uint8Array, Bytes | string | Uint8Array]) => Observable<Option<AccountId32>>, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]> & QueryableStorageEntry<ApiType, [ITuple<[SpCoreCryptoKeyTypeId, Bytes]>]>;
-      /**
-       * The next session keys for a validator.
-       **/
-      nextKeys: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Option<OpalRuntimeRuntimeCommonSessionKeys>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * True if the underlying economic identities or weighting behind the validators
-       * has changed in the queued validator set.
-       **/
-      queuedChanged: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The queued keys for the next session. When the next session begins, these keys
-       * will be used to determine the validator's session keys.
-       **/
-      queuedKeys: AugmentedQuery<ApiType, () => Observable<Vec<ITuple<[AccountId32, OpalRuntimeRuntimeCommonSessionKeys]>>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The current set of validators.
-       **/
-      validators: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    stateTrieMigration: {
-      /**
-       * The limits that are imposed on automatic migrations.
-       * 
-       * If set to None, then no automatic migration happens.
-       **/
-      autoLimits: AugmentedQuery<ApiType, () => Observable<Option<PalletStateTrieMigrationMigrationLimits>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Migration progress.
-       * 
-       * This stores the snapshot of the last migrated keys. It can be set into motion and move
-       * forward by any of the means provided by this pallet.
-       **/
-      migrationProcess: AugmentedQuery<ApiType, () => Observable<PalletStateTrieMigrationMigrationTask>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The maximum limits that the signed migration could use.
-       * 
-       * If not set, no signed submission is allowed.
-       **/
-      signedMigrationMaxLimits: AugmentedQuery<ApiType, () => Observable<Option<PalletStateTrieMigrationMigrationLimits>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    sudo: {
-      /**
-       * The `AccountId` of the sudo key.
-       **/
-      key: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    system: {
-      /**
-       * The full account information for a particular account ID.
-       **/
-      account: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<FrameSystemAccountInfo>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Total length (in bytes) for all extrinsics put together, for the current block.
-       **/
-      allExtrinsicsLen: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Map of block numbers to block hashes.
-       **/
-      blockHash: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<H256>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * The current weight for the block.
-       **/
-      blockWeight: AugmentedQuery<ApiType, () => Observable<FrameSupportDispatchPerDispatchClassWeight>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Digest of the current block, also part of the block header.
-       **/
-      digest: AugmentedQuery<ApiType, () => Observable<SpRuntimeDigest>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The number of events in the `Events<T>` list.
-       **/
-      eventCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Events deposited for the current block.
-       * 
-       * NOTE: The item is unbound and should therefore never be read on chain.
-       * It could otherwise inflate the PoV size of a block.
-       * 
-       * Events have a large in-memory size. Box the events to not go out-of-memory
-       * just in case someone still reads them from within the runtime.
-       **/
-      events: AugmentedQuery<ApiType, () => Observable<Vec<FrameSystemEventRecord>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Mapping between a topic (represented by T::Hash) and a vector of indexes
-       * of events in the `<Events<T>>` list.
-       * 
-       * All topic vectors have deterministic storage locations depending on the topic. This
-       * allows light-clients to leverage the changes trie storage tracking mechanism and
-       * in case of changes fetch the list of events of interest.
-       * 
-       * The value has the type `(BlockNumberFor<T>, EventIndex)` because if we used only just
-       * the `EventIndex` then in case if the topic has the same contents on the next block
-       * no notification will be triggered thus the event might be lost.
-       **/
-      eventTopics: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Vec<ITuple<[u32, u32]>>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * The execution phase of the block.
-       **/
-      executionPhase: AugmentedQuery<ApiType, () => Observable<Option<FrameSystemPhase>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Total extrinsics count for the current block.
-       **/
-      extrinsicCount: AugmentedQuery<ApiType, () => Observable<Option<u32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Extrinsics data for the current block (maps an extrinsic's index to its data).
-       **/
-      extrinsicData: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened.
-       **/
-      lastRuntimeUpgrade: AugmentedQuery<ApiType, () => Observable<Option<FrameSystemLastRuntimeUpgradeInfo>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The current block number being processed. Set by `execute_block`.
-       **/
-      number: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Hash of the previous block.
-       **/
-      parentHash: AugmentedQuery<ApiType, () => Observable<H256>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * True if we have upgraded so that AccountInfo contains three types of `RefCount`. False
-       * (default) if not.
-       **/
-      upgradedToTripleRefCount: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * True if we have upgraded so that `type RefCount` is `u32`. False (default) if not.
-       **/
-      upgradedToU32RefCount: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    technicalCommittee: {
-      /**
-       * The current members of the collective. This is stored sorted (just by value).
-       **/
-      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The prime member that helps determine the default vote behavior in case of absentations.
-       **/
-      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Proposals so far.
-       **/
-      proposalCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Actual proposal for a given hash, if it's current.
-       **/
-      proposalOf: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<Call>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * The hashes of the active proposals.
-       **/
-      proposals: AugmentedQuery<ApiType, () => Observable<Vec<H256>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Votes on a given proposal, if it is ongoing.
-       **/
-      voting: AugmentedQuery<ApiType, (arg: H256 | string | Uint8Array) => Observable<Option<PalletCollectiveVotes>>, [H256]> & QueryableStorageEntry<ApiType, [H256]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    technicalCommitteeMembership: {
-      /**
-       * The current membership, stored as an ordered Vec.
-       **/
-      members: AugmentedQuery<ApiType, () => Observable<Vec<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The current prime member, if one exists.
-       **/
-      prime: AugmentedQuery<ApiType, () => Observable<Option<AccountId32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    testUtils: {
-      enabled: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      testValue: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    timestamp: {
-      /**
-       * Did the timestamp get updated in this block?
-       **/
-      didUpdate: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Current time for the current block.
-       **/
-      now: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    tokens: {
-      /**
-       * The balance of a token type under an account.
-       * 
-       * NOTE: If the total is ever zero, decrease account ref account.
-       * 
-       * NOTE: This is only used in the case that this module is used to store
-       * balances.
-       **/
-      accounts: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<OrmlTokensAccountData>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [AccountId32, PalletForeignAssetsAssetId]>;
-      /**
-       * Any liquidity locks of a token type under an account.
-       * NOTE: Should only be accessed when setting, changing and freeing a lock.
-       **/
-      locks: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<Vec<OrmlTokensBalanceLock>>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [AccountId32, PalletForeignAssetsAssetId]>;
-      /**
-       * Named reserves on some account balances.
-       **/
-      reserves: AugmentedQuery<ApiType, (arg1: AccountId32 | string | Uint8Array, arg2: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<Vec<OrmlTokensReserveData>>, [AccountId32, PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [AccountId32, PalletForeignAssetsAssetId]>;
-      /**
-       * The total issuance of a token type.
-       **/
-      totalIssuance: AugmentedQuery<ApiType, (arg: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array) => Observable<u128>, [PalletForeignAssetsAssetId]> & QueryableStorageEntry<ApiType, [PalletForeignAssetsAssetId]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    transactionPayment: {
-      nextFeeMultiplier: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      storageVersion: AugmentedQuery<ApiType, () => Observable<PalletTransactionPaymentReleases>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    treasury: {
-      /**
-       * Proposal indices that have been approved but not yet awarded.
-       **/
-      approvals: AugmentedQuery<ApiType, () => Observable<Vec<u32>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The amount which has been reported as inactive to Currency.
-       **/
-      deactivated: AugmentedQuery<ApiType, () => Observable<u128>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Number of proposals that have been made.
-       **/
-      proposalCount: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Proposals that have been made.
-       **/
-      proposals: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Option<PalletTreasuryProposal>>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    unique: {
-      /**
-       * Used for migrations
-       **/
-      chainVersion: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * (Collection id (controlled?2), who created (real))
-       * TODO: Off chain worker should remove from this map when collection gets removed
-       **/
-      createItemBasket: AugmentedQuery<ApiType, (arg: ITuple<[u32, AccountId32]> | [u32 | AnyNumber | Uint8Array, AccountId32 | string | Uint8Array]) => Observable<Option<u32>>, [ITuple<[u32, AccountId32]>]> & QueryableStorageEntry<ApiType, [ITuple<[u32, AccountId32]>]>;
-      /**
-       * Last sponsoring of fungible tokens approval in a collection
-       **/
-      fungibleApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
-      /**
-       * Collection id (controlled?2), owning user (real)
-       **/
-      fungibleTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, AccountId32]>;
-      /**
-       * Last sponsoring of NFT approval in a collection
-       **/
-      nftApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Collection id (controlled?2), token id (controlled?2)
-       **/
-      nftTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Last sponsoring of RFT approval in a collection
-       **/
-      refungibleApproveBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, u32, AccountId32]>;
-      /**
-       * Collection id (controlled?2), token id (controlled?2)
-       **/
-      reFungibleTransferBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array, arg3: AccountId32 | string | Uint8Array) => Observable<Option<u32>>, [u32, u32, AccountId32]> & QueryableStorageEntry<ApiType, [u32, u32, AccountId32]>;
-      /**
-       * Last sponsoring of token property setting // todo:doc rephrase this and the following
-       **/
-      tokenPropertyBasket: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Option<u32>>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    vesting: {
-      /**
-       * Vesting schedules of an account.
-       * 
-       * VestingSchedules: map AccountId => Vec<VestingSchedule>
-       **/
-      vestingSchedules: AugmentedQuery<ApiType, (arg: AccountId32 | string | Uint8Array) => Observable<Vec<OrmlVestingVestingSchedule>>, [AccountId32]> & QueryableStorageEntry<ApiType, [AccountId32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-    xcmpQueue: {
-      /**
-       * Counter for the related counted storage map
-       **/
-      counterForOverweight: AugmentedQuery<ApiType, () => Observable<u32>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Inbound aggregate XCMP messages. It can only be one per ParaId/block.
-       **/
-      inboundXcmpMessages: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u32 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32, u32]> & QueryableStorageEntry<ApiType, [u32, u32]>;
-      /**
-       * Status of the inbound XCMP channels.
-       **/
-      inboundXcmpStatus: AugmentedQuery<ApiType, () => Observable<Vec<CumulusPalletXcmpQueueInboundChannelDetails>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The messages outbound in a given XCMP channel.
-       **/
-      outboundXcmpMessages: AugmentedQuery<ApiType, (arg1: u32 | AnyNumber | Uint8Array, arg2: u16 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32, u16]> & QueryableStorageEntry<ApiType, [u32, u16]>;
-      /**
-       * The non-empty XCMP channels in order of becoming non-empty, and the index of the first
-       * and last outbound message. If the two indices are equal, then it indicates an empty
-       * queue and there must be a non-`Ok` `OutboundStatus`. We assume queues grow no greater
-       * than 65535 items. Queue indices for normal messages begin at one; zero is reserved in
-       * case of the need to send a high-priority signal message this block.
-       * The bool is true if there is a signal message waiting to be sent.
-       **/
-      outboundXcmpStatus: AugmentedQuery<ApiType, () => Observable<Vec<CumulusPalletXcmpQueueOutboundChannelDetails>>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The messages that exceeded max individual message weight budget.
-       * 
-       * These message stay in this storage map until they are manually dispatched via
-       * `service_overweight`.
-       **/
-      overweight: AugmentedQuery<ApiType, (arg: u64 | AnyNumber | Uint8Array) => Observable<Option<ITuple<[u32, u32, Bytes]>>>, [u64]> & QueryableStorageEntry<ApiType, [u64]>;
-      /**
-       * The number of overweight messages ever recorded in `Overweight`. Also doubles as the next
-       * available free overweight index.
-       **/
-      overweightCount: AugmentedQuery<ApiType, () => Observable<u64>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * The configuration which controls the dynamics of the outbound queue.
-       **/
-      queueConfig: AugmentedQuery<ApiType, () => Observable<CumulusPalletXcmpQueueQueueConfigData>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Whether or not the XCMP queue is suspended from executing incoming XCMs or not.
-       **/
-      queueSuspended: AugmentedQuery<ApiType, () => Observable<bool>, []> & QueryableStorageEntry<ApiType, []>;
-      /**
-       * Any signal messages waiting to be sent.
-       **/
-      signalMessages: AugmentedQuery<ApiType, (arg: u32 | AnyNumber | Uint8Array) => Observable<Bytes>, [u32]> & QueryableStorageEntry<ApiType, [u32]>;
-      /**
-       * Generic query
-       **/
-      [key: string]: QueryableStorageEntry<ApiType>;
-    };
-  } // AugmentedQueries
-} // declare module
deletedjs-packages/types/src/augment-api-rpc.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api-rpc.ts
+++ /dev/null
@@ -1,618 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/rpc-core/types/jsonrpc';
-
-import type { PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCollectionLimits, UpDataStructsCollectionStats, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsRpcCollection, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo } from './default/index.js';
-import type { AugmentedRpc } from '@polkadot/rpc-core/types';
-import type { Metadata, StorageKey } from '@polkadot/types';
-import type { Bytes, HashMap, Json, Null, Option, Text, U256, U64, Vec, bool, f64, u32, u64 } from '@polkadot/types-codec';
-import type { AnyNumber, Codec } from '@polkadot/types-codec/types';
-import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author';
-import type { EpochAuthorship } from '@polkadot/types/interfaces/babe';
-import type { BeefySignedCommitment } from '@polkadot/types/interfaces/beefy';
-import type { BlockHash } from '@polkadot/types/interfaces/chain';
-import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate';
-import type { AuthorityId } from '@polkadot/types/interfaces/consensus';
-import type { CodeUploadRequest, CodeUploadResult, ContractCallRequest, ContractExecResult, ContractInstantiateResult, InstantiateRequestV1 } from '@polkadot/types/interfaces/contracts';
-import type { BlockStats } from '@polkadot/types/interfaces/dev';
-import type { CreatedBlock } from '@polkadot/types/interfaces/engine';
-import type { EthAccount, EthCallRequest, EthFeeHistory, EthFilter, EthFilterChanges, EthLog, EthReceipt, EthRichBlock, EthSubKind, EthSubParams, EthSyncStatus, EthTransaction, EthTransactionRequest, EthWork } from '@polkadot/types/interfaces/eth';
-import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
-import type { EncodedFinalityProofs, JustificationNotification, ReportedRoundStates } from '@polkadot/types/interfaces/grandpa';
-import type { MmrHash, MmrLeafBatchProof } from '@polkadot/types/interfaces/mmr';
-import type { StorageKind } from '@polkadot/types/interfaces/offchain';
-import type { FeeDetails, RuntimeDispatchInfoV1 } from '@polkadot/types/interfaces/payment';
-import type { RpcMethods } from '@polkadot/types/interfaces/rpc';
-import type { AccountId, BlockNumber, H160, H256, H64, Hash, Header, Index, Justification, KeyValue, SignedBlock, StorageData } from '@polkadot/types/interfaces/runtime';
-import type { MigrationStatusResult, ReadProof, RuntimeVersion, TraceBlockResponse } from '@polkadot/types/interfaces/state';
-import type { ApplyExtrinsicResult, ChainProperties, ChainType, Health, NetworkState, NodeRole, PeerInfo, SyncState } from '@polkadot/types/interfaces/system';
-import type { IExtrinsic, Observable } from '@polkadot/types/types';
-
-export type __AugmentedRpc = AugmentedRpc<() => unknown>;
-
-declare module '@polkadot/rpc-core/types/jsonrpc' {
-  interface RpcInterface {
-    author: {
-      /**
-       * Returns true if the keystore has private keys for the given public key and key type.
-       **/
-      hasKey: AugmentedRpc<(publicKey: Bytes | string | Uint8Array, keyType: Text | string) => Observable<bool>>;
-      /**
-       * Returns true if the keystore has private keys for the given session public keys.
-       **/
-      hasSessionKeys: AugmentedRpc<(sessionKeys: Bytes | string | Uint8Array) => Observable<bool>>;
-      /**
-       * Insert a key into the keystore.
-       **/
-      insertKey: AugmentedRpc<(keyType: Text | string, suri: Text | string, publicKey: Bytes | string | Uint8Array) => Observable<Bytes>>;
-      /**
-       * Returns all pending extrinsics, potentially grouped by sender
-       **/
-      pendingExtrinsics: AugmentedRpc<() => Observable<Vec<Extrinsic>>>;
-      /**
-       * Remove given extrinsic from the pool and temporarily ban it to prevent reimporting
-       **/
-      removeExtrinsic: AugmentedRpc<(bytesOrHash: Vec<ExtrinsicOrHash> | (ExtrinsicOrHash | { Hash: any } | { Extrinsic: any } | string | Uint8Array)[]) => Observable<Vec<Hash>>>;
-      /**
-       * Generate new session keys and returns the corresponding public keys
-       **/
-      rotateKeys: AugmentedRpc<() => Observable<Bytes>>;
-      /**
-       * Submit and subscribe to watch an extrinsic until unsubscribed
-       **/
-      submitAndWatchExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable<ExtrinsicStatus>>;
-      /**
-       * Submit a fully formatted extrinsic for block inclusion
-       **/
-      submitExtrinsic: AugmentedRpc<(extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable<Hash>>;
-    };
-    babe: {
-      /**
-       * Returns data about which slots (primary or secondary) can be claimed in the current epoch with the keys in the keystore
-       **/
-      epochAuthorship: AugmentedRpc<() => Observable<HashMap<AuthorityId, EpochAuthorship>>>;
-    };
-    beefy: {
-      /**
-       * Returns hash of the latest BEEFY finalized block as seen by this client.
-       **/
-      getFinalizedHead: AugmentedRpc<() => Observable<H256>>;
-      /**
-       * Returns the block most recently finalized by BEEFY, alongside side its justification.
-       **/
-      subscribeJustifications: AugmentedRpc<() => Observable<BeefySignedCommitment>>;
-    };
-    chain: {
-      /**
-       * Get header and body of a relay chain block
-       **/
-      getBlock: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable<SignedBlock>>;
-      /**
-       * Get the block hash for a specific block
-       **/
-      getBlockHash: AugmentedRpc<(blockNumber?: BlockNumber | AnyNumber | Uint8Array) => Observable<BlockHash>>;
-      /**
-       * Get hash of the last finalized block in the canon chain
-       **/
-      getFinalizedHead: AugmentedRpc<() => Observable<BlockHash>>;
-      /**
-       * Retrieves the header for a specific block
-       **/
-      getHeader: AugmentedRpc<(hash?: BlockHash | string | Uint8Array) => Observable<Header>>;
-      /**
-       * Retrieves the newest header via subscription
-       **/
-      subscribeAllHeads: AugmentedRpc<() => Observable<Header>>;
-      /**
-       * Retrieves the best finalized header via subscription
-       **/
-      subscribeFinalizedHeads: AugmentedRpc<() => Observable<Header>>;
-      /**
-       * Retrieves the best header via subscription
-       **/
-      subscribeNewHeads: AugmentedRpc<() => Observable<Header>>;
-    };
-    childstate: {
-      /**
-       * Returns the keys with prefix from a child storage, leave empty to get all the keys
-       **/
-      getKeys: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
-      /**
-       * Returns the keys with prefix from a child storage with pagination support
-       **/
-      getKeysPaged: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, prefix: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
-      /**
-       * Returns a child storage entry at a specific block state
-       **/
-      getStorage: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Option<StorageData>>>;
-      /**
-       * Returns child storage entries for multiple keys at a specific block state
-       **/
-      getStorageEntries: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: Hash | string | Uint8Array) => Observable<Vec<Option<StorageData>>>>;
-      /**
-       * Returns the hash of a child storage entry at a block state
-       **/
-      getStorageHash: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Option<Hash>>>;
-      /**
-       * Returns the size of a child storage entry at a block state
-       **/
-      getStorageSize: AugmentedRpc<(childKey: PrefixedStorageKey | string | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: Hash | string | Uint8Array) => Observable<Option<u64>>>;
-    };
-    contracts: {
-      /**
-       * @deprecated Use the runtime interface `api.call.contractsApi.call` instead
-       * Executes a call to a contract
-       **/
-      call: AugmentedRpc<(callRequest: ContractCallRequest | { origin?: any; dest?: any; value?: any; gasLimit?: any; storageDepositLimit?: any; inputData?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ContractExecResult>>;
-      /**
-       * @deprecated Use the runtime interface `api.call.contractsApi.getStorage` instead
-       * Returns the value under a specified storage key in a contract
-       **/
-      getStorage: AugmentedRpc<(address: AccountId | string | Uint8Array, key: H256 | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<Option<Bytes>>>;
-      /**
-       * @deprecated Use the runtime interface `api.call.contractsApi.instantiate` instead
-       * Instantiate a new contract
-       **/
-      instantiate: AugmentedRpc<(request: InstantiateRequestV1 | { origin?: any; value?: any; gasLimit?: any; code?: any; data?: any; salt?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ContractInstantiateResult>>;
-      /**
-       * @deprecated Not available in newer versions of the contracts interfaces
-       * Returns the projected time a given contract will be able to sustain paying its rent
-       **/
-      rentProjection: AugmentedRpc<(address: AccountId | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<Option<BlockNumber>>>;
-      /**
-       * @deprecated Use the runtime interface `api.call.contractsApi.uploadCode` instead
-       * Upload new code without instantiating a contract from it
-       **/
-      uploadCode: AugmentedRpc<(uploadRequest: CodeUploadRequest | { origin?: any; code?: any; storageDepositLimit?: any } | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<CodeUploadResult>>;
-    };
-    dev: {
-      /**
-       * Reexecute the specified `block_hash` and gather statistics while doing so
-       **/
-      getBlockStats: AugmentedRpc<(at: Hash | string | Uint8Array) => Observable<Option<BlockStats>>>;
-    };
-    engine: {
-      /**
-       * Instructs the manual-seal authorship task to create a new block
-       **/
-      createBlock: AugmentedRpc<(createEmpty: bool | boolean | Uint8Array, finalize: bool | boolean | Uint8Array, parentHash?: BlockHash | string | Uint8Array) => Observable<CreatedBlock>>;
-      /**
-       * Instructs the manual-seal authorship task to finalize a block
-       **/
-      finalizeBlock: AugmentedRpc<(hash: BlockHash | string | Uint8Array, justification?: Justification) => Observable<bool>>;
-    };
-    eth: {
-      /**
-       * Returns accounts list.
-       **/
-      accounts: AugmentedRpc<() => Observable<Vec<H160>>>;
-      /**
-       * Returns the blockNumber
-       **/
-      blockNumber: AugmentedRpc<() => Observable<U256>>;
-      /**
-       * Call contract, returning the output data.
-       **/
-      call: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<Bytes>>;
-      /**
-       * Returns the chain ID used for transaction signing at the current best block. None is returned if not available.
-       **/
-      chainId: AugmentedRpc<() => Observable<U64>>;
-      /**
-       * Returns block author.
-       **/
-      coinbase: AugmentedRpc<() => Observable<H160>>;
-      /**
-       * Estimate gas needed for execution of given contract.
-       **/
-      estimateGas: AugmentedRpc<(request: EthCallRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns fee history for given block count & reward percentiles
-       **/
-      feeHistory: AugmentedRpc<(blockCount: U256 | AnyNumber | Uint8Array, newestBlock: BlockNumber | AnyNumber | Uint8Array, rewardPercentiles: Option<Vec<f64>> | null | Uint8Array | Vec<f64> | (f64)[]) => Observable<EthFeeHistory>>;
-      /**
-       * Returns current gas price.
-       **/
-      gasPrice: AugmentedRpc<() => Observable<U256>>;
-      /**
-       * Returns balance of the given account.
-       **/
-      getBalance: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns block with given hash.
-       **/
-      getBlockByHash: AugmentedRpc<(hash: H256 | string | Uint8Array, full: bool | boolean | Uint8Array) => Observable<Option<EthRichBlock>>>;
-      /**
-       * Returns block with given number.
-       **/
-      getBlockByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array, full: bool | boolean | Uint8Array) => Observable<Option<EthRichBlock>>>;
-      /**
-       * Returns the number of transactions in a block with given hash.
-       **/
-      getBlockTransactionCountByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns the number of transactions in a block with given block number.
-       **/
-      getBlockTransactionCountByNumber: AugmentedRpc<(block: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns the code at given address at given time (block number).
-       **/
-      getCode: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<Bytes>>;
-      /**
-       * Returns filter changes since last poll.
-       **/
-      getFilterChanges: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable<EthFilterChanges>>;
-      /**
-       * Returns all logs matching given filter (in a range 'from' - 'to').
-       **/
-      getFilterLogs: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable<Vec<EthLog>>>;
-      /**
-       * Returns logs matching given filter object.
-       **/
-      getLogs: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable<Vec<EthLog>>>;
-      /**
-       * Returns proof for account and storage.
-       **/
-      getProof: AugmentedRpc<(address: H160 | string | Uint8Array, storageKeys: Vec<H256> | (H256 | string | Uint8Array)[], number: BlockNumber | AnyNumber | Uint8Array) => Observable<EthAccount>>;
-      /**
-       * Returns content of the storage at given address.
-       **/
-      getStorageAt: AugmentedRpc<(address: H160 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<H256>>;
-      /**
-       * Returns transaction at given block hash and index.
-       **/
-      getTransactionByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthTransaction>>;
-      /**
-       * Returns transaction by given block number and index.
-       **/
-      getTransactionByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthTransaction>>;
-      /**
-       * Get transaction by its hash.
-       **/
-      getTransactionByHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<EthTransaction>>;
-      /**
-       * Returns the number of transactions sent from given address at given time (block number).
-       **/
-      getTransactionCount: AugmentedRpc<(address: H160 | string | Uint8Array, number?: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns transaction receipt by transaction hash.
-       **/
-      getTransactionReceipt: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<EthReceipt>>;
-      /**
-       * Returns an uncles at given block and index.
-       **/
-      getUncleByBlockHashAndIndex: AugmentedRpc<(hash: H256 | string | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthRichBlock>>;
-      /**
-       * Returns an uncles at given block and index.
-       **/
-      getUncleByBlockNumberAndIndex: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array, index: U256 | AnyNumber | Uint8Array) => Observable<EthRichBlock>>;
-      /**
-       * Returns the number of uncles in a block with given hash.
-       **/
-      getUncleCountByBlockHash: AugmentedRpc<(hash: H256 | string | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns the number of uncles in a block with given block number.
-       **/
-      getUncleCountByBlockNumber: AugmentedRpc<(number: BlockNumber | AnyNumber | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns the hash of the current block, the seedHash, and the boundary condition to be met.
-       **/
-      getWork: AugmentedRpc<() => Observable<EthWork>>;
-      /**
-       * Returns the number of hashes per second that the node is mining with.
-       **/
-      hashrate: AugmentedRpc<() => Observable<U256>>;
-      /**
-       * Returns max priority fee per gas
-       **/
-      maxPriorityFeePerGas: AugmentedRpc<() => Observable<U256>>;
-      /**
-       * Returns true if client is actively mining new blocks.
-       **/
-      mining: AugmentedRpc<() => Observable<bool>>;
-      /**
-       * Returns id of new block filter.
-       **/
-      newBlockFilter: AugmentedRpc<() => Observable<U256>>;
-      /**
-       * Returns id of new filter.
-       **/
-      newFilter: AugmentedRpc<(filter: EthFilter | { fromBlock?: any; toBlock?: any; blockHash?: any; address?: any; topics?: any } | string | Uint8Array) => Observable<U256>>;
-      /**
-       * Returns id of new block filter.
-       **/
-      newPendingTransactionFilter: AugmentedRpc<() => Observable<U256>>;
-      /**
-       * Returns protocol version encoded as a string (quotes are necessary).
-       **/
-      protocolVersion: AugmentedRpc<() => Observable<u64>>;
-      /**
-       * Sends signed transaction, returning its hash.
-       **/
-      sendRawTransaction: AugmentedRpc<(bytes: Bytes | string | Uint8Array) => Observable<H256>>;
-      /**
-       * Sends transaction; will block waiting for signer to return the transaction hash
-       **/
-      sendTransaction: AugmentedRpc<(tx: EthTransactionRequest | { from?: any; to?: any; gasPrice?: any; gas?: any; value?: any; data?: any; nonce?: any } | string | Uint8Array) => Observable<H256>>;
-      /**
-       * Used for submitting mining hashrate.
-       **/
-      submitHashrate: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array, hash: H256 | string | Uint8Array) => Observable<bool>>;
-      /**
-       * Used for submitting a proof-of-work solution.
-       **/
-      submitWork: AugmentedRpc<(nonce: H64 | string | Uint8Array, headerHash: H256 | string | Uint8Array, mixDigest: H256 | string | Uint8Array) => Observable<bool>>;
-      /**
-       * Subscribe to Eth subscription.
-       **/
-      subscribe: AugmentedRpc<(kind: EthSubKind | 'newHeads' | 'logs' | 'newPendingTransactions' | 'syncing' | number | Uint8Array, params?: EthSubParams | { None: any } | { Logs: any } | string | Uint8Array) => Observable<Null>>;
-      /**
-       * Returns an object with data about the sync status or false.
-       **/
-      syncing: AugmentedRpc<() => Observable<EthSyncStatus>>;
-      /**
-       * Uninstalls filter.
-       **/
-      uninstallFilter: AugmentedRpc<(index: U256 | AnyNumber | Uint8Array) => Observable<bool>>;
-    };
-    grandpa: {
-      /**
-       * Prove finality for the given block number, returning the Justification for the last block in the set.
-       **/
-      proveFinality: AugmentedRpc<(blockNumber: BlockNumber | AnyNumber | Uint8Array) => Observable<Option<EncodedFinalityProofs>>>;
-      /**
-       * Returns the state of the current best round state as well as the ongoing background rounds
-       **/
-      roundState: AugmentedRpc<() => Observable<ReportedRoundStates>>;
-      /**
-       * Subscribes to grandpa justifications
-       **/
-      subscribeJustifications: AugmentedRpc<() => Observable<JustificationNotification>>;
-    };
-    mmr: {
-      /**
-       * Generate MMR proof for the given block numbers.
-       **/
-      generateProof: AugmentedRpc<(blockNumbers: Vec<u64> | (u64 | AnyNumber | Uint8Array)[], bestKnownBlockNumber?: u64 | AnyNumber | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<MmrLeafBatchProof>>;
-      /**
-       * Get the MMR root hash for the current best block.
-       **/
-      root: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<MmrHash>>;
-      /**
-       * Verify an MMR proof
-       **/
-      verifyProof: AugmentedRpc<(proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable<bool>>;
-      /**
-       * Verify an MMR proof statelessly given an mmr_root
-       **/
-      verifyProofStateless: AugmentedRpc<(root: MmrHash | string | Uint8Array, proof: MmrLeafBatchProof | { blockHash?: any; leaves?: any; proof?: any } | string | Uint8Array) => Observable<bool>>;
-    };
-    net: {
-      /**
-       * Returns true if client is actively listening for network connections. Otherwise false.
-       **/
-      listening: AugmentedRpc<() => Observable<bool>>;
-      /**
-       * Returns number of peers connected to node.
-       **/
-      peerCount: AugmentedRpc<() => Observable<Text>>;
-      /**
-       * Returns protocol version.
-       **/
-      version: AugmentedRpc<() => Observable<Text>>;
-    };
-    offchain: {
-      /**
-       * Get offchain local storage under given key and prefix
-       **/
-      localStorageGet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array) => Observable<Option<Bytes>>>;
-      /**
-       * Set offchain local storage under given key and prefix
-       **/
-      localStorageSet: AugmentedRpc<(kind: StorageKind | 'PERSISTENT' | 'LOCAL' | number | Uint8Array, key: Bytes | string | Uint8Array, value: Bytes | string | Uint8Array) => Observable<Null>>;
-    };
-    payment: {
-      /**
-       * @deprecated Use `api.call.transactionPaymentApi.queryFeeDetails` instead
-       * Query the detailed fee of a given encoded extrinsic
-       **/
-      queryFeeDetails: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<FeeDetails>>;
-      /**
-       * @deprecated Use `api.call.transactionPaymentApi.queryInfo` instead
-       * Retrieves the fee information for an encoded extrinsic
-       **/
-      queryInfo: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<RuntimeDispatchInfoV1>>;
-    };
-    rpc: {
-      /**
-       * Retrieves the list of RPC methods that are exposed by the node
-       **/
-      methods: AugmentedRpc<() => Observable<RpcMethods>>;
-    };
-    state: {
-      /**
-       * Perform a call to a builtin on the chain
-       **/
-      call: AugmentedRpc<(method: Text | string, data: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<Bytes>>;
-      /**
-       * Retrieves the keys with prefix of a specific child storage
-       **/
-      getChildKeys: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
-      /**
-       * Returns proof of storage for child key entries at a specific block state.
-       **/
-      getChildReadProof: AugmentedRpc<(childStorageKey: PrefixedStorageKey | string | Uint8Array, keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable<ReadProof>>;
-      /**
-       * Retrieves the child storage for a key
-       **/
-      getChildStorage: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<StorageData>>;
-      /**
-       * Retrieves the child storage hash
-       **/
-      getChildStorageHash: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Hash>>;
-      /**
-       * Retrieves the child storage size
-       **/
-      getChildStorageSize: AugmentedRpc<(childStorageKey: StorageKey | string | Uint8Array | any, childDefinition: StorageKey | string | Uint8Array | any, childType: u32 | AnyNumber | Uint8Array, key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<u64>>;
-      /**
-       * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys
-       * Retrieves the keys with a certain prefix
-       **/
-      getKeys: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
-      /**
-       * Returns the keys with prefix with pagination support.
-       **/
-      getKeysPaged: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, count: u32 | AnyNumber | Uint8Array, startKey?: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<StorageKey>>>;
-      /**
-       * Returns the runtime metadata
-       **/
-      getMetadata: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<Metadata>>;
-      /**
-       * @deprecated Use `api.rpc.state.getKeysPaged` to retrieve keys
-       * Returns the keys with prefix, leave empty to get all the keys (deprecated: Use getKeysPaged)
-       **/
-      getPairs: AugmentedRpc<(prefix: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Vec<KeyValue>>>;
-      /**
-       * Returns proof of storage entries at a specific block state
-       **/
-      getReadProof: AugmentedRpc<(keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: BlockHash | string | Uint8Array) => Observable<ReadProof>>;
-      /**
-       * Get the runtime version
-       **/
-      getRuntimeVersion: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<RuntimeVersion>>;
-      /**
-       * Retrieves the storage for a key
-       **/
-      getStorage: AugmentedRpc<<T = Codec>(key: StorageKey | string | Uint8Array | any, block?: Hash | Uint8Array | string) => Observable<T>>;
-      /**
-       * Retrieves the storage hash
-       **/
-      getStorageHash: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<Hash>>;
-      /**
-       * Retrieves the storage size
-       **/
-      getStorageSize: AugmentedRpc<(key: StorageKey | string | Uint8Array | any, at?: BlockHash | string | Uint8Array) => Observable<u64>>;
-      /**
-       * Query historical storage entries (by key) starting from a start block
-       **/
-      queryStorage: AugmentedRpc<<T = Codec[]>(keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], fromBlock?: Hash | Uint8Array | string, toBlock?: Hash | Uint8Array | string) => Observable<[Hash, T][]>>;
-      /**
-       * Query storage entries (by key) starting at block hash given as the second parameter
-       **/
-      queryStorageAt: AugmentedRpc<<T = Codec[]>(keys: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[], at?: Hash | Uint8Array | string) => Observable<T>>;
-      /**
-       * Retrieves the runtime version via subscription
-       **/
-      subscribeRuntimeVersion: AugmentedRpc<() => Observable<RuntimeVersion>>;
-      /**
-       * Subscribes to storage changes for the provided keys
-       **/
-      subscribeStorage: AugmentedRpc<<T = Codec[]>(keys?: Vec<StorageKey> | (StorageKey | string | Uint8Array | any)[]) => Observable<T>>;
-      /**
-       * Provides a way to trace the re-execution of a single block
-       **/
-      traceBlock: AugmentedRpc<(block: Hash | string | Uint8Array, targets: Option<Text> | null | Uint8Array | Text | string, storageKeys: Option<Text> | null | Uint8Array | Text | string, methods: Option<Text> | null | Uint8Array | Text | string) => Observable<TraceBlockResponse>>;
-      /**
-       * Check current migration state
-       **/
-      trieMigrationStatus: AugmentedRpc<(at?: BlockHash | string | Uint8Array) => Observable<MigrationStatusResult>>;
-    };
-    syncstate: {
-      /**
-       * Returns the json-serialized chainspec running the node, with a sync state.
-       **/
-      genSyncSpec: AugmentedRpc<(raw: bool | boolean | Uint8Array) => Observable<Json>>;
-    };
-    system: {
-      /**
-       * Retrieves the next accountIndex as available on the node
-       **/
-      accountNextIndex: AugmentedRpc<(accountId: AccountId | string | Uint8Array) => Observable<Index>>;
-      /**
-       * Adds the supplied directives to the current log filter
-       **/
-      addLogFilter: AugmentedRpc<(directives: Text | string) => Observable<Null>>;
-      /**
-       * Adds a reserved peer
-       **/
-      addReservedPeer: AugmentedRpc<(peer: Text | string) => Observable<Text>>;
-      /**
-       * Retrieves the chain
-       **/
-      chain: AugmentedRpc<() => Observable<Text>>;
-      /**
-       * Retrieves the chain type
-       **/
-      chainType: AugmentedRpc<() => Observable<ChainType>>;
-      /**
-       * Dry run an extrinsic at a given block
-       **/
-      dryRun: AugmentedRpc<(extrinsic: Bytes | string | Uint8Array, at?: BlockHash | string | Uint8Array) => Observable<ApplyExtrinsicResult>>;
-      /**
-       * Return health status of the node
-       **/
-      health: AugmentedRpc<() => Observable<Health>>;
-      /**
-       * The addresses include a trailing /p2p/ with the local PeerId, and are thus suitable to be passed to addReservedPeer or as a bootnode address for example
-       **/
-      localListenAddresses: AugmentedRpc<() => Observable<Vec<Text>>>;
-      /**
-       * Returns the base58-encoded PeerId of the node
-       **/
-      localPeerId: AugmentedRpc<() => Observable<Text>>;
-      /**
-       * Retrieves the node name
-       **/
-      name: AugmentedRpc<() => Observable<Text>>;
-      /**
-       * Returns current state of the network
-       **/
-      networkState: AugmentedRpc<() => Observable<NetworkState>>;
-      /**
-       * Returns the roles the node is running as
-       **/
-      nodeRoles: AugmentedRpc<() => Observable<Vec<NodeRole>>>;
-      /**
-       * Returns the currently connected peers
-       **/
-      peers: AugmentedRpc<() => Observable<Vec<PeerInfo>>>;
-      /**
-       * Get a custom set of properties as a JSON object, defined in the chain spec
-       **/
-      properties: AugmentedRpc<() => Observable<ChainProperties>>;
-      /**
-       * Remove a reserved peer
-       **/
-      removeReservedPeer: AugmentedRpc<(peerId: Text | string) => Observable<Text>>;
-      /**
-       * Returns the list of reserved peers
-       **/
-      reservedPeers: AugmentedRpc<() => Observable<Vec<Text>>>;
-      /**
-       * Resets the log filter to Substrate defaults
-       **/
-      resetLogFilter: AugmentedRpc<() => Observable<Null>>;
-      /**
-       * Returns the state of the syncing of the node
-       **/
-      syncState: AugmentedRpc<() => Observable<SyncState>>;
-      /**
-       * Retrieves the version of the node
-       **/
-      version: AugmentedRpc<() => Observable<Text>>;
-    };
-    web3: {
-      /**
-       * Returns current client version.
-       **/
-      clientVersion: AugmentedRpc<() => Observable<Text>>;
-      /**
-       * Returns sha3 of the given data
-       **/
-      sha3: AugmentedRpc<(data: Bytes | string | Uint8Array) => Observable<H256>>;
-    };
-  } // RpcInterface
-} // declare module
deletedjs-packages/types/src/augment-api-runtime.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api-runtime.ts
+++ /dev/null
@@ -1,264 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/api-base/types/calls';
-
-import type { ApiTypes, AugmentedCall, DecoratedCallBase } from '@polkadot/api-base/types';
-import type { Bytes, Null, Option, Result, U256, Vec, bool, u256, u32, u64 } from '@polkadot/types-codec';
-import type { AnyNumber, ITuple } from '@polkadot/types-codec/types';
-import type { CheckInherentsResult, InherentData } from '@polkadot/types/interfaces/blockbuilder';
-import type { BlockHash } from '@polkadot/types/interfaces/chain';
-import type { AuthorityId } from '@polkadot/types/interfaces/consensus';
-import type { CollationInfo } from '@polkadot/types/interfaces/cumulus';
-import type { BlockV2, EthReceiptV3, EthTransactionStatus, TransactionV2 } from '@polkadot/types/interfaces/eth';
-import type { EvmAccount, EvmCallInfoV2, EvmCreateInfoV2 } from '@polkadot/types/interfaces/evm';
-import type { Extrinsic } from '@polkadot/types/interfaces/extrinsics';
-import type { OpaqueMetadata } from '@polkadot/types/interfaces/metadata';
-import type { FeeDetails, RuntimeDispatchInfo } from '@polkadot/types/interfaces/payment';
-import type { AccountId, Balance, Block, H160, H256, Header, Index, KeyTypeId, Permill, SlotDuration, Weight } from '@polkadot/types/interfaces/runtime';
-import type { RuntimeVersion } from '@polkadot/types/interfaces/state';
-import type { ApplyExtrinsicResult, DispatchError } from '@polkadot/types/interfaces/system';
-import type { TransactionSource, TransactionValidity } from '@polkadot/types/interfaces/txqueue';
-import type { IExtrinsic, Observable } from '@polkadot/types/types';
-
-export type __AugmentedCall<ApiType extends ApiTypes> = AugmentedCall<ApiType>;
-export type __DecoratedCallBase<ApiType extends ApiTypes> = DecoratedCallBase<ApiType>;
-
-declare module '@polkadot/api-base/types/calls' {
-  interface AugmentedCalls<ApiType extends ApiTypes> {
-    /** 0xbc9d89904f5b923f/1 */
-    accountNonceApi: {
-      /**
-       * The API to query account nonce (aka transaction index)
-       **/
-      accountNonce: AugmentedCall<ApiType, (accountId: AccountId | string | Uint8Array) => Observable<Index>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0xdd718d5cc53262d4/1 */
-    auraApi: {
-      /**
-       * Return the current set of authorities.
-       **/
-      authorities: AugmentedCall<ApiType, () => Observable<Vec<AuthorityId>>>;
-      /**
-       * Returns the slot duration for Aura.
-       **/
-      slotDuration: AugmentedCall<ApiType, () => Observable<SlotDuration>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0x40fe3ad401f8959a/6 */
-    blockBuilder: {
-      /**
-       * Apply the given extrinsic.
-       **/
-      applyExtrinsic: AugmentedCall<ApiType, (extrinsic: Extrinsic | IExtrinsic | string | Uint8Array) => Observable<ApplyExtrinsicResult>>;
-      /**
-       * Check that the inherents are valid.
-       **/
-      checkInherents: AugmentedCall<ApiType, (block: Block | { header?: any; extrinsics?: any } | string | Uint8Array, data: InherentData | { data?: any } | string | Uint8Array) => Observable<CheckInherentsResult>>;
-      /**
-       * Finish the current block.
-       **/
-      finalizeBlock: AugmentedCall<ApiType, () => Observable<Header>>;
-      /**
-       * Generate inherent extrinsics.
-       **/
-      inherentExtrinsics: AugmentedCall<ApiType, (inherent: InherentData | { data?: any } | string | Uint8Array) => Observable<Vec<Extrinsic>>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0xea93e3f16f3d6962/2 */
-    collectCollationInfo: {
-      /**
-       * Collect information about a collation.
-       **/
-      collectCollationInfo: AugmentedCall<ApiType, (header: Header | { parentHash?: any; number?: any; stateRoot?: any; extrinsicsRoot?: any; digest?: any } | string | Uint8Array) => Observable<CollationInfo>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0xe65b00e46cedd0aa/2 */
-    convertTransactionRuntimeApi: {
-      /**
-       * Converts an Ethereum-style transaction to Extrinsic
-       **/
-      convertTransaction: AugmentedCall<ApiType, (transaction: TransactionV2 | { Legacy: any } | { EIP2930: any } | { EIP1559: any } | string | Uint8Array) => Observable<Extrinsic>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0xdf6acb689907609b/4 */
-    core: {
-      /**
-       * Execute the given block.
-       **/
-      executeBlock: AugmentedCall<ApiType, (block: Block | { header?: any; extrinsics?: any } | string | Uint8Array) => Observable<Null>>;
-      /**
-       * Initialize a block with the given header.
-       **/
-      initializeBlock: AugmentedCall<ApiType, (header: Header | { parentHash?: any; number?: any; stateRoot?: any; extrinsicsRoot?: any; digest?: any } | string | Uint8Array) => Observable<Null>>;
-      /**
-       * Returns the version of the runtime.
-       **/
-      version: AugmentedCall<ApiType, () => Observable<RuntimeVersion>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0x582211f65bb14b89/5 */
-    ethereumRuntimeRPCApi: {
-      /**
-       * Returns pallet_evm::Accounts by address.
-       **/
-      accountBasic: AugmentedCall<ApiType, (address: H160 | string | Uint8Array) => Observable<EvmAccount>>;
-      /**
-       * For a given account address, returns pallet_evm::AccountCodes.
-       **/
-      accountCodeAt: AugmentedCall<ApiType, (address: H160 | string | Uint8Array) => Observable<Bytes>>;
-      /**
-       * Returns the converted FindAuthor::find_author authority id.
-       **/
-      author: AugmentedCall<ApiType, () => Observable<H160>>;
-      /**
-       * Returns a frame_ethereum::call response. If `estimate` is true,
-       **/
-      call: AugmentedCall<ApiType, (from: H160 | string | Uint8Array, to: H160 | string | Uint8Array, data: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: U256 | AnyNumber | Uint8Array, maxFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option<Vec<ITuple<[H160, Vec<H256>]>>> | null | Uint8Array | Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => Observable<Result<EvmCallInfoV2, DispatchError>>>;
-      /**
-       * Returns runtime defined pallet_evm::ChainId.
-       **/
-      chainId: AugmentedCall<ApiType, () => Observable<u64>>;
-      /**
-       * Returns a frame_ethereum::call response. If `estimate` is true,
-       **/
-      create: AugmentedCall<ApiType, (from: H160 | string | Uint8Array, data: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: U256 | AnyNumber | Uint8Array, maxFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, estimate: bool | boolean | Uint8Array, accessList: Option<Vec<ITuple<[H160, Vec<H256>]>>> | null | Uint8Array | Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => Observable<Result<EvmCreateInfoV2, DispatchError>>>;
-      /**
-       * Return all the current data for a block in a single runtime call.
-       **/
-      currentAll: AugmentedCall<ApiType, () => Observable<ITuple<[Option<BlockV2>, Option<Vec<EthReceiptV3>>, Option<Vec<EthTransactionStatus>>]>>>;
-      /**
-       * Return the current block.
-       **/
-      currentBlock: AugmentedCall<ApiType, () => Observable<BlockV2>>;
-      /**
-       * Return the current receipt.
-       **/
-      currentReceipts: AugmentedCall<ApiType, () => Observable<Option<Vec<EthReceiptV3>>>>;
-      /**
-       * Return the current transaction status.
-       **/
-      currentTransactionStatuses: AugmentedCall<ApiType, () => Observable<Option<Vec<EthTransactionStatus>>>>;
-      /**
-       * Return the elasticity multiplier.
-       **/
-      elasticity: AugmentedCall<ApiType, () => Observable<Option<Permill>>>;
-      /**
-       * Receives a `Vec<OpaqueExtrinsic>` and filters all the ethereum transactions.
-       **/
-      extrinsicFilter: AugmentedCall<ApiType, (xts: Vec<Extrinsic> | (Extrinsic | IExtrinsic | string | Uint8Array)[]) => Observable<Vec<TransactionV2>>>;
-      /**
-       * Returns FixedGasPrice::min_gas_price
-       **/
-      gasPrice: AugmentedCall<ApiType, () => Observable<u256>>;
-      /**
-       * For a given account address and index, returns pallet_evm::AccountStorages.
-       **/
-      storageAt: AugmentedCall<ApiType, (address: H160 | string | Uint8Array, index: u256 | AnyNumber | Uint8Array) => Observable<H256>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0x37e397fc7c91f5e4/2 */
-    metadata: {
-      /**
-       * Returns the metadata of a runtime
-       **/
-      metadata: AugmentedCall<ApiType, () => Observable<OpaqueMetadata>>;
-      /**
-       * Returns the metadata at a given version.
-       **/
-      metadataAtVersion: AugmentedCall<ApiType, (version: u32 | AnyNumber | Uint8Array) => Observable<Option<OpaqueMetadata>>>;
-      /**
-       * Returns the supported metadata versions.
-       **/
-      metadataVersions: AugmentedCall<ApiType, () => Observable<Vec<u32>>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0xf78b278be53f454c/2 */
-    offchainWorkerApi: {
-      /**
-       * Starts the off-chain task for given block header.
-       **/
-      offchainWorker: AugmentedCall<ApiType, (header: Header | { parentHash?: any; number?: any; stateRoot?: any; extrinsicsRoot?: any; digest?: any } | string | Uint8Array) => Observable<Null>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0xab3c0572291feb8b/1 */
-    sessionKeys: {
-      /**
-       * Decode the given public session keys.
-       **/
-      decodeSessionKeys: AugmentedCall<ApiType, (encoded: Bytes | string | Uint8Array) => Observable<Option<Vec<ITuple<[Bytes, KeyTypeId]>>>>>;
-      /**
-       * Generate a set of session keys with optionally using the given seed.
-       **/
-      generateSessionKeys: AugmentedCall<ApiType, (seed: Option<Bytes> | null | Uint8Array | Bytes | string) => Observable<Bytes>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0xd2bc9897eed08f15/3 */
-    taggedTransactionQueue: {
-      /**
-       * Validate the transaction.
-       **/
-      validateTransaction: AugmentedCall<ApiType, (source: TransactionSource | 'InBlock' | 'Local' | 'External' | number | Uint8Array, tx: Extrinsic | IExtrinsic | string | Uint8Array, blockHash: BlockHash | string | Uint8Array) => Observable<TransactionValidity>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-    /** 0x37c8bb1350a9a2a8/4 */
-    transactionPaymentApi: {
-      /**
-       * The transaction fee details
-       **/
-      queryFeeDetails: AugmentedCall<ApiType, (uxt: Extrinsic | IExtrinsic | string | Uint8Array, len: u32 | AnyNumber | Uint8Array) => Observable<FeeDetails>>;
-      /**
-       * The transaction info
-       **/
-      queryInfo: AugmentedCall<ApiType, (uxt: Extrinsic | IExtrinsic | string | Uint8Array, len: u32 | AnyNumber | Uint8Array) => Observable<RuntimeDispatchInfo>>;
-      /**
-       * Query the output of the current LengthToFee given some input
-       **/
-      queryLengthToFee: AugmentedCall<ApiType, (length: u32 | AnyNumber | Uint8Array) => Observable<Balance>>;
-      /**
-       * Query the output of the current WeightToFee given some input
-       **/
-      queryWeightToFee: AugmentedCall<ApiType, (weight: Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => Observable<Balance>>;
-      /**
-       * Generic call
-       **/
-      [key: string]: DecoratedCallBase<ApiType>;
-    };
-  } // AugmentedCalls
-} // declare module
deletedjs-packages/types/src/augment-api-tx.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api-tx.ts
+++ /dev/null
@@ -1,1250 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/api-base/types/submittable';
-
-import type { ApiTypes, AugmentedSubmittable, SubmittableExtrinsic, SubmittableExtrinsicFunction } from '@polkadot/api-base/types';
-import type { Data } from '@polkadot/types';
-import type { Bytes, Compact, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
-import type { AnyNumber, IMethod, ITuple } from '@polkadot/types-codec/types';
-import type { AccountId32, Call, H160, H256, MultiAddress } from '@polkadot/types/interfaces/runtime';
-import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumLog, EthereumTransactionTransactionV2, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, OpalRuntimeOriginCaller, OpalRuntimeRuntimeCommonSessionKeys, OrmlVestingVestingSchedule, PalletConfigurationAppPromotionConfiguration, PalletDemocracyConviction, PalletDemocracyMetadataOwner, PalletDemocracyVoteAccountVote, PalletEvmAccountBasicCrossAccountIdRepr, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletIdentityBitFlags, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistration, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, SpWeightsWeightV2Weight, StagingXcmV3MultiLocation, StagingXcmV3WeightLimit, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission } from '@polkadot/types/lookup';
-
-export type __AugmentedSubmittable = AugmentedSubmittable<() => unknown>;
-export type __SubmittableExtrinsic<ApiType extends ApiTypes> = SubmittableExtrinsic<ApiType>;
-export type __SubmittableExtrinsicFunction<ApiType extends ApiTypes> = SubmittableExtrinsicFunction<ApiType>;
-
-declare module '@polkadot/api-base/types/submittable' {
-  interface AugmentedSubmittables<ApiType extends ApiTypes> {
-    appPromotion: {
-      /**
-       * See [`Pallet::force_unstake`].
-       **/
-      forceUnstake: AugmentedSubmittable<(pendingBlocks: Vec<u32> | (u32 | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<u32>]>;
-      /**
-       * See [`Pallet::payout_stakers`].
-       **/
-      payoutStakers: AugmentedSubmittable<(stakersNumber: Option<u8> | null | Uint8Array | u8 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u8>]>;
-      /**
-       * See [`Pallet::set_admin_address`].
-       **/
-      setAdminAddress: AugmentedSubmittable<(admin: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * See [`Pallet::sponsor_collection`].
-       **/
-      sponsorCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::sponsor_contract`].
-       **/
-      sponsorContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
-      /**
-       * See [`Pallet::stake`].
-       **/
-      stake: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
-      /**
-       * See [`Pallet::stop_sponsoring_collection`].
-       **/
-      stopSponsoringCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::stop_sponsoring_contract`].
-       **/
-      stopSponsoringContract: AugmentedSubmittable<(contractId: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
-      /**
-       * See [`Pallet::unstake_all`].
-       **/
-      unstakeAll: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::unstake_partial`].
-       **/
-      unstakePartial: AugmentedSubmittable<(amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u128]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    balances: {
-      /**
-       * See [`Pallet::force_set_balance`].
-       **/
-      forceSetBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
-      /**
-       * See [`Pallet::force_transfer`].
-       **/
-      forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress, Compact<u128>]>;
-      /**
-       * See [`Pallet::force_unreserve`].
-       **/
-      forceUnreserve: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, u128]>;
-      /**
-       * See [`Pallet::set_balance_deprecated`].
-       **/
-      setBalanceDeprecated: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, newFree: Compact<u128> | AnyNumber | Uint8Array, oldReserved: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>, Compact<u128>]>;
-      /**
-       * See [`Pallet::transfer`].
-       **/
-      transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
-      /**
-       * See [`Pallet::transfer_all`].
-       **/
-      transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, bool]>;
-      /**
-       * See [`Pallet::transfer_allow_death`].
-       **/
-      transferAllowDeath: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
-      /**
-       * See [`Pallet::transfer_keep_alive`].
-       **/
-      transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Compact<u128>]>;
-      /**
-       * See [`Pallet::upgrade_accounts`].
-       **/
-      upgradeAccounts: AugmentedSubmittable<(who: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    collatorSelection: {
-      /**
-       * See [`Pallet::add_invulnerable`].
-       **/
-      addInvulnerable: AugmentedSubmittable<(updated: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
-      /**
-       * See [`Pallet::force_release_license`].
-       **/
-      forceReleaseLicense: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
-      /**
-       * See [`Pallet::get_license`].
-       **/
-      getLicense: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::offboard`].
-       **/
-      offboard: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::onboard`].
-       **/
-      onboard: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::release_license`].
-       **/
-      releaseLicense: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::remove_invulnerable`].
-       **/
-      removeInvulnerable: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    configuration: {
-      /**
-       * See [`Pallet::set_app_promotion_configuration_override`].
-       **/
-      setAppPromotionConfigurationOverride: AugmentedSubmittable<(configuration: PalletConfigurationAppPromotionConfiguration | { recalculationInterval?: any; pendingInterval?: any; intervalIncome?: any; maxStakersPerCalculation?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletConfigurationAppPromotionConfiguration]>;
-      /**
-       * See [`Pallet::set_collator_selection_desired_collators`].
-       **/
-      setCollatorSelectionDesiredCollators: AugmentedSubmittable<(max: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
-      /**
-       * See [`Pallet::set_collator_selection_kick_threshold`].
-       **/
-      setCollatorSelectionKickThreshold: AugmentedSubmittable<(threshold: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
-      /**
-       * See [`Pallet::set_collator_selection_license_bond`].
-       **/
-      setCollatorSelectionLicenseBond: AugmentedSubmittable<(amount: Option<u128> | null | Uint8Array | u128 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u128>]>;
-      /**
-       * See [`Pallet::set_min_gas_price_override`].
-       **/
-      setMinGasPriceOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
-      /**
-       * See [`Pallet::set_weight_to_fee_coefficient_override`].
-       **/
-      setWeightToFeeCoefficientOverride: AugmentedSubmittable<(coeff: Option<u64> | null | Uint8Array | u64 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u64>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    council: {
-      /**
-       * See [`Pallet::close`].
-       **/
-      close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, SpWeightsWeightV2Weight, Compact<u32>]>;
-      /**
-       * See [`Pallet::disapprove_proposal`].
-       **/
-      disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
-      /**
-       * See [`Pallet::execute`].
-       **/
-      execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, Compact<u32>]>;
-      /**
-       * See [`Pallet::propose`].
-       **/
-      propose: AugmentedSubmittable<(threshold: Compact<u32> | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Call, Compact<u32>]>;
-      /**
-       * See [`Pallet::set_members`].
-       **/
-      setMembers: AugmentedSubmittable<(newMembers: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[], prime: Option<AccountId32> | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>, Option<AccountId32>, u32]>;
-      /**
-       * See [`Pallet::vote`].
-       **/
-      vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, bool]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    councilMembership: {
-      /**
-       * See [`Pallet::add_member`].
-       **/
-      addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::change_key`].
-       **/
-      changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::clear_prime`].
-       **/
-      clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::remove_member`].
-       **/
-      removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::reset_members`].
-       **/
-      resetMembers: AugmentedSubmittable<(members: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
-      /**
-       * See [`Pallet::set_prime`].
-       **/
-      setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::swap_member`].
-       **/
-      swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    cumulusXcm: {
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    democracy: {
-      /**
-       * See [`Pallet::blacklist`].
-       **/
-      blacklist: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, maybeRefIndex: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [H256, Option<u32>]>;
-      /**
-       * See [`Pallet::cancel_proposal`].
-       **/
-      cancelProposal: AugmentedSubmittable<(propIndex: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
-      /**
-       * See [`Pallet::cancel_referendum`].
-       **/
-      cancelReferendum: AugmentedSubmittable<(refIndex: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
-      /**
-       * See [`Pallet::clear_public_proposals`].
-       **/
-      clearPublicProposals: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::delegate`].
-       **/
-      delegate: AugmentedSubmittable<(to: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, conviction: PalletDemocracyConviction | 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x' | number | Uint8Array, balance: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletDemocracyConviction, u128]>;
-      /**
-       * See [`Pallet::emergency_cancel`].
-       **/
-      emergencyCancel: AugmentedSubmittable<(refIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::external_propose`].
-       **/
-      externalPropose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded]>;
-      /**
-       * See [`Pallet::external_propose_default`].
-       **/
-      externalProposeDefault: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded]>;
-      /**
-       * See [`Pallet::external_propose_majority`].
-       **/
-      externalProposeMajority: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded]>;
-      /**
-       * See [`Pallet::fast_track`].
-       **/
-      fastTrack: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, votingPeriod: u32 | AnyNumber | Uint8Array, delay: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, u32, u32]>;
-      /**
-       * See [`Pallet::propose`].
-       **/
-      propose: AugmentedSubmittable<(proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, value: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [FrameSupportPreimagesBounded, Compact<u128>]>;
-      /**
-       * See [`Pallet::remove_other_vote`].
-       **/
-      removeOtherVote: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, u32]>;
-      /**
-       * See [`Pallet::remove_vote`].
-       **/
-      removeVote: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::second`].
-       **/
-      second: AugmentedSubmittable<(proposal: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
-      /**
-       * See [`Pallet::set_metadata`].
-       **/
-      setMetadata: AugmentedSubmittable<(owner: PalletDemocracyMetadataOwner | { External: any } | { Proposal: any } | { Referendum: any } | string | Uint8Array, maybeHash: Option<H256> | null | Uint8Array | H256 | string) => SubmittableExtrinsic<ApiType>, [PalletDemocracyMetadataOwner, Option<H256>]>;
-      /**
-       * See [`Pallet::undelegate`].
-       **/
-      undelegate: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::unlock`].
-       **/
-      unlock: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::veto_external`].
-       **/
-      vetoExternal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
-      /**
-       * See [`Pallet::vote`].
-       **/
-      vote: AugmentedSubmittable<(refIndex: Compact<u32> | AnyNumber | Uint8Array, vote: PalletDemocracyVoteAccountVote | { Standard: any } | { Split: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, PalletDemocracyVoteAccountVote]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    dmpQueue: {
-      /**
-       * See [`Pallet::service_overweight`].
-       **/
-      serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u64, SpWeightsWeightV2Weight]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    ethereum: {
-      /**
-       * See [`Pallet::transact`].
-       **/
-      transact: AugmentedSubmittable<(transaction: EthereumTransactionTransactionV2 | { Legacy: any } | { EIP2930: any } | { EIP1559: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [EthereumTransactionTransactionV2]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    evm: {
-      /**
-       * See [`Pallet::call`].
-       **/
-      call: AugmentedSubmittable<(source: H160 | string | Uint8Array, target: H160 | string | Uint8Array, input: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, accessList: Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic<ApiType>, [H160, H160, Bytes, U256, u64, U256, Option<U256>, Option<U256>, Vec<ITuple<[H160, Vec<H256>]>>]>;
-      /**
-       * See [`Pallet::create`].
-       **/
-      create: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, accessList: Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic<ApiType>, [H160, Bytes, U256, u64, U256, Option<U256>, Option<U256>, Vec<ITuple<[H160, Vec<H256>]>>]>;
-      /**
-       * See [`Pallet::create2`].
-       **/
-      create2: AugmentedSubmittable<(source: H160 | string | Uint8Array, init: Bytes | string | Uint8Array, salt: H256 | string | Uint8Array, value: U256 | AnyNumber | Uint8Array, gasLimit: u64 | AnyNumber | Uint8Array, maxFeePerGas: U256 | AnyNumber | Uint8Array, maxPriorityFeePerGas: Option<U256> | null | Uint8Array | U256 | AnyNumber, nonce: Option<U256> | null | Uint8Array | U256 | AnyNumber, accessList: Vec<ITuple<[H160, Vec<H256>]>> | ([H160 | string | Uint8Array, Vec<H256> | (H256 | string | Uint8Array)[]])[]) => SubmittableExtrinsic<ApiType>, [H160, Bytes, H256, U256, u64, U256, Option<U256>, Option<U256>, Vec<ITuple<[H160, Vec<H256>]>>]>;
-      /**
-       * See [`Pallet::withdraw`].
-       **/
-      withdraw: AugmentedSubmittable<(address: H160 | string | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160, u128]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    evmContractHelpers: {
-      /**
-       * See [`Pallet::migrate_from_self_sponsoring`].
-       **/
-      migrateFromSelfSponsoring: AugmentedSubmittable<(addresses: Vec<H160> | (H160 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<H160>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    evmMigration: {
-      /**
-       * See [`Pallet::begin`].
-       **/
-      begin: AugmentedSubmittable<(address: H160 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160]>;
-      /**
-       * See [`Pallet::finish`].
-       **/
-      finish: AugmentedSubmittable<(address: H160 | string | Uint8Array, code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H160, Bytes]>;
-      /**
-       * See [`Pallet::insert_eth_logs`].
-       **/
-      insertEthLogs: AugmentedSubmittable<(logs: Vec<EthereumLog> | (EthereumLog | { address?: any; topics?: any; data?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<EthereumLog>]>;
-      /**
-       * See [`Pallet::insert_events`].
-       **/
-      insertEvents: AugmentedSubmittable<(events: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Bytes>]>;
-      /**
-       * See [`Pallet::remove_rmrk_data`].
-       **/
-      removeRmrkData: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::set_data`].
-       **/
-      setData: AugmentedSubmittable<(address: H160 | string | Uint8Array, data: Vec<ITuple<[H256, H256]>> | ([H256 | string | Uint8Array, H256 | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [H160, Vec<ITuple<[H256, H256]>>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    fellowshipCollective: {
-      /**
-       * See [`Pallet::add_member`].
-       **/
-      addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::cleanup_poll`].
-       **/
-      cleanupPoll: AugmentedSubmittable<(pollIndex: u32 | AnyNumber | Uint8Array, max: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
-      /**
-       * See [`Pallet::demote_member`].
-       **/
-      demoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::promote_member`].
-       **/
-      promoteMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::remove_member`].
-       **/
-      removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, minRank: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, u16]>;
-      /**
-       * See [`Pallet::vote`].
-       **/
-      vote: AugmentedSubmittable<(poll: u32 | AnyNumber | Uint8Array, aye: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, bool]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    fellowshipReferenda: {
-      /**
-       * See [`Pallet::cancel`].
-       **/
-      cancel: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::kill`].
-       **/
-      kill: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::nudge_referendum`].
-       **/
-      nudgeReferendum: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::one_fewer_deciding`].
-       **/
-      oneFewerDeciding: AugmentedSubmittable<(track: u16 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u16]>;
-      /**
-       * See [`Pallet::place_decision_deposit`].
-       **/
-      placeDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::refund_decision_deposit`].
-       **/
-      refundDecisionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::refund_submission_deposit`].
-       **/
-      refundSubmissionDeposit: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::set_metadata`].
-       **/
-      setMetadata: AugmentedSubmittable<(index: u32 | AnyNumber | Uint8Array, maybeHash: Option<H256> | null | Uint8Array | H256 | string) => SubmittableExtrinsic<ApiType>, [u32, Option<H256>]>;
-      /**
-       * See [`Pallet::submit`].
-       **/
-      submit: AugmentedSubmittable<(proposalOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, proposal: FrameSupportPreimagesBounded | { Legacy: any } | { Inline: any } | { Lookup: any } | string | Uint8Array, enactmentMoment: FrameSupportScheduleDispatchTime | { At: any } | { After: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [OpalRuntimeOriginCaller, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    foreignAssets: {
-      /**
-       * See [`Pallet::register_foreign_asset`].
-       **/
-      registerForeignAsset: AugmentedSubmittable<(owner: AccountId32 | string | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>;
-      /**
-       * See [`Pallet::update_foreign_asset`].
-       **/
-      updateForeignAsset: AugmentedSubmittable<(foreignAssetId: u32 | AnyNumber | Uint8Array, location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, metadata: PalletForeignAssetsModuleAssetMetadata | { name?: any; symbol?: any; decimals?: any; minimalBalance?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, StagingXcmVersionedMultiLocation, PalletForeignAssetsModuleAssetMetadata]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    identity: {
-      /**
-       * See [`Pallet::add_registrar`].
-       **/
-      addRegistrar: AugmentedSubmittable<(account: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::add_sub`].
-       **/
-      addSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Data]>;
-      /**
-       * See [`Pallet::cancel_request`].
-       **/
-      cancelRequest: AugmentedSubmittable<(regIndex: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::clear_identity`].
-       **/
-      clearIdentity: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::force_insert_identities`].
-       **/
-      forceInsertIdentities: AugmentedSubmittable<(identities: Vec<ITuple<[AccountId32, PalletIdentityRegistration]>> | ([AccountId32 | string | Uint8Array, PalletIdentityRegistration | { judgements?: any; deposit?: any; info?: any } | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[AccountId32, PalletIdentityRegistration]>>]>;
-      /**
-       * See [`Pallet::force_remove_identities`].
-       **/
-      forceRemoveIdentities: AugmentedSubmittable<(identities: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
-      /**
-       * See [`Pallet::force_set_subs`].
-       **/
-      forceSetSubs: AugmentedSubmittable<(subs: Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>> | ([AccountId32 | string | Uint8Array, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]> | [u128 | AnyNumber | Uint8Array, Vec<ITuple<[AccountId32, Data]>> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]]])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>>]>;
-      /**
-       * See [`Pallet::kill_identity`].
-       **/
-      killIdentity: AugmentedSubmittable<(target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::provide_judgement`].
-       **/
-      provideJudgement: AugmentedSubmittable<(regIndex: Compact<u32> | AnyNumber | Uint8Array, target: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, judgement: PalletIdentityJudgement | { Unknown: any } | { FeePaid: any } | { Reasonable: any } | { KnownGood: any } | { OutOfDate: any } | { LowQuality: any } | { Erroneous: any } | string | Uint8Array, identity: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, MultiAddress, PalletIdentityJudgement, H256]>;
-      /**
-       * See [`Pallet::quit_sub`].
-       **/
-      quitSub: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::remove_sub`].
-       **/
-      removeSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::rename_sub`].
-       **/
-      renameSub: AugmentedSubmittable<(sub: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, data: Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Data]>;
-      /**
-       * See [`Pallet::request_judgement`].
-       **/
-      requestJudgement: AugmentedSubmittable<(regIndex: Compact<u32> | AnyNumber | Uint8Array, maxFee: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Compact<u128>]>;
-      /**
-       * See [`Pallet::set_account_id`].
-       **/
-      setAccountId: AugmentedSubmittable<(index: Compact<u32> | AnyNumber | Uint8Array, updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, MultiAddress]>;
-      /**
-       * See [`Pallet::set_fee`].
-       **/
-      setFee: AugmentedSubmittable<(index: Compact<u32> | AnyNumber | Uint8Array, fee: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Compact<u128>]>;
-      /**
-       * See [`Pallet::set_fields`].
-       **/
-      setFields: AugmentedSubmittable<(index: Compact<u32> | AnyNumber | Uint8Array, fields: PalletIdentityBitFlags) => SubmittableExtrinsic<ApiType>, [Compact<u32>, PalletIdentityBitFlags]>;
-      /**
-       * See [`Pallet::set_identity`].
-       **/
-      setIdentity: AugmentedSubmittable<(info: PalletIdentityIdentityInfo | { additional?: any; display?: any; legal?: any; web?: any; riot?: any; email?: any; pgpFingerprint?: any; image?: any; twitter?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletIdentityIdentityInfo]>;
-      /**
-       * See [`Pallet::set_subs`].
-       **/
-      setSubs: AugmentedSubmittable<(subs: Vec<ITuple<[AccountId32, Data]>> | ([AccountId32 | string | Uint8Array, Data | { None: any } | { Raw: any } | { BlakeTwo256: any } | { Sha256: any } | { Keccak256: any } | { ShaThree256: any } | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[AccountId32, Data]>>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    inflation: {
-      /**
-       * See [`Pallet::start_inflation`].
-       **/
-      startInflation: AugmentedSubmittable<(inflationStartRelayBlock: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    maintenance: {
-      /**
-       * See [`Pallet::disable`].
-       **/
-      disable: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::enable`].
-       **/
-      enable: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    parachainInfo: {
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    parachainSystem: {
-      /**
-       * See [`Pallet::authorize_upgrade`].
-       **/
-      authorizeUpgrade: AugmentedSubmittable<(codeHash: H256 | string | Uint8Array, checkVersion: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, bool]>;
-      /**
-       * See [`Pallet::enact_authorized_upgrade`].
-       **/
-      enactAuthorizedUpgrade: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
-      /**
-       * See [`Pallet::set_validation_data`].
-       **/
-      setValidationData: AugmentedSubmittable<(data: CumulusPrimitivesParachainInherentParachainInherentData | { validationData?: any; relayChainState?: any; downwardMessages?: any; horizontalMessages?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [CumulusPrimitivesParachainInherentParachainInherentData]>;
-      /**
-       * See [`Pallet::sudo_send_upward_message`].
-       **/
-      sudoSendUpwardMessage: AugmentedSubmittable<(message: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    polkadotXcm: {
-      /**
-       * See [`Pallet::execute`].
-       **/
-      execute: AugmentedSubmittable<(message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array, maxWeight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedXcm, SpWeightsWeightV2Weight]>;
-      /**
-       * See [`Pallet::force_default_xcm_version`].
-       **/
-      forceDefaultXcmVersion: AugmentedSubmittable<(maybeXcmVersion: Option<u32> | null | Uint8Array | u32 | AnyNumber) => SubmittableExtrinsic<ApiType>, [Option<u32>]>;
-      /**
-       * See [`Pallet::force_subscribe_version_notify`].
-       **/
-      forceSubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation]>;
-      /**
-       * See [`Pallet::force_suspension`].
-       **/
-      forceSuspension: AugmentedSubmittable<(suspended: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [bool]>;
-      /**
-       * See [`Pallet::force_unsubscribe_version_notify`].
-       **/
-      forceUnsubscribeVersionNotify: AugmentedSubmittable<(location: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation]>;
-      /**
-       * See [`Pallet::force_xcm_version`].
-       **/
-      forceXcmVersion: AugmentedSubmittable<(location: StagingXcmV3MultiLocation | { parents?: any; interior?: any } | string | Uint8Array, version: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmV3MultiLocation, u32]>;
-      /**
-       * See [`Pallet::limited_reserve_transfer_assets`].
-       **/
-      limitedReserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>;
-      /**
-       * See [`Pallet::limited_teleport_assets`].
-       **/
-      limitedTeleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array, weightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32, StagingXcmV3WeightLimit]>;
-      /**
-       * See [`Pallet::reserve_transfer_assets`].
-       **/
-      reserveTransferAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>;
-      /**
-       * See [`Pallet::send`].
-       **/
-      send: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, message: StagingXcmVersionedXcm | { V2: any } | { V3: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedXcm]>;
-      /**
-       * See [`Pallet::teleport_assets`].
-       **/
-      teleportAssets: AugmentedSubmittable<(dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, beneficiary: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeAssetItem: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiLocation, StagingXcmVersionedMultiAssets, u32]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    preimage: {
-      /**
-       * See [`Pallet::note_preimage`].
-       **/
-      notePreimage: AugmentedSubmittable<(bytes: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
-      /**
-       * See [`Pallet::request_preimage`].
-       **/
-      requestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
-      /**
-       * See [`Pallet::unnote_preimage`].
-       **/
-      unnotePreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
-      /**
-       * See [`Pallet::unrequest_preimage`].
-       **/
-      unrequestPreimage: AugmentedSubmittable<(hash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    scheduler: {
-      /**
-       * See [`Pallet::cancel`].
-       **/
-      cancel: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, index: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
-      /**
-       * See [`Pallet::cancel_named`].
-       **/
-      cancelNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [U8aFixed]>;
-      /**
-       * See [`Pallet::schedule`].
-       **/
-      schedule: AugmentedSubmittable<(when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
-      /**
-       * See [`Pallet::schedule_after`].
-       **/
-      scheduleAfter: AugmentedSubmittable<(after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
-      /**
-       * See [`Pallet::schedule_named`].
-       **/
-      scheduleNamed: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, when: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [U8aFixed, u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
-      /**
-       * See [`Pallet::schedule_named_after`].
-       **/
-      scheduleNamedAfter: AugmentedSubmittable<(id: U8aFixed | string | Uint8Array, after: u32 | AnyNumber | Uint8Array, maybePeriodic: Option<ITuple<[u32, u32]>> | null | Uint8Array | ITuple<[u32, u32]> | [u32 | AnyNumber | Uint8Array, u32 | AnyNumber | Uint8Array], priority: u8 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [U8aFixed, u32, Option<ITuple<[u32, u32]>>, u8, Call]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    session: {
-      /**
-       * See [`Pallet::purge_keys`].
-       **/
-      purgeKeys: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::set_keys`].
-       **/
-      setKeys: AugmentedSubmittable<(keys: OpalRuntimeRuntimeCommonSessionKeys | { aura?: any } | string | Uint8Array, proof: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [OpalRuntimeRuntimeCommonSessionKeys, Bytes]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    stateTrieMigration: {
-      /**
-       * See [`Pallet::continue_migrate`].
-       **/
-      continueMigrate: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array, realSizeUpper: u32 | AnyNumber | Uint8Array, witnessTask: PalletStateTrieMigrationMigrationTask | { progressTop?: any; progressChild?: any; size_?: any; topItems?: any; childItems?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletStateTrieMigrationMigrationLimits, u32, PalletStateTrieMigrationMigrationTask]>;
-      /**
-       * See [`Pallet::control_auto_migration`].
-       **/
-      controlAutoMigration: AugmentedSubmittable<(maybeConfig: Option<PalletStateTrieMigrationMigrationLimits> | null | Uint8Array | PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string) => SubmittableExtrinsic<ApiType>, [Option<PalletStateTrieMigrationMigrationLimits>]>;
-      /**
-       * See [`Pallet::force_set_progress`].
-       **/
-      forceSetProgress: AugmentedSubmittable<(progressTop: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array, progressChild: PalletStateTrieMigrationProgress | { ToStart: any } | { LastKey: any } | { Complete: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletStateTrieMigrationProgress, PalletStateTrieMigrationProgress]>;
-      /**
-       * See [`Pallet::migrate_custom_child`].
-       **/
-      migrateCustomChild: AugmentedSubmittable<(root: Bytes | string | Uint8Array, childKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[], totalSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes, Vec<Bytes>, u32]>;
-      /**
-       * See [`Pallet::migrate_custom_top`].
-       **/
-      migrateCustomTop: AugmentedSubmittable<(keys: Vec<Bytes> | (Bytes | string | Uint8Array)[], witnessSize: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<Bytes>, u32]>;
-      /**
-       * See [`Pallet::set_signed_max_limits`].
-       **/
-      setSignedMaxLimits: AugmentedSubmittable<(limits: PalletStateTrieMigrationMigrationLimits | { size_?: any; item?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletStateTrieMigrationMigrationLimits]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    structure: {
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    sudo: {
-      /**
-       * See [`Pallet::set_key`].
-       **/
-      setKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::sudo`].
-       **/
-      sudo: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call]>;
-      /**
-       * See [`Pallet::sudo_as`].
-       **/
-      sudoAs: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, Call]>;
-      /**
-       * See [`Pallet::sudo_unchecked_weight`].
-       **/
-      sudoUncheckedWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, SpWeightsWeightV2Weight]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    system: {
-      /**
-       * See [`Pallet::kill_prefix`].
-       **/
-      killPrefix: AugmentedSubmittable<(prefix: Bytes | string | Uint8Array, subkeys: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes, u32]>;
-      /**
-       * See [`Pallet::kill_storage`].
-       **/
-      killStorage: AugmentedSubmittable<(keys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Bytes>]>;
-      /**
-       * See [`Pallet::remark`].
-       **/
-      remark: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
-      /**
-       * See [`Pallet::remark_with_event`].
-       **/
-      remarkWithEvent: AugmentedSubmittable<(remark: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
-      /**
-       * See [`Pallet::set_code`].
-       **/
-      setCode: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
-      /**
-       * See [`Pallet::set_code_without_checks`].
-       **/
-      setCodeWithoutChecks: AugmentedSubmittable<(code: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Bytes]>;
-      /**
-       * See [`Pallet::set_heap_pages`].
-       **/
-      setHeapPages: AugmentedSubmittable<(pages: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u64]>;
-      /**
-       * See [`Pallet::set_storage`].
-       **/
-      setStorage: AugmentedSubmittable<(items: Vec<ITuple<[Bytes, Bytes]>> | ([Bytes | string | Uint8Array, Bytes | string | Uint8Array])[]) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[Bytes, Bytes]>>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    technicalCommittee: {
-      /**
-       * See [`Pallet::close`].
-       **/
-      close: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, proposalWeightBound: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, SpWeightsWeightV2Weight, Compact<u32>]>;
-      /**
-       * See [`Pallet::disapprove_proposal`].
-       **/
-      disapproveProposal: AugmentedSubmittable<(proposalHash: H256 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256]>;
-      /**
-       * See [`Pallet::execute`].
-       **/
-      execute: AugmentedSubmittable<(proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, Compact<u32>]>;
-      /**
-       * See [`Pallet::propose`].
-       **/
-      propose: AugmentedSubmittable<(threshold: Compact<u32> | AnyNumber | Uint8Array, proposal: Call | IMethod | string | Uint8Array, lengthBound: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>, Call, Compact<u32>]>;
-      /**
-       * See [`Pallet::set_members`].
-       **/
-      setMembers: AugmentedSubmittable<(newMembers: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[], prime: Option<AccountId32> | null | Uint8Array | AccountId32 | string, oldCount: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>, Option<AccountId32>, u32]>;
-      /**
-       * See [`Pallet::vote`].
-       **/
-      vote: AugmentedSubmittable<(proposal: H256 | string | Uint8Array, index: Compact<u32> | AnyNumber | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [H256, Compact<u32>, bool]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    technicalCommitteeMembership: {
-      /**
-       * See [`Pallet::add_member`].
-       **/
-      addMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::change_key`].
-       **/
-      changeKey: AugmentedSubmittable<(updated: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::clear_prime`].
-       **/
-      clearPrime: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::remove_member`].
-       **/
-      removeMember: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::reset_members`].
-       **/
-      resetMembers: AugmentedSubmittable<(members: Vec<AccountId32> | (AccountId32 | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<AccountId32>]>;
-      /**
-       * See [`Pallet::set_prime`].
-       **/
-      setPrime: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::swap_member`].
-       **/
-      swapMember: AugmentedSubmittable<(remove: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, add: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    testUtils: {
-      /**
-       * See `Pallet::batch_all`.
-       **/
-      batchAll: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
-      /**
-       * See `Pallet::enable`.
-       **/
-      enable: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See `Pallet::inc_test_value`.
-       **/
-      incTestValue: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See `Pallet::just_take_fee`.
-       **/
-      justTakeFee: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See `Pallet::set_test_value`.
-       **/
-      setTestValue: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See `Pallet::set_test_value_and_rollback`.
-       **/
-      setTestValueAndRollback: AugmentedSubmittable<(value: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    timestamp: {
-      /**
-       * See [`Pallet::set`].
-       **/
-      set: AugmentedSubmittable<(now: Compact<u64> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u64>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    tokens: {
-      /**
-       * See [`Pallet::force_transfer`].
-       **/
-      forceTransfer: AugmentedSubmittable<(source: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, MultiAddress, PalletForeignAssetsAssetId, Compact<u128>]>;
-      /**
-       * See [`Pallet::set_balance`].
-       **/
-      setBalance: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, newFree: Compact<u128> | AnyNumber | Uint8Array, newReserved: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, Compact<u128>, Compact<u128>]>;
-      /**
-       * See [`Pallet::transfer`].
-       **/
-      transfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, Compact<u128>]>;
-      /**
-       * See [`Pallet::transfer_all`].
-       **/
-      transferAll: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, keepAlive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, bool]>;
-      /**
-       * See [`Pallet::transfer_keep_alive`].
-       **/
-      transferKeepAlive: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: Compact<u128> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, PalletForeignAssetsAssetId, Compact<u128>]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    treasury: {
-      /**
-       * See [`Pallet::approve_proposal`].
-       **/
-      approveProposal: AugmentedSubmittable<(proposalId: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
-      /**
-       * See [`Pallet::propose_spend`].
-       **/
-      proposeSpend: AugmentedSubmittable<(value: Compact<u128> | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u128>, MultiAddress]>;
-      /**
-       * See [`Pallet::reject_proposal`].
-       **/
-      rejectProposal: AugmentedSubmittable<(proposalId: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
-      /**
-       * See [`Pallet::remove_approval`].
-       **/
-      removeApproval: AugmentedSubmittable<(proposalId: Compact<u32> | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u32>]>;
-      /**
-       * See [`Pallet::spend`].
-       **/
-      spend: AugmentedSubmittable<(amount: Compact<u128> | AnyNumber | Uint8Array, beneficiary: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Compact<u128>, MultiAddress]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    unique: {
-      /**
-       * See [`Pallet::add_collection_admin`].
-       **/
-      addCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newAdminId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * See [`Pallet::add_to_allow_list`].
-       **/
-      addToAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * See [`Pallet::approve`].
-       **/
-      approve: AugmentedSubmittable<(spender: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
-      /**
-       * See [`Pallet::approve_from`].
-       **/
-      approveFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, to: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
-      /**
-       * See [`Pallet::burn_from`].
-       **/
-      burnFrom: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32, u128]>;
-      /**
-       * See [`Pallet::burn_item`].
-       **/
-      burnItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
-      /**
-       * See [`Pallet::change_collection_owner`].
-       **/
-      changeCollectionOwner: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newOwner: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, AccountId32]>;
-      /**
-       * See [`Pallet::confirm_sponsorship`].
-       **/
-      confirmSponsorship: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::create_collection`].
-       **/
-      createCollection: AugmentedSubmittable<(collectionName: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], collectionDescription: Vec<u16> | (u16 | AnyNumber | Uint8Array)[], tokenPrefix: Bytes | string | Uint8Array, mode: UpDataStructsCollectionMode | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<u16>, Vec<u16>, Bytes, UpDataStructsCollectionMode]>;
-      /**
-       * See [`Pallet::create_collection_ex`].
-       **/
-      createCollectionEx: AugmentedSubmittable<(data: UpDataStructsCreateCollectionData | { mode?: any; access?: any; name?: any; description?: any; tokenPrefix?: any; limits?: any; permissions?: any; tokenPropertyPermissions?: any; properties?: any; adminList?: any; pendingSponsor?: any; flags?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [UpDataStructsCreateCollectionData]>;
-      /**
-       * See [`Pallet::create_item`].
-       **/
-      createItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, data: UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, UpDataStructsCreateItemData]>;
-      /**
-       * See [`Pallet::create_multiple_items`].
-       **/
-      createMultipleItems: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, owner: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, itemsData: Vec<UpDataStructsCreateItemData> | (UpDataStructsCreateItemData | { NFT: any } | { Fungible: any } | { ReFungible: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, Vec<UpDataStructsCreateItemData>]>;
-      /**
-       * See [`Pallet::create_multiple_items_ex`].
-       **/
-      createMultipleItemsEx: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, data: UpDataStructsCreateItemExData | { NFT: any } | { Fungible: any } | { RefungibleMultipleItems: any } | { RefungibleMultipleOwners: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCreateItemExData]>;
-      /**
-       * See [`Pallet::delete_collection_properties`].
-       **/
-      deleteCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<Bytes>]>;
-      /**
-       * See [`Pallet::delete_token_properties`].
-       **/
-      deleteTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec<Bytes> | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, u32, Vec<Bytes>]>;
-      /**
-       * See [`Pallet::destroy_collection`].
-       **/
-      destroyCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::force_repair_collection`].
-       **/
-      forceRepairCollection: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::force_repair_item`].
-       **/
-      forceRepairItem: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32]>;
-      /**
-       * See [`Pallet::remove_collection_admin`].
-       **/
-      removeCollectionAdmin: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, accountId: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * See [`Pallet::remove_collection_sponsor`].
-       **/
-      removeCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::remove_from_allow_list`].
-       **/
-      removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-      /**
-       * See [`Pallet::repartition`].
-       **/
-      repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, u32, u128]>;
-      /**
-       * See [`Pallet::set_allowance_for_all`].
-       **/
-      setAllowanceForAll: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, operator: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, approve: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
-      /**
-       * See [`Pallet::set_collection_limits`].
-       **/
-      setCollectionLimits: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionLimits | { accountTokenOwnershipLimit?: any; sponsoredDataSize?: any; sponsoredDataRateLimit?: any; tokenLimit?: any; sponsorTransferTimeout?: any; sponsorApproveTimeout?: any; ownerCanTransfer?: any; ownerCanDestroy?: any; transfersEnabled?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCollectionLimits]>;
-      /**
-       * See [`Pallet::set_collection_permissions`].
-       **/
-      setCollectionPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newPermission: UpDataStructsCollectionPermissions | { access?: any; mintMode?: any; nesting?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, UpDataStructsCollectionPermissions]>;
-      /**
-       * See [`Pallet::set_collection_properties`].
-       **/
-      setCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, properties: Vec<UpDataStructsProperty> | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsProperty>]>;
-      /**
-       * See [`Pallet::set_collection_sponsor`].
-       **/
-      setCollectionSponsor: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newSponsor: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, AccountId32]>;
-      /**
-       * See [`Pallet::set_token_properties`].
-       **/
-      setTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, properties: Vec<UpDataStructsProperty> | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, u32, Vec<UpDataStructsProperty>]>;
-      /**
-       * See [`Pallet::set_token_property_permissions`].
-       **/
-      setTokenPropertyPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyPermissions: Vec<UpDataStructsPropertyKeyPermission> | (UpDataStructsPropertyKeyPermission | { key?: any; permission?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [u32, Vec<UpDataStructsPropertyKeyPermission>]>;
-      /**
-       * See [`Pallet::set_transfers_enabled_flag`].
-       **/
-      setTransfersEnabledFlag: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, value: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32, bool]>;
-      /**
-       * See [`Pallet::transfer`].
-       **/
-      transfer: AugmentedSubmittable<(recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
-      /**
-       * See [`Pallet::transfer_from`].
-       **/
-      transferFrom: AugmentedSubmittable<(from: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, recipient: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, itemId: u32 | AnyNumber | Uint8Array, value: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u32, u32, u128]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    utility: {
-      /**
-       * See [`Pallet::as_derivative`].
-       **/
-      asDerivative: AugmentedSubmittable<(index: u16 | AnyNumber | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u16, Call]>;
-      /**
-       * See [`Pallet::batch`].
-       **/
-      batch: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
-      /**
-       * See [`Pallet::batch_all`].
-       **/
-      batchAll: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
-      /**
-       * See [`Pallet::dispatch_as`].
-       **/
-      dispatchAs: AugmentedSubmittable<(asOrigin: OpalRuntimeOriginCaller | { system: any } | { Void: any } | { Council: any } | { TechnicalCommittee: any } | { PolkadotXcm: any } | { CumulusXcm: any } | { Origins: any } | { Ethereum: any } | string | Uint8Array, call: Call | IMethod | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [OpalRuntimeOriginCaller, Call]>;
-      /**
-       * See [`Pallet::force_batch`].
-       **/
-      forceBatch: AugmentedSubmittable<(calls: Vec<Call> | (Call | IMethod | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [Vec<Call>]>;
-      /**
-       * See [`Pallet::with_weight`].
-       **/
-      withWeight: AugmentedSubmittable<(call: Call | IMethod | string | Uint8Array, weight: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Call, SpWeightsWeightV2Weight]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    vesting: {
-      /**
-       * See [`Pallet::claim`].
-       **/
-      claim: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::claim_for`].
-       **/
-      claimFor: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress]>;
-      /**
-       * See [`Pallet::update_vesting_schedules`].
-       **/
-      updateVestingSchedules: AugmentedSubmittable<(who: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, vestingSchedules: Vec<OrmlVestingVestingSchedule> | (OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [MultiAddress, Vec<OrmlVestingVestingSchedule>]>;
-      /**
-       * See [`Pallet::vested_transfer`].
-       **/
-      vestedTransfer: AugmentedSubmittable<(dest: MultiAddress | { Id: any } | { Index: any } | { Raw: any } | { Address32: any } | { Address20: any } | string | Uint8Array, schedule: OrmlVestingVestingSchedule | { start?: any; period?: any; periodCount?: any; perPeriod?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [MultiAddress, OrmlVestingVestingSchedule]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    xcmpQueue: {
-      /**
-       * See [`Pallet::resume_xcm_execution`].
-       **/
-      resumeXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::service_overweight`].
-       **/
-      serviceOverweight: AugmentedSubmittable<(index: u64 | AnyNumber | Uint8Array, weightLimit: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [u64, SpWeightsWeightV2Weight]>;
-      /**
-       * See [`Pallet::suspend_xcm_execution`].
-       **/
-      suspendXcmExecution: AugmentedSubmittable<() => SubmittableExtrinsic<ApiType>, []>;
-      /**
-       * See [`Pallet::update_drop_threshold`].
-       **/
-      updateDropThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::update_resume_threshold`].
-       **/
-      updateResumeThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::update_suspend_threshold`].
-       **/
-      updateSuspendThreshold: AugmentedSubmittable<(updated: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [u32]>;
-      /**
-       * See [`Pallet::update_threshold_weight`].
-       **/
-      updateThresholdWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [SpWeightsWeightV2Weight]>;
-      /**
-       * See [`Pallet::update_weight_restrict_decay`].
-       **/
-      updateWeightRestrictDecay: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [SpWeightsWeightV2Weight]>;
-      /**
-       * See [`Pallet::update_xcmp_max_individual_weight`].
-       **/
-      updateXcmpMaxIndividualWeight: AugmentedSubmittable<(updated: SpWeightsWeightV2Weight | { refTime?: any; proofSize?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [SpWeightsWeightV2Weight]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-    xTokens: {
-      /**
-       * See [`Pallet::transfer`].
-       **/
-      transfer: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletForeignAssetsAssetId, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
-      /**
-       * See [`Pallet::transfer_multiasset`].
-       **/
-      transferMultiasset: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
-      /**
-       * See [`Pallet::transfer_multiassets`].
-       **/
-      transferMultiassets: AugmentedSubmittable<(assets: StagingXcmVersionedMultiAssets | { V2: any } | { V3: any } | string | Uint8Array, feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiAssets, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
-      /**
-       * See [`Pallet::transfer_multiasset_with_fee`].
-       **/
-      transferMultiassetWithFee: AugmentedSubmittable<(asset: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, fee: StagingXcmVersionedMultiAsset | { V2: any } | { V3: any } | string | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
-      /**
-       * See [`Pallet::transfer_multicurrencies`].
-       **/
-      transferMulticurrencies: AugmentedSubmittable<(currencies: Vec<ITuple<[PalletForeignAssetsAssetId, u128]>> | ([PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, u128 | AnyNumber | Uint8Array])[], feeItem: u32 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [Vec<ITuple<[PalletForeignAssetsAssetId, u128]>>, u32, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
-      /**
-       * See [`Pallet::transfer_with_fee`].
-       **/
-      transferWithFee: AugmentedSubmittable<(currencyId: PalletForeignAssetsAssetId | { ForeignAssetId: any } | { NativeAssetId: any } | string | Uint8Array, amount: u128 | AnyNumber | Uint8Array, fee: u128 | AnyNumber | Uint8Array, dest: StagingXcmVersionedMultiLocation | { V2: any } | { V3: any } | string | Uint8Array, destWeightLimit: StagingXcmV3WeightLimit | { Unlimited: any } | { Limited: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PalletForeignAssetsAssetId, u128, u128, StagingXcmVersionedMultiLocation, StagingXcmV3WeightLimit]>;
-      /**
-       * Generic tx
-       **/
-      [key: string]: SubmittableExtrinsicFunction<ApiType>;
-    };
-  } // AugmentedSubmittables
-} // declare module
deletedjs-packages/types/src/augment-api.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-api.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-chain`, do not edit
-/* eslint-disable */
-
-import './augment-api-consts.js';
-import './augment-api-errors.js';
-import './augment-api-events.js';
-import './augment-api-query.js';
-import './augment-api-tx.js';
-import './augment-api-rpc.js';
-import './augment-api-runtime.js';
deletedjs-packages/types/src/augment-types.tsdiffbeforeafterboth
--- a/js-packages/types/src/augment-types.ts
+++ /dev/null
@@ -1,1232 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/types/types/registry';
-
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesIdAmount, PalletBalancesReasons, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCollatorSelectionHoldReason, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCodeMetadata, PalletEvmCoderSubstrateError, PalletEvmContractHelpersCall, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletGovOriginsOrigin, PalletIdentityBitFlags, PalletIdentityCall, PalletIdentityError, PalletIdentityEvent, PalletIdentityIdentityField, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletRankedCollectiveCall, PalletRankedCollectiveError, PalletRankedCollectiveEvent, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletReferendaCall, PalletReferendaCurve, PalletReferendaDecidingStatus, PalletReferendaDeposit, PalletReferendaError, PalletReferendaEvent, PalletReferendaReferendumInfo, PalletReferendaReferendumStatus, PalletReferendaTrackInfo, PalletRefungibleError, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduled, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStateTrieMigrationCall, PalletStateTrieMigrationError, PalletStateTrieMigrationEvent, PalletStateTrieMigrationMigrationCompute, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, ParachainInfoCall, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5AbridgedHrmpChannel, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, PolkadotPrimitivesVstagingAsyncBackingParams, SpArithmeticArithmeticError, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmDoubleEncoded, StagingXcmV2BodyId, StagingXcmV2BodyPart, StagingXcmV2Instruction, StagingXcmV2Junction, StagingXcmV2MultiAsset, StagingXcmV2MultiLocation, StagingXcmV2MultiassetAssetId, StagingXcmV2MultiassetAssetInstance, StagingXcmV2MultiassetFungibility, StagingXcmV2MultiassetMultiAssetFilter, StagingXcmV2MultiassetMultiAssets, StagingXcmV2MultiassetWildFungibility, StagingXcmV2MultiassetWildMultiAsset, StagingXcmV2MultilocationJunctions, StagingXcmV2NetworkId, StagingXcmV2OriginKind, StagingXcmV2Response, StagingXcmV2TraitsError, StagingXcmV2WeightLimit, StagingXcmV2Xcm, StagingXcmV3Instruction, StagingXcmV3Junction, StagingXcmV3JunctionBodyId, StagingXcmV3JunctionBodyPart, StagingXcmV3JunctionNetworkId, StagingXcmV3Junctions, StagingXcmV3MaybeErrorCode, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetAssetId, StagingXcmV3MultiassetAssetInstance, StagingXcmV3MultiassetFungibility, StagingXcmV3MultiassetMultiAssetFilter, StagingXcmV3MultiassetMultiAssets, StagingXcmV3MultiassetWildFungibility, StagingXcmV3MultiassetWildMultiAsset, StagingXcmV3PalletInfo, StagingXcmV3QueryResponseInfo, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3WeightLimit, StagingXcmV3Xcm, StagingXcmVersionedAssetId, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedResponse, StagingXcmVersionedXcm, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission,
-  UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue } from '@polkadot/types/lookup';
-import type { Data, StorageKey } from '@polkadot/types';
-import type { BitVec, Bool, Bytes, F32, F64, I128, I16, I256, I32, I64, I8, ISize, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, f32, f64, i128, i16, i256, i32, i64, i8, isize, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec';
-import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets';
-import type { BlockAttestations, IncludedBlocks, MoreAttestations } from '@polkadot/types/interfaces/attestations';
-import type { RawAuraPreDigest } from '@polkadot/types/interfaces/aura';
-import type { ExtrinsicOrHash, ExtrinsicStatus } from '@polkadot/types/interfaces/author';
-import type { UncleEntryItem } from '@polkadot/types/interfaces/authorship';
-import type { AllowedSlots, BabeAuthorityWeight, BabeBlockWeight, BabeEpochConfiguration, BabeEquivocationProof, BabeGenesisConfiguration, BabeGenesisConfigurationV1, BabeWeight, Epoch, EpochAuthorship, MaybeRandomness, MaybeVrf, NextConfigDescriptor, NextConfigDescriptorV1, OpaqueKeyOwnershipProof, Randomness, RawBabePreDigest, RawBabePreDigestCompat, RawBabePreDigestPrimary, RawBabePreDigestPrimaryTo159, RawBabePreDigestSecondaryPlain, RawBabePreDigestSecondaryTo159, RawBabePreDigestSecondaryVRF, RawBabePreDigestTo159, SlotNumber, VrfData, VrfOutput, VrfProof } from '@polkadot/types/interfaces/babe';
-import type { AccountData, BalanceLock, BalanceLockTo212, BalanceStatus, Reasons, ReserveData, ReserveIdentifier, VestingSchedule, WithdrawReasons } from '@polkadot/types/interfaces/balances';
-import type { BeefyAuthoritySet, BeefyCommitment, BeefyEquivocationProof, BeefyId, BeefyNextAuthoritySet, BeefyPayload, BeefyPayloadId, BeefySignedCommitment, BeefyVoteMessage, MmrRootHash, ValidatorSet, ValidatorSetId } from '@polkadot/types/interfaces/beefy';
-import type { BenchmarkBatch, BenchmarkConfig, BenchmarkList, BenchmarkMetadata, BenchmarkParameter, BenchmarkResult } from '@polkadot/types/interfaces/benchmark';
-import type { CheckInherentsResult, InherentData, InherentIdentifier } from '@polkadot/types/interfaces/blockbuilder';
-import type { BridgeMessageId, BridgedBlockHash, BridgedBlockNumber, BridgedHeader, CallOrigin, ChainId, DeliveredMessages, DispatchFeePayment, InboundLaneData, InboundRelayer, InitializationData, LaneId, MessageData, MessageKey, MessageNonce, MessagesDeliveryProofOf, MessagesProofOf, OperatingMode, OutboundLaneData, OutboundMessageFee, OutboundPayload, Parameter, RelayerId, UnrewardedRelayer, UnrewardedRelayersState } from '@polkadot/types/interfaces/bridges';
-import type { BlockHash } from '@polkadot/types/interfaces/chain';
-import type { PrefixedStorageKey } from '@polkadot/types/interfaces/childstate';
-import type { StatementKind } from '@polkadot/types/interfaces/claims';
-import type { CollectiveOrigin, MemberCount, ProposalIndex, Votes, VotesTo230 } from '@polkadot/types/interfaces/collective';
-import type { AuthorityId, RawVRFOutput } from '@polkadot/types/interfaces/consensus';
-import type { AliveContractInfo, CodeHash, CodeSource, CodeUploadRequest, CodeUploadResult, CodeUploadResultValue, ContractCallFlags, ContractCallRequest, ContractExecResult, ContractExecResultOk, ContractExecResultResult, ContractExecResultSuccessTo255, ContractExecResultSuccessTo260, ContractExecResultTo255, ContractExecResultTo260, ContractExecResultTo267, ContractExecResultU64, ContractInfo, ContractInstantiateResult, ContractInstantiateResultTo267, ContractInstantiateResultTo299, ContractInstantiateResultU64, ContractReturnFlags, ContractStorageKey, DeletedContract, ExecReturnValue, Gas, HostFnWeights, HostFnWeightsTo264, InstantiateRequest, InstantiateRequestV1, InstantiateRequestV2, InstantiateReturnValue, InstantiateReturnValueOk, InstantiateReturnValueTo267, InstructionWeights, Limits, LimitsTo264, PrefabWasmModule, RentProjection, Schedule, ScheduleTo212, ScheduleTo258, ScheduleTo264, SeedOf, StorageDeposit, TombstoneContractInfo, TrieId } from '@polkadot/types/interfaces/contracts';
-import type { ContractConstructorSpecLatest, ContractConstructorSpecV0, ContractConstructorSpecV1, ContractConstructorSpecV2, ContractConstructorSpecV3, ContractConstructorSpecV4, ContractContractSpecV0, ContractContractSpecV1, ContractContractSpecV2, ContractContractSpecV3, ContractContractSpecV4, ContractCryptoHasher, ContractDiscriminant, ContractDisplayName, ContractEnvironmentV4, ContractEventParamSpecLatest, ContractEventParamSpecV0, ContractEventParamSpecV2, ContractEventSpecLatest, ContractEventSpecV0, ContractEventSpecV1, ContractEventSpecV2, ContractLayoutArray, ContractLayoutCell, ContractLayoutEnum, ContractLayoutHash, ContractLayoutHashingStrategy, ContractLayoutKey, ContractLayoutStruct, ContractLayoutStructField, ContractMessageParamSpecLatest, ContractMessageParamSpecV0, ContractMessageParamSpecV2, ContractMessageSpecLatest, ContractMessageSpecV0, ContractMessageSpecV1, ContractMessageSpecV2, ContractMessageSpecV3, ContractMetadata, ContractMetadataLatest, ContractMetadataV0, ContractMetadataV1, ContractMetadataV2, ContractMetadataV3, ContractMetadataV4, ContractProject, ContractProjectContract, ContractProjectInfo, ContractProjectSource, ContractProjectV0, ContractSelector, ContractStorageLayout, ContractTypeSpec } from '@polkadot/types/interfaces/contractsAbi';
-import type { FundIndex, FundInfo, LastContribution, TrieIndex } from '@polkadot/types/interfaces/crowdloan';
-import type { CollationInfo, CollationInfoV1, ConfigData, MessageId, OverweightIndex, PageCounter, PageIndexData } from '@polkadot/types/interfaces/cumulus';
-import type { AccountVote, AccountVoteSplit, AccountVoteStandard, Conviction, Delegations, PreimageStatus, PreimageStatusAvailable, PriorLock, PropIndex, Proposal, ProxyState, ReferendumIndex, ReferendumInfo, ReferendumInfoFinished, ReferendumInfoTo239, ReferendumStatus, Tally, Voting, VotingDelegating, VotingDirect, VotingDirectVote } from '@polkadot/types/interfaces/democracy';
-import type { BlockStats } from '@polkadot/types/interfaces/dev';
-import type { ApprovalFlag, DefunctVoter, Renouncing, SetIndex, Vote, VoteIndex, VoteThreshold, VoterInfo } from '@polkadot/types/interfaces/elections';
-import type { CreatedBlock, ImportedAux } from '@polkadot/types/interfaces/engine';
-import type { BlockV0, BlockV1, BlockV2, EIP1559Transaction, EIP2930Transaction, EthAccessList, EthAccessListItem, EthAccount, EthAddress, EthBlock, EthBloom, EthCallRequest, EthFeeHistory, EthFilter, EthFilterAddress, EthFilterChanges, EthFilterTopic, EthFilterTopicEntry, EthFilterTopicInner, EthHeader, EthLog, EthReceipt, EthReceiptV0, EthReceiptV3, EthRichBlock, EthRichHeader, EthStorageProof, EthSubKind, EthSubParams, EthSubResult, EthSyncInfo, EthSyncStatus, EthTransaction, EthTransactionAction, EthTransactionCondition, EthTransactionRequest, EthTransactionSignature, EthTransactionStatus, EthWork, EthereumAccountId, EthereumAddress, EthereumLookupSource, EthereumSignature, LegacyTransaction, TransactionV0, TransactionV1, TransactionV2 } from '@polkadot/types/interfaces/eth';
-import type { EvmAccount, EvmCallInfo, EvmCallInfoV2, EvmCreateInfo, EvmCreateInfoV2, EvmLog, EvmVicinity, EvmWeightInfo, ExitError, ExitFatal, ExitReason, ExitRevert, ExitSucceed } from '@polkadot/types/interfaces/evm';
-import type { AnySignature, EcdsaSignature, Ed25519Signature, Era, Extrinsic, ExtrinsicEra, ExtrinsicPayload, ExtrinsicPayloadUnknown, ExtrinsicPayloadV4, ExtrinsicSignature, ExtrinsicSignatureV4, ExtrinsicUnknown, ExtrinsicV4, ImmortalEra, MortalEra, MultiSignature, Signature, SignerPayload, Sr25519Signature } from '@polkadot/types/interfaces/extrinsics';
-import type { FungiblesAccessError } from '@polkadot/types/interfaces/fungibles';
-import type { AssetOptions, Owner, PermissionLatest, PermissionVersions, PermissionsV1 } from '@polkadot/types/interfaces/genericAsset';
-import type { ActiveGilt, ActiveGiltsTotal, ActiveIndex, GiltBid } from '@polkadot/types/interfaces/gilt';
-import type { AuthorityIndex, AuthorityList, AuthoritySet, AuthoritySetChange, AuthoritySetChanges, AuthorityWeight, DelayKind, DelayKindBest, EncodedFinalityProofs, ForkTreePendingChange, ForkTreePendingChangeNode, GrandpaCommit, GrandpaEquivocation, GrandpaEquivocationProof, GrandpaEquivocationValue, GrandpaJustification, GrandpaPrecommit, GrandpaPrevote, GrandpaSignedPrecommit, JustificationNotification, KeyOwnerProof, NextAuthority, PendingChange, PendingPause, PendingResume, Precommits, Prevotes, ReportedRoundStates, RoundState, SetId, StoredPendingChange, StoredState } from '@polkadot/types/interfaces/grandpa';
-import type { IdentityFields, IdentityInfo, IdentityInfoAdditional, IdentityInfoTo198, IdentityJudgement, RegistrarIndex, RegistrarInfo, Registration, RegistrationJudgement, RegistrationTo198 } from '@polkadot/types/interfaces/identity';
-import type { AuthIndex, AuthoritySignature, Heartbeat, HeartbeatTo244, OpaqueMultiaddr, OpaqueNetworkState, OpaquePeerId } from '@polkadot/types/interfaces/imOnline';
-import type { CallIndex, LotteryConfig } from '@polkadot/types/interfaces/lottery';
-import type { CustomMetadata15, CustomValueMetadata15, ErrorMetadataLatest, ErrorMetadataV10, ErrorMetadataV11, ErrorMetadataV12, ErrorMetadataV13, ErrorMetadataV14, ErrorMetadataV9, EventMetadataLatest, EventMetadataV10, EventMetadataV11, EventMetadataV12, EventMetadataV13, EventMetadataV14, EventMetadataV9, ExtrinsicMetadataLatest, ExtrinsicMetadataV11, ExtrinsicMetadataV12, ExtrinsicMetadataV13, ExtrinsicMetadataV14, ExtrinsicMetadataV15, FunctionArgumentMetadataLatest, FunctionArgumentMetadataV10, FunctionArgumentMetadataV11, FunctionArgumentMetadataV12, FunctionArgumentMetadataV13, FunctionArgumentMetadataV14, FunctionArgumentMetadataV9, FunctionMetadataLatest, FunctionMetadataV10, FunctionMetadataV11, FunctionMetadataV12, FunctionMetadataV13, FunctionMetadataV14, FunctionMetadataV9, MetadataAll, MetadataLatest, MetadataV10, MetadataV11, MetadataV12, MetadataV13, MetadataV14, MetadataV15, MetadataV9, ModuleConstantMetadataV10, ModuleConstantMetadataV11, ModuleConstantMetadataV12, ModuleConstantMetadataV13, ModuleConstantMetadataV9, ModuleMetadataV10, ModuleMetadataV11, ModuleMetadataV12, ModuleMetadataV13, ModuleMetadataV9, OpaqueMetadata, OuterEnums15, PalletCallMetadataLatest, PalletCallMetadataV14, PalletConstantMetadataLatest, PalletConstantMetadataV14, PalletErrorMetadataLatest, PalletErrorMetadataV14, PalletEventMetadataLatest, PalletEventMetadataV14, PalletMetadataLatest, PalletMetadataV14, PalletMetadataV15, PalletStorageMetadataLatest, PalletStorageMetadataV14, PortableType, PortableTypeV14, RuntimeApiMetadataLatest, RuntimeApiMetadataV15, RuntimeApiMethodMetadataV15, RuntimeApiMethodParamMetadataV15, SignedExtensionMetadataLatest, SignedExtensionMetadataV14, StorageEntryMetadataLatest, StorageEntryMetadataV10, StorageEntryMetadataV11, StorageEntryMetadataV12, StorageEntryMetadataV13, StorageEntryMetadataV14, StorageEntryMetadataV9, StorageEntryModifierLatest, StorageEntryModifierV10, StorageEntryModifierV11, StorageEntryModifierV12, StorageEntryModifierV13, StorageEntryModifierV14, StorageEntryModifierV9, StorageEntryTypeLatest, StorageEntryTypeV10, StorageEntryTypeV11, StorageEntryTypeV12, StorageEntryTypeV13, StorageEntryTypeV14, StorageEntryTypeV9, StorageHasher, StorageHasherV10, StorageHasherV11, StorageHasherV12, StorageHasherV13, StorageHasherV14, StorageHasherV9, StorageMetadataV10, StorageMetadataV11, StorageMetadataV12, StorageMetadataV13, StorageMetadataV9 } from '@polkadot/types/interfaces/metadata';
-import type { MmrBatchProof, MmrEncodableOpaqueLeaf, MmrError, MmrHash, MmrLeafBatchProof, MmrLeafIndex, MmrLeafProof, MmrNodeIndex, MmrProof } from '@polkadot/types/interfaces/mmr';
-import type { NftCollectionId, NftItemId } from '@polkadot/types/interfaces/nfts';
-import type { NpApiError, NpPoolId } from '@polkadot/types/interfaces/nompools';
-import type { StorageKind } from '@polkadot/types/interfaces/offchain';
-import type { DeferredOffenceOf, Kind, OffenceDetails, Offender, OpaqueTimeSlot, ReportIdOf, Reporter } from '@polkadot/types/interfaces/offences';
-import type { AbridgedCandidateReceipt, AbridgedHostConfiguration, AbridgedHrmpChannel, AssignmentId, AssignmentKind, AttestedCandidate, AuctionIndex, AuthorityDiscoveryId, AvailabilityBitfield, AvailabilityBitfieldRecord, BackedCandidate, Bidder, BufferedSessionChange, CandidateCommitments, CandidateDescriptor, CandidateEvent, CandidateHash, CandidateInfo, CandidatePendingAvailability, CandidateReceipt, CollatorId, CollatorSignature, CommittedCandidateReceipt, CoreAssignment, CoreIndex, CoreOccupied, CoreState, DisputeLocation, DisputeProof, DisputeResult, DisputeState, DisputeStatement, DisputeStatementSet, DisputesTimeSlot, DoubleVoteReport, DownwardMessage, ExecutorParam, ExecutorParams, ExecutorParamsHash, ExplicitDisputeStatement, GlobalValidationData, GlobalValidationSchedule, GroupIndex, GroupRotationInfo, HeadData, HostConfiguration, HrmpChannel, HrmpChannelId, HrmpOpenChannelRequest, InboundDownwardMessage, InboundHrmpMessage, InboundHrmpMessages, IncomingParachain, IncomingParachainDeploy, IncomingParachainFixed, InvalidDisputeStatementKind, LeasePeriod, LeasePeriodOf, LocalValidationData, MessageIngestionType, MessageQueueChain, MessagingStateSnapshot, MessagingStateSnapshotEgressEntry, MultiDisputeStatementSet, NewBidder, OccupiedCore, OccupiedCoreAssumption, OldV1SessionInfo, OutboundHrmpMessage, ParaGenesisArgs, ParaId, ParaInfo, ParaLifecycle, ParaPastCodeMeta, ParaScheduling, ParaValidatorIndex, ParachainDispatchOrigin, ParachainInherentData, ParachainProposal, ParachainsInherentData, ParathreadClaim, ParathreadClaimQueue, ParathreadEntry, PendingSlashes, PersistedValidationData, PvfCheckStatement, PvfExecTimeoutKind, PvfPrepTimeoutKind, QueuedParathread, RegisteredParachainInfo, RelayBlockNumber, RelayChainBlockNumber, RelayChainHash, RelayHash, Remark, ReplacementTimes, Retriable, ScheduledCore, Scheduling, ScrapedOnChainVotes, ServiceQuality, SessionInfo, SessionInfoValidatorGroup, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SigningContext, SlashingOffenceKind, SlotRange, SlotRange10, Statement, SubId, SystemInherentData, TransientValidationData, UpgradeGoAhead, UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode, ValidationCodeHash, ValidationData, ValidationDataType, ValidationFunctionParams, ValidatorSignature, ValidityAttestation, VecInboundHrmpMessage, WinnersData, WinnersData10, WinnersDataTuple, WinnersDataTuple10, WinningData, WinningData10, WinningDataEntry } from '@polkadot/types/interfaces/parachains';
-import type { FeeDetails, InclusionFee, RuntimeDispatchInfo, RuntimeDispatchInfoV1, RuntimeDispatchInfoV2 } from '@polkadot/types/interfaces/payment';
-import type { Approvals } from '@polkadot/types/interfaces/poll';
-import type { ProxyAnnouncement, ProxyDefinition, ProxyType } from '@polkadot/types/interfaces/proxy';
-import type { AccountStatus, AccountValidity } from '@polkadot/types/interfaces/purchase';
-import type { ActiveRecovery, RecoveryConfig } from '@polkadot/types/interfaces/recovery';
-import type { RpcMethods } from '@polkadot/types/interfaces/rpc';
-import type { AccountId, AccountId20, AccountId32, AccountId33, AccountIdOf, AccountIndex, Address, AssetId, Balance, BalanceOf, Block, BlockNumber, BlockNumberFor, BlockNumberOf, Call, CallHash, CallHashOf, ChangesTrieConfiguration, ChangesTrieSignal, CodecHash, Consensus, ConsensusEngineId, CrateVersion, Digest, DigestItem, EncodedJustification, ExtrinsicsWeight, Fixed128, Fixed64, FixedI128, FixedI64, FixedU128, FixedU64, H1024, H128, H160, H2048, H256, H32, H512, H64, Hash, Header, HeaderPartial, I32F32, Index, IndicesLookupSource, Justification, Justifications, KeyTypeId, KeyValue, LockIdentifier, LookupSource, LookupTarget, ModuleId, Moment, MultiAddress, MultiSigner, OpaqueCall, Origin, OriginCaller, PalletId, PalletVersion, PalletsOrigin, Pays, PerU16, Perbill, Percent, Permill, Perquintill, Phantom, PhantomData, PreRuntime, Releases, RuntimeCall, RuntimeDbWeight, RuntimeEvent, Seal, SealV0, SignedBlock, SignedBlockWithJustification, SignedBlockWithJustifications, Slot, SlotDuration, StorageData, StorageInfo, StorageProof, TransactionInfo, TransactionLongevity, TransactionPriority, TransactionStorageProof, TransactionTag, U32F32, ValidatorId, ValidatorIdOf, Weight, WeightMultiplier, WeightV0, WeightV1, WeightV2 } from '@polkadot/types/interfaces/runtime';
-import type { Si0Field, Si0LookupTypeId, Si0Path, Si0Type, Si0TypeDef, Si0TypeDefArray, Si0TypeDefBitSequence, Si0TypeDefCompact, Si0TypeDefComposite, Si0TypeDefPhantom, Si0TypeDefPrimitive, Si0TypeDefSequence, Si0TypeDefTuple, Si0TypeDefVariant, Si0TypeParameter, Si0Variant, Si1Field, Si1LookupTypeId, Si1Path, Si1Type, Si1TypeDef, Si1TypeDefArray, Si1TypeDefBitSequence, Si1TypeDefCompact, Si1TypeDefComposite, Si1TypeDefPrimitive, Si1TypeDefSequence, Si1TypeDefTuple, Si1TypeDefVariant, Si1TypeParameter, Si1Variant, SiField, SiLookupTypeId, SiPath, SiType, SiTypeDef, SiTypeDefArray, SiTypeDefBitSequence, SiTypeDefCompact, SiTypeDefComposite, SiTypeDefPrimitive, SiTypeDefSequence, SiTypeDefTuple, SiTypeDefVariant, SiTypeParameter, SiVariant } from '@polkadot/types/interfaces/scaleInfo';
-import type { Period, Priority, SchedulePeriod, SchedulePriority, Scheduled, ScheduledTo254, TaskAddress } from '@polkadot/types/interfaces/scheduler';
-import type { BeefyKey, FullIdentification, IdentificationTuple, Keys, MembershipProof, SessionIndex, SessionKeys1, SessionKeys10, SessionKeys10B, SessionKeys2, SessionKeys3, SessionKeys4, SessionKeys5, SessionKeys6, SessionKeys6B, SessionKeys7, SessionKeys7B, SessionKeys8, SessionKeys8B, SessionKeys9, SessionKeys9B, ValidatorCount } from '@polkadot/types/interfaces/session';
-import type { Bid, BidKind, SocietyJudgement, SocietyVote, StrikeCount, VouchingStatus } from '@polkadot/types/interfaces/society';
-import type { ActiveEraInfo, CompactAssignments, CompactAssignmentsTo257, CompactAssignmentsTo265, CompactAssignmentsWith16, CompactAssignmentsWith24, CompactScore, CompactScoreCompact, ElectionCompute, ElectionPhase, ElectionResult, ElectionScore, ElectionSize, ElectionStatus, EraIndex, EraPoints, EraRewardPoints, EraRewards, Exposure, ExtendedBalance, Forcing, IndividualExposure, KeyType, MomentOf, Nominations, NominatorIndex, NominatorIndexCompact, OffchainAccuracy, OffchainAccuracyCompact, PhragmenScore, Points, RawSolution, RawSolutionTo265, RawSolutionWith16, RawSolutionWith24, ReadySolution, RewardDestination, RewardPoint, RoundSnapshot, SeatHolder, SignedSubmission, SignedSubmissionOf, SignedSubmissionTo276, SlashJournalEntry, SlashingSpans, SlashingSpansTo204, SolutionOrSnapshotSize, SolutionSupport, SolutionSupports, SpanIndex, SpanRecord, StakingLedger, StakingLedgerTo223, StakingLedgerTo240, SubmissionIndicesOf, Supports, UnappliedSlash, UnappliedSlashOther, UnlockChunk, ValidatorIndex, ValidatorIndexCompact, ValidatorPrefs, ValidatorPrefsTo145, ValidatorPrefsTo196, ValidatorPrefsWithBlocked, ValidatorPrefsWithCommission, VoteWeight, Voter } from '@polkadot/types/interfaces/staking';
-import type { ApiId, BlockTrace, BlockTraceEvent, BlockTraceEventData, BlockTraceSpan, KeyValueOption, MigrationStatusResult, ReadProof, RuntimeVersion, RuntimeVersionApi, RuntimeVersionPartial, RuntimeVersionPre3, RuntimeVersionPre4, SpecVersion, StorageChangeSet, TraceBlockResponse, TraceError } from '@polkadot/types/interfaces/state';
-import type { WeightToFeeCoefficient } from '@polkadot/types/interfaces/support';
-import type { AccountInfo, AccountInfoWithDualRefCount, AccountInfoWithProviders, AccountInfoWithRefCount, AccountInfoWithRefCountU8, AccountInfoWithTripleRefCount, ApplyExtrinsicResult, ApplyExtrinsicResultPre6, ArithmeticError, BlockLength, BlockWeights, ChainProperties, ChainType, ConsumedWeight, DigestOf, DispatchClass, DispatchError, DispatchErrorModule, DispatchErrorModulePre6, DispatchErrorModuleU8, DispatchErrorModuleU8a, DispatchErrorPre6, DispatchErrorPre6First, DispatchErrorTo198, DispatchInfo, DispatchInfoTo190, DispatchInfoTo244, DispatchOutcome, DispatchOutcomePre6, DispatchResult, DispatchResultOf, DispatchResultTo198, Event, EventId, EventIndex, EventRecord, Health, InvalidTransaction, Key, LastRuntimeUpgradeInfo, NetworkState, NetworkStatePeerset, NetworkStatePeersetInfo, NodeRole, NotConnectedPeer, Peer, PeerEndpoint, PeerEndpointAddr, PeerInfo, PeerPing, PerDispatchClassU32, PerDispatchClassWeight, PerDispatchClassWeightsPerClass, Phase, RawOrigin, RefCount, RefCountTo259, SyncState, SystemOrigin, TokenError, TransactionValidityError, TransactionalError, UnknownTransaction, WeightPerClass } from '@polkadot/types/interfaces/system';
-import type { Bounty, BountyIndex, BountyStatus, BountyStatusActive, BountyStatusCuratorProposed, BountyStatusPendingPayout, OpenTip, OpenTipFinderTo225, OpenTipTip, OpenTipTo225, TreasuryProposal } from '@polkadot/types/interfaces/treasury';
-import type { Multiplier } from '@polkadot/types/interfaces/txpayment';
-import type { TransactionSource, TransactionValidity, ValidTransaction } from '@polkadot/types/interfaces/txqueue';
-import type { ClassDetails, ClassId, ClassMetadata, DepositBalance, DepositBalanceOf, DestroyWitness, InstanceDetails, InstanceId, InstanceMetadata } from '@polkadot/types/interfaces/uniques';
-import type { Multisig, Timepoint } from '@polkadot/types/interfaces/utility';
-import type { VestingInfo } from '@polkadot/types/interfaces/vesting';
-import type { AssetInstance, AssetInstanceV0, AssetInstanceV1, AssetInstanceV2, BodyId, BodyPart, DoubleEncodedCall, Fungibility, FungibilityV0, FungibilityV1, FungibilityV2, InboundStatus, InstructionV2, InteriorMultiLocation, Junction, JunctionV0, JunctionV1, JunctionV2, Junctions, JunctionsV1, JunctionsV2, MultiAsset, MultiAssetFilter, MultiAssetFilterV1, MultiAssetFilterV2, MultiAssetV0, MultiAssetV1, MultiAssetV2, MultiAssets, MultiAssetsV1, MultiAssetsV2, MultiLocation, MultiLocationV0, MultiLocationV1, MultiLocationV2, NetworkId, OriginKindV0, OriginKindV1, OriginKindV2, OutboundStatus, Outcome, QueryId, QueryStatus, QueueConfigData, Response, ResponseV0, ResponseV1, ResponseV2, ResponseV2Error, ResponseV2Result, VersionMigrationStage, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation, VersionedResponse, VersionedXcm, WeightLimitV2, WildFungibility, WildFungibilityV0, WildFungibilityV1, WildFungibilityV2, WildMultiAsset, WildMultiAssetV1, WildMultiAssetV2, Xcm, XcmAssetId, XcmError, XcmErrorV0, XcmErrorV1, XcmErrorV2, XcmOrder, XcmOrderV0, XcmOrderV1, XcmOrderV2, XcmOrigin, XcmOriginKind, XcmV0, XcmV1, XcmV2, XcmVersion, XcmpMessageFormat } from '@polkadot/types/interfaces/xcm';
-
-declare module '@polkadot/types/types/registry' {
-  interface InterfaceTypes {
-    AbridgedCandidateReceipt: AbridgedCandidateReceipt;
-    AbridgedHostConfiguration: AbridgedHostConfiguration;
-    AbridgedHrmpChannel: AbridgedHrmpChannel;
-    AccountData: AccountData;
-    AccountId: AccountId;
-    AccountId20: AccountId20;
-    AccountId32: AccountId32;
-    AccountId33: AccountId33;
-    AccountIdOf: AccountIdOf;
-    AccountIndex: AccountIndex;
-    AccountInfo: AccountInfo;
-    AccountInfoWithDualRefCount: AccountInfoWithDualRefCount;
-    AccountInfoWithProviders: AccountInfoWithProviders;
-    AccountInfoWithRefCount: AccountInfoWithRefCount;
-    AccountInfoWithRefCountU8: AccountInfoWithRefCountU8;
-    AccountInfoWithTripleRefCount: AccountInfoWithTripleRefCount;
-    AccountStatus: AccountStatus;
-    AccountValidity: AccountValidity;
-    AccountVote: AccountVote;
-    AccountVoteSplit: AccountVoteSplit;
-    AccountVoteStandard: AccountVoteStandard;
-    ActiveEraInfo: ActiveEraInfo;
-    ActiveGilt: ActiveGilt;
-    ActiveGiltsTotal: ActiveGiltsTotal;
-    ActiveIndex: ActiveIndex;
-    ActiveRecovery: ActiveRecovery;
-    Address: Address;
-    AliveContractInfo: AliveContractInfo;
-    AllowedSlots: AllowedSlots;
-    AnySignature: AnySignature;
-    ApiId: ApiId;
-    ApplyExtrinsicResult: ApplyExtrinsicResult;
-    ApplyExtrinsicResultPre6: ApplyExtrinsicResultPre6;
-    ApprovalFlag: ApprovalFlag;
-    Approvals: Approvals;
-    ArithmeticError: ArithmeticError;
-    AssetApproval: AssetApproval;
-    AssetApprovalKey: AssetApprovalKey;
-    AssetBalance: AssetBalance;
-    AssetDestroyWitness: AssetDestroyWitness;
-    AssetDetails: AssetDetails;
-    AssetId: AssetId;
-    AssetInstance: AssetInstance;
-    AssetInstanceV0: AssetInstanceV0;
-    AssetInstanceV1: AssetInstanceV1;
-    AssetInstanceV2: AssetInstanceV2;
-    AssetMetadata: AssetMetadata;
-    AssetOptions: AssetOptions;
-    AssignmentId: AssignmentId;
-    AssignmentKind: AssignmentKind;
-    AttestedCandidate: AttestedCandidate;
-    AuctionIndex: AuctionIndex;
-    AuthIndex: AuthIndex;
-    AuthorityDiscoveryId: AuthorityDiscoveryId;
-    AuthorityId: AuthorityId;
-    AuthorityIndex: AuthorityIndex;
-    AuthorityList: AuthorityList;
-    AuthoritySet: AuthoritySet;
-    AuthoritySetChange: AuthoritySetChange;
-    AuthoritySetChanges: AuthoritySetChanges;
-    AuthoritySignature: AuthoritySignature;
-    AuthorityWeight: AuthorityWeight;
-    AvailabilityBitfield: AvailabilityBitfield;
-    AvailabilityBitfieldRecord: AvailabilityBitfieldRecord;
-    BabeAuthorityWeight: BabeAuthorityWeight;
-    BabeBlockWeight: BabeBlockWeight;
-    BabeEpochConfiguration: BabeEpochConfiguration;
-    BabeEquivocationProof: BabeEquivocationProof;
-    BabeGenesisConfiguration: BabeGenesisConfiguration;
-    BabeGenesisConfigurationV1: BabeGenesisConfigurationV1;
-    BabeWeight: BabeWeight;
-    BackedCandidate: BackedCandidate;
-    Balance: Balance;
-    BalanceLock: BalanceLock;
-    BalanceLockTo212: BalanceLockTo212;
-    BalanceOf: BalanceOf;
-    BalanceStatus: BalanceStatus;
-    BeefyAuthoritySet: BeefyAuthoritySet;
-    BeefyCommitment: BeefyCommitment;
-    BeefyEquivocationProof: BeefyEquivocationProof;
-    BeefyId: BeefyId;
-    BeefyKey: BeefyKey;
-    BeefyNextAuthoritySet: BeefyNextAuthoritySet;
-    BeefyPayload: BeefyPayload;
-    BeefyPayloadId: BeefyPayloadId;
-    BeefySignedCommitment: BeefySignedCommitment;
-    BeefyVoteMessage: BeefyVoteMessage;
-    BenchmarkBatch: BenchmarkBatch;
-    BenchmarkConfig: BenchmarkConfig;
-    BenchmarkList: BenchmarkList;
-    BenchmarkMetadata: BenchmarkMetadata;
-    BenchmarkParameter: BenchmarkParameter;
-    BenchmarkResult: BenchmarkResult;
-    Bid: Bid;
-    Bidder: Bidder;
-    BidKind: BidKind;
-    BitVec: BitVec;
-    Block: Block;
-    BlockAttestations: BlockAttestations;
-    BlockHash: BlockHash;
-    BlockLength: BlockLength;
-    BlockNumber: BlockNumber;
-    BlockNumberFor: BlockNumberFor;
-    BlockNumberOf: BlockNumberOf;
-    BlockStats: BlockStats;
-    BlockTrace: BlockTrace;
-    BlockTraceEvent: BlockTraceEvent;
-    BlockTraceEventData: BlockTraceEventData;
-    BlockTraceSpan: BlockTraceSpan;
-    BlockV0: BlockV0;
-    BlockV1: BlockV1;
-    BlockV2: BlockV2;
-    BlockWeights: BlockWeights;
-    BodyId: BodyId;
-    BodyPart: BodyPart;
-    bool: bool;
-    Bool: Bool;
-    Bounty: Bounty;
-    BountyIndex: BountyIndex;
-    BountyStatus: BountyStatus;
-    BountyStatusActive: BountyStatusActive;
-    BountyStatusCuratorProposed: BountyStatusCuratorProposed;
-    BountyStatusPendingPayout: BountyStatusPendingPayout;
-    BridgedBlockHash: BridgedBlockHash;
-    BridgedBlockNumber: BridgedBlockNumber;
-    BridgedHeader: BridgedHeader;
-    BridgeMessageId: BridgeMessageId;
-    BufferedSessionChange: BufferedSessionChange;
-    Bytes: Bytes;
-    Call: Call;
-    CallHash: CallHash;
-    CallHashOf: CallHashOf;
-    CallIndex: CallIndex;
-    CallOrigin: CallOrigin;
-    CandidateCommitments: CandidateCommitments;
-    CandidateDescriptor: CandidateDescriptor;
-    CandidateEvent: CandidateEvent;
-    CandidateHash: CandidateHash;
-    CandidateInfo: CandidateInfo;
-    CandidatePendingAvailability: CandidatePendingAvailability;
-    CandidateReceipt: CandidateReceipt;
-    ChainId: ChainId;
-    ChainProperties: ChainProperties;
-    ChainType: ChainType;
-    ChangesTrieConfiguration: ChangesTrieConfiguration;
-    ChangesTrieSignal: ChangesTrieSignal;
-    CheckInherentsResult: CheckInherentsResult;
-    ClassDetails: ClassDetails;
-    ClassId: ClassId;
-    ClassMetadata: ClassMetadata;
-    CodecHash: CodecHash;
-    CodeHash: CodeHash;
-    CodeSource: CodeSource;
-    CodeUploadRequest: CodeUploadRequest;
-    CodeUploadResult: CodeUploadResult;
-    CodeUploadResultValue: CodeUploadResultValue;
-    CollationInfo: CollationInfo;
-    CollationInfoV1: CollationInfoV1;
-    CollatorId: CollatorId;
-    CollatorSignature: CollatorSignature;
-    CollectiveOrigin: CollectiveOrigin;
-    CommittedCandidateReceipt: CommittedCandidateReceipt;
-    CompactAssignments: CompactAssignments;
-    CompactAssignmentsTo257: CompactAssignmentsTo257;
-    CompactAssignmentsTo265: CompactAssignmentsTo265;
-    CompactAssignmentsWith16: CompactAssignmentsWith16;
-    CompactAssignmentsWith24: CompactAssignmentsWith24;
-    CompactScore: CompactScore;
-    CompactScoreCompact: CompactScoreCompact;
-    ConfigData: ConfigData;
-    Consensus: Consensus;
-    ConsensusEngineId: ConsensusEngineId;
-    ConsumedWeight: ConsumedWeight;
-    ContractCallFlags: ContractCallFlags;
-    ContractCallRequest: ContractCallRequest;
-    ContractConstructorSpecLatest: ContractConstructorSpecLatest;
-    ContractConstructorSpecV0: ContractConstructorSpecV0;
-    ContractConstructorSpecV1: ContractConstructorSpecV1;
-    ContractConstructorSpecV2: ContractConstructorSpecV2;
-    ContractConstructorSpecV3: ContractConstructorSpecV3;
-    ContractConstructorSpecV4: ContractConstructorSpecV4;
-    ContractContractSpecV0: ContractContractSpecV0;
-    ContractContractSpecV1: ContractContractSpecV1;
-    ContractContractSpecV2: ContractContractSpecV2;
-    ContractContractSpecV3: ContractContractSpecV3;
-    ContractContractSpecV4: ContractContractSpecV4;
-    ContractCryptoHasher: ContractCryptoHasher;
-    ContractDiscriminant: ContractDiscriminant;
-    ContractDisplayName: ContractDisplayName;
-    ContractEnvironmentV4: ContractEnvironmentV4;
-    ContractEventParamSpecLatest: ContractEventParamSpecLatest;
-    ContractEventParamSpecV0: ContractEventParamSpecV0;
-    ContractEventParamSpecV2: ContractEventParamSpecV2;
-    ContractEventSpecLatest: ContractEventSpecLatest;
-    ContractEventSpecV0: ContractEventSpecV0;
-    ContractEventSpecV1: ContractEventSpecV1;
-    ContractEventSpecV2: ContractEventSpecV2;
-    ContractExecResult: ContractExecResult;
-    ContractExecResultOk: ContractExecResultOk;
-    ContractExecResultResult: ContractExecResultResult;
-    ContractExecResultSuccessTo255: ContractExecResultSuccessTo255;
-    ContractExecResultSuccessTo260: ContractExecResultSuccessTo260;
-    ContractExecResultTo255: ContractExecResultTo255;
-    ContractExecResultTo260: ContractExecResultTo260;
-    ContractExecResultTo267: ContractExecResultTo267;
-    ContractExecResultU64: ContractExecResultU64;
-    ContractInfo: ContractInfo;
-    ContractInstantiateResult: ContractInstantiateResult;
-    ContractInstantiateResultTo267: ContractInstantiateResultTo267;
-    ContractInstantiateResultTo299: ContractInstantiateResultTo299;
-    ContractInstantiateResultU64: ContractInstantiateResultU64;
-    ContractLayoutArray: ContractLayoutArray;
-    ContractLayoutCell: ContractLayoutCell;
-    ContractLayoutEnum: ContractLayoutEnum;
-    ContractLayoutHash: ContractLayoutHash;
-    ContractLayoutHashingStrategy: ContractLayoutHashingStrategy;
-    ContractLayoutKey: ContractLayoutKey;
-    ContractLayoutStruct: ContractLayoutStruct;
-    ContractLayoutStructField: ContractLayoutStructField;
-    ContractMessageParamSpecLatest: ContractMessageParamSpecLatest;
-    ContractMessageParamSpecV0: ContractMessageParamSpecV0;
-    ContractMessageParamSpecV2: ContractMessageParamSpecV2;
-    ContractMessageSpecLatest: ContractMessageSpecLatest;
-    ContractMessageSpecV0: ContractMessageSpecV0;
-    ContractMessageSpecV1: ContractMessageSpecV1;
-    ContractMessageSpecV2: ContractMessageSpecV2;
-    ContractMessageSpecV3: ContractMessageSpecV3;
-    ContractMetadata: ContractMetadata;
-    ContractMetadataLatest: ContractMetadataLatest;
-    ContractMetadataV0: ContractMetadataV0;
-    ContractMetadataV1: ContractMetadataV1;
-    ContractMetadataV2: ContractMetadataV2;
-    ContractMetadataV3: ContractMetadataV3;
-    ContractMetadataV4: ContractMetadataV4;
-    ContractProject: ContractProject;
-    ContractProjectContract: ContractProjectContract;
-    ContractProjectInfo: ContractProjectInfo;
-    ContractProjectSource: ContractProjectSource;
-    ContractProjectV0: ContractProjectV0;
-    ContractReturnFlags: ContractReturnFlags;
-    ContractSelector: ContractSelector;
-    ContractStorageKey: ContractStorageKey;
-    ContractStorageLayout: ContractStorageLayout;
-    ContractTypeSpec: ContractTypeSpec;
-    Conviction: Conviction;
-    CoreAssignment: CoreAssignment;
-    CoreIndex: CoreIndex;
-    CoreOccupied: CoreOccupied;
-    CoreState: CoreState;
-    CrateVersion: CrateVersion;
-    CreatedBlock: CreatedBlock;
-    CustomMetadata15: CustomMetadata15;
-    CustomValueMetadata15: CustomValueMetadata15;
-    Data: Data;
-    DeferredOffenceOf: DeferredOffenceOf;
-    DefunctVoter: DefunctVoter;
-    DelayKind: DelayKind;
-    DelayKindBest: DelayKindBest;
-    Delegations: Delegations;
-    DeletedContract: DeletedContract;
-    DeliveredMessages: DeliveredMessages;
-    DepositBalance: DepositBalance;
-    DepositBalanceOf: DepositBalanceOf;
-    DestroyWitness: DestroyWitness;
-    Digest: Digest;
-    DigestItem: DigestItem;
-    DigestOf: DigestOf;
-    DispatchClass: DispatchClass;
-    DispatchError: DispatchError;
-    DispatchErrorModule: DispatchErrorModule;
-    DispatchErrorModulePre6: DispatchErrorModulePre6;
-    DispatchErrorModuleU8: DispatchErrorModuleU8;
-    DispatchErrorModuleU8a: DispatchErrorModuleU8a;
-    DispatchErrorPre6: DispatchErrorPre6;
-    DispatchErrorPre6First: DispatchErrorPre6First;
-    DispatchErrorTo198: DispatchErrorTo198;
-    DispatchFeePayment: DispatchFeePayment;
-    DispatchInfo: DispatchInfo;
-    DispatchInfoTo190: DispatchInfoTo190;
-    DispatchInfoTo244: DispatchInfoTo244;
-    DispatchOutcome: DispatchOutcome;
-    DispatchOutcomePre6: DispatchOutcomePre6;
-    DispatchResult: DispatchResult;
-    DispatchResultOf: DispatchResultOf;
-    DispatchResultTo198: DispatchResultTo198;
-    DisputeLocation: DisputeLocation;
-    DisputeProof: DisputeProof;
-    DisputeResult: DisputeResult;
-    DisputeState: DisputeState;
-    DisputeStatement: DisputeStatement;
-    DisputeStatementSet: DisputeStatementSet;
-    DisputesTimeSlot: DisputesTimeSlot;
-    DoubleEncodedCall: DoubleEncodedCall;
-    DoubleVoteReport: DoubleVoteReport;
-    DownwardMessage: DownwardMessage;
-    EcdsaSignature: EcdsaSignature;
-    Ed25519Signature: Ed25519Signature;
-    EIP1559Transaction: EIP1559Transaction;
-    EIP2930Transaction: EIP2930Transaction;
-    ElectionCompute: ElectionCompute;
-    ElectionPhase: ElectionPhase;
-    ElectionResult: ElectionResult;
-    ElectionScore: ElectionScore;
-    ElectionSize: ElectionSize;
-    ElectionStatus: ElectionStatus;
-    EncodedFinalityProofs: EncodedFinalityProofs;
-    EncodedJustification: EncodedJustification;
-    Epoch: Epoch;
-    EpochAuthorship: EpochAuthorship;
-    Era: Era;
-    EraIndex: EraIndex;
-    EraPoints: EraPoints;
-    EraRewardPoints: EraRewardPoints;
-    EraRewards: EraRewards;
-    ErrorMetadataLatest: ErrorMetadataLatest;
-    ErrorMetadataV10: ErrorMetadataV10;
-    ErrorMetadataV11: ErrorMetadataV11;
-    ErrorMetadataV12: ErrorMetadataV12;
-    ErrorMetadataV13: ErrorMetadataV13;
-    ErrorMetadataV14: ErrorMetadataV14;
-    ErrorMetadataV9: ErrorMetadataV9;
-    EthAccessList: EthAccessList;
-    EthAccessListItem: EthAccessListItem;
-    EthAccount: EthAccount;
-    EthAddress: EthAddress;
-    EthBlock: EthBlock;
-    EthBloom: EthBloom;
-    EthCallRequest: EthCallRequest;
-    EthereumAccountId: EthereumAccountId;
-    EthereumAddress: EthereumAddress;
-    EthereumLookupSource: EthereumLookupSource;
-    EthereumSignature: EthereumSignature;
-    EthFeeHistory: EthFeeHistory;
-    EthFilter: EthFilter;
-    EthFilterAddress: EthFilterAddress;
-    EthFilterChanges: EthFilterChanges;
-    EthFilterTopic: EthFilterTopic;
-    EthFilterTopicEntry: EthFilterTopicEntry;
-    EthFilterTopicInner: EthFilterTopicInner;
-    EthHeader: EthHeader;
-    EthLog: EthLog;
-    EthReceipt: EthReceipt;
-    EthReceiptV0: EthReceiptV0;
-    EthReceiptV3: EthReceiptV3;
-    EthRichBlock: EthRichBlock;
-    EthRichHeader: EthRichHeader;
-    EthStorageProof: EthStorageProof;
-    EthSubKind: EthSubKind;
-    EthSubParams: EthSubParams;
-    EthSubResult: EthSubResult;
-    EthSyncInfo: EthSyncInfo;
-    EthSyncStatus: EthSyncStatus;
-    EthTransaction: EthTransaction;
-    EthTransactionAction: EthTransactionAction;
-    EthTransactionCondition: EthTransactionCondition;
-    EthTransactionRequest: EthTransactionRequest;
-    EthTransactionSignature: EthTransactionSignature;
-    EthTransactionStatus: EthTransactionStatus;
-    EthWork: EthWork;
-    Event: Event;
-    EventId: EventId;
-    EventIndex: EventIndex;
-    EventMetadataLatest: EventMetadataLatest;
-    EventMetadataV10: EventMetadataV10;
-    EventMetadataV11: EventMetadataV11;
-    EventMetadataV12: EventMetadataV12;
-    EventMetadataV13: EventMetadataV13;
-    EventMetadataV14: EventMetadataV14;
-    EventMetadataV9: EventMetadataV9;
-    EventRecord: EventRecord;
-    EvmAccount: EvmAccount;
-    EvmCallInfo: EvmCallInfo;
-    EvmCallInfoV2: EvmCallInfoV2;
-    EvmCreateInfo: EvmCreateInfo;
-    EvmCreateInfoV2: EvmCreateInfoV2;
-    EvmLog: EvmLog;
-    EvmVicinity: EvmVicinity;
-    EvmWeightInfo: EvmWeightInfo;
-    ExecReturnValue: ExecReturnValue;
-    ExecutorParam: ExecutorParam;
-    ExecutorParams: ExecutorParams;
-    ExecutorParamsHash: ExecutorParamsHash;
-    ExitError: ExitError;
-    ExitFatal: ExitFatal;
-    ExitReason: ExitReason;
-    ExitRevert: ExitRevert;
-    ExitSucceed: ExitSucceed;
-    ExplicitDisputeStatement: ExplicitDisputeStatement;
-    Exposure: Exposure;
-    ExtendedBalance: ExtendedBalance;
-    Extrinsic: Extrinsic;
-    ExtrinsicEra: ExtrinsicEra;
-    ExtrinsicMetadataLatest: ExtrinsicMetadataLatest;
-    ExtrinsicMetadataV11: ExtrinsicMetadataV11;
-    ExtrinsicMetadataV12: ExtrinsicMetadataV12;
-    ExtrinsicMetadataV13: ExtrinsicMetadataV13;
-    ExtrinsicMetadataV14: ExtrinsicMetadataV14;
-    ExtrinsicMetadataV15: ExtrinsicMetadataV15;
-    ExtrinsicOrHash: ExtrinsicOrHash;
-    ExtrinsicPayload: ExtrinsicPayload;
-    ExtrinsicPayloadUnknown: ExtrinsicPayloadUnknown;
-    ExtrinsicPayloadV4: ExtrinsicPayloadV4;
-    ExtrinsicSignature: ExtrinsicSignature;
-    ExtrinsicSignatureV4: ExtrinsicSignatureV4;
-    ExtrinsicStatus: ExtrinsicStatus;
-    ExtrinsicsWeight: ExtrinsicsWeight;
-    ExtrinsicUnknown: ExtrinsicUnknown;
-    ExtrinsicV4: ExtrinsicV4;
-    f32: f32;
-    F32: F32;
-    f64: f64;
-    F64: F64;
-    FeeDetails: FeeDetails;
-    Fixed128: Fixed128;
-    Fixed64: Fixed64;
-    FixedI128: FixedI128;
-    FixedI64: FixedI64;
-    FixedU128: FixedU128;
-    FixedU64: FixedU64;
-    Forcing: Forcing;
-    ForkTreePendingChange: ForkTreePendingChange;
-    ForkTreePendingChangeNode: ForkTreePendingChangeNode;
-    FullIdentification: FullIdentification;
-    FunctionArgumentMetadataLatest: FunctionArgumentMetadataLatest;
-    FunctionArgumentMetadataV10: FunctionArgumentMetadataV10;
-    FunctionArgumentMetadataV11: FunctionArgumentMetadataV11;
-    FunctionArgumentMetadataV12: FunctionArgumentMetadataV12;
-    FunctionArgumentMetadataV13: FunctionArgumentMetadataV13;
-    FunctionArgumentMetadataV14: FunctionArgumentMetadataV14;
-    FunctionArgumentMetadataV9: FunctionArgumentMetadataV9;
-    FunctionMetadataLatest: FunctionMetadataLatest;
-    FunctionMetadataV10: FunctionMetadataV10;
-    FunctionMetadataV11: FunctionMetadataV11;
-    FunctionMetadataV12: FunctionMetadataV12;
-    FunctionMetadataV13: FunctionMetadataV13;
-    FunctionMetadataV14: FunctionMetadataV14;
-    FunctionMetadataV9: FunctionMetadataV9;
-    FundIndex: FundIndex;
-    FundInfo: FundInfo;
-    Fungibility: Fungibility;
-    FungibilityV0: FungibilityV0;
-    FungibilityV1: FungibilityV1;
-    FungibilityV2: FungibilityV2;
-    FungiblesAccessError: FungiblesAccessError;
-    Gas: Gas;
-    GiltBid: GiltBid;
-    GlobalValidationData: GlobalValidationData;
-    GlobalValidationSchedule: GlobalValidationSchedule;
-    GrandpaCommit: GrandpaCommit;
-    GrandpaEquivocation: GrandpaEquivocation;
-    GrandpaEquivocationProof: GrandpaEquivocationProof;
-    GrandpaEquivocationValue: GrandpaEquivocationValue;
-    GrandpaJustification: GrandpaJustification;
-    GrandpaPrecommit: GrandpaPrecommit;
-    GrandpaPrevote: GrandpaPrevote;
-    GrandpaSignedPrecommit: GrandpaSignedPrecommit;
-    GroupIndex: GroupIndex;
-    GroupRotationInfo: GroupRotationInfo;
-    H1024: H1024;
-    H128: H128;
-    H160: H160;
-    H2048: H2048;
-    H256: H256;
-    H32: H32;
-    H512: H512;
-    H64: H64;
-    Hash: Hash;
-    HeadData: HeadData;
-    Header: Header;
-    HeaderPartial: HeaderPartial;
-    Health: Health;
-    Heartbeat: Heartbeat;
-    HeartbeatTo244: HeartbeatTo244;
-    HostConfiguration: HostConfiguration;
-    HostFnWeights: HostFnWeights;
-    HostFnWeightsTo264: HostFnWeightsTo264;
-    HrmpChannel: HrmpChannel;
-    HrmpChannelId: HrmpChannelId;
-    HrmpOpenChannelRequest: HrmpOpenChannelRequest;
-    i128: i128;
-    I128: I128;
-    i16: i16;
-    I16: I16;
-    i256: i256;
-    I256: I256;
-    i32: i32;
-    I32: I32;
-    I32F32: I32F32;
-    i64: i64;
-    I64: I64;
-    i8: i8;
-    I8: I8;
-    IdentificationTuple: IdentificationTuple;
-    IdentityFields: IdentityFields;
-    IdentityInfo: IdentityInfo;
-    IdentityInfoAdditional: IdentityInfoAdditional;
-    IdentityInfoTo198: IdentityInfoTo198;
-    IdentityJudgement: IdentityJudgement;
-    ImmortalEra: ImmortalEra;
-    ImportedAux: ImportedAux;
-    InboundDownwardMessage: InboundDownwardMessage;
-    InboundHrmpMessage: InboundHrmpMessage;
-    InboundHrmpMessages: InboundHrmpMessages;
-    InboundLaneData: InboundLaneData;
-    InboundRelayer: InboundRelayer;
-    InboundStatus: InboundStatus;
-    IncludedBlocks: IncludedBlocks;
-    InclusionFee: InclusionFee;
-    IncomingParachain: IncomingParachain;
-    IncomingParachainDeploy: IncomingParachainDeploy;
-    IncomingParachainFixed: IncomingParachainFixed;
-    Index: Index;
-    IndicesLookupSource: IndicesLookupSource;
-    IndividualExposure: IndividualExposure;
-    InherentData: InherentData;
-    InherentIdentifier: InherentIdentifier;
-    InitializationData: InitializationData;
-    InstanceDetails: InstanceDetails;
-    InstanceId: InstanceId;
-    InstanceMetadata: InstanceMetadata;
-    InstantiateRequest: InstantiateRequest;
-    InstantiateRequestV1: InstantiateRequestV1;
-    InstantiateRequestV2: InstantiateRequestV2;
-    InstantiateReturnValue: InstantiateReturnValue;
-    InstantiateReturnValueOk: InstantiateReturnValueOk;
-    InstantiateReturnValueTo267: InstantiateReturnValueTo267;
-    InstructionV2: InstructionV2;
-    InstructionWeights: InstructionWeights;
-    InteriorMultiLocation: InteriorMultiLocation;
-    InvalidDisputeStatementKind: InvalidDisputeStatementKind;
-    InvalidTransaction: InvalidTransaction;
-    isize: isize;
-    ISize: ISize;
-    Json: Json;
-    Junction: Junction;
-    Junctions: Junctions;
-    JunctionsV1: JunctionsV1;
-    JunctionsV2: JunctionsV2;
-    JunctionV0: JunctionV0;
-    JunctionV1: JunctionV1;
-    JunctionV2: JunctionV2;
-    Justification: Justification;
-    JustificationNotification: JustificationNotification;
-    Justifications: Justifications;
-    Key: Key;
-    KeyOwnerProof: KeyOwnerProof;
-    Keys: Keys;
-    KeyType: KeyType;
-    KeyTypeId: KeyTypeId;
-    KeyValue: KeyValue;
-    KeyValueOption: KeyValueOption;
-    Kind: Kind;
-    LaneId: LaneId;
-    LastContribution: LastContribution;
-    LastRuntimeUpgradeInfo: LastRuntimeUpgradeInfo;
-    LeasePeriod: LeasePeriod;
-    LeasePeriodOf: LeasePeriodOf;
-    LegacyTransaction: LegacyTransaction;
-    Limits: Limits;
-    LimitsTo264: LimitsTo264;
-    LocalValidationData: LocalValidationData;
-    LockIdentifier: LockIdentifier;
-    LookupSource: LookupSource;
-    LookupTarget: LookupTarget;
-    LotteryConfig: LotteryConfig;
-    MaybeRandomness: MaybeRandomness;
-    MaybeVrf: MaybeVrf;
-    MemberCount: MemberCount;
-    MembershipProof: MembershipProof;
-    MessageData: MessageData;
-    MessageId: MessageId;
-    MessageIngestionType: MessageIngestionType;
-    MessageKey: MessageKey;
-    MessageNonce: MessageNonce;
-    MessageQueueChain: MessageQueueChain;
-    MessagesDeliveryProofOf: MessagesDeliveryProofOf;
-    MessagesProofOf: MessagesProofOf;
-    MessagingStateSnapshot: MessagingStateSnapshot;
-    MessagingStateSnapshotEgressEntry: MessagingStateSnapshotEgressEntry;
-    MetadataAll: MetadataAll;
-    MetadataLatest: MetadataLatest;
-    MetadataV10: MetadataV10;
-    MetadataV11: MetadataV11;
-    MetadataV12: MetadataV12;
-    MetadataV13: MetadataV13;
-    MetadataV14: MetadataV14;
-    MetadataV15: MetadataV15;
-    MetadataV9: MetadataV9;
-    MigrationStatusResult: MigrationStatusResult;
-    MmrBatchProof: MmrBatchProof;
-    MmrEncodableOpaqueLeaf: MmrEncodableOpaqueLeaf;
-    MmrError: MmrError;
-    MmrHash: MmrHash;
-    MmrLeafBatchProof: MmrLeafBatchProof;
-    MmrLeafIndex: MmrLeafIndex;
-    MmrLeafProof: MmrLeafProof;
-    MmrNodeIndex: MmrNodeIndex;
-    MmrProof: MmrProof;
-    MmrRootHash: MmrRootHash;
-    ModuleConstantMetadataV10: ModuleConstantMetadataV10;
-    ModuleConstantMetadataV11: ModuleConstantMetadataV11;
-    ModuleConstantMetadataV12: ModuleConstantMetadataV12;
-    ModuleConstantMetadataV13: ModuleConstantMetadataV13;
-    ModuleConstantMetadataV9: ModuleConstantMetadataV9;
-    ModuleId: ModuleId;
-    ModuleMetadataV10: ModuleMetadataV10;
-    ModuleMetadataV11: ModuleMetadataV11;
-    ModuleMetadataV12: ModuleMetadataV12;
-    ModuleMetadataV13: ModuleMetadataV13;
-    ModuleMetadataV9: ModuleMetadataV9;
-    Moment: Moment;
-    MomentOf: MomentOf;
-    MoreAttestations: MoreAttestations;
-    MortalEra: MortalEra;
-    MultiAddress: MultiAddress;
-    MultiAsset: MultiAsset;
-    MultiAssetFilter: MultiAssetFilter;
-    MultiAssetFilterV1: MultiAssetFilterV1;
-    MultiAssetFilterV2: MultiAssetFilterV2;
-    MultiAssets: MultiAssets;
-    MultiAssetsV1: MultiAssetsV1;
-    MultiAssetsV2: MultiAssetsV2;
-    MultiAssetV0: MultiAssetV0;
-    MultiAssetV1: MultiAssetV1;
-    MultiAssetV2: MultiAssetV2;
-    MultiDisputeStatementSet: MultiDisputeStatementSet;
-    MultiLocation: MultiLocation;
-    MultiLocationV0: MultiLocationV0;
-    MultiLocationV1: MultiLocationV1;
-    MultiLocationV2: MultiLocationV2;
-    Multiplier: Multiplier;
-    Multisig: Multisig;
-    MultiSignature: MultiSignature;
-    MultiSigner: MultiSigner;
-    NetworkId: NetworkId;
-    NetworkState: NetworkState;
-    NetworkStatePeerset: NetworkStatePeerset;
-    NetworkStatePeersetInfo: NetworkStatePeersetInfo;
-    NewBidder: NewBidder;
-    NextAuthority: NextAuthority;
-    NextConfigDescriptor: NextConfigDescriptor;
-    NextConfigDescriptorV1: NextConfigDescriptorV1;
-    NftCollectionId: NftCollectionId;
-    NftItemId: NftItemId;
-    NodeRole: NodeRole;
-    Nominations: Nominations;
-    NominatorIndex: NominatorIndex;
-    NominatorIndexCompact: NominatorIndexCompact;
-    NotConnectedPeer: NotConnectedPeer;
-    NpApiError: NpApiError;
-    NpPoolId: NpPoolId;
-    Null: Null;
-    OccupiedCore: OccupiedCore;
-    OccupiedCoreAssumption: OccupiedCoreAssumption;
-    OffchainAccuracy: OffchainAccuracy;
-    OffchainAccuracyCompact: OffchainAccuracyCompact;
-    OffenceDetails: OffenceDetails;
-    Offender: Offender;
-    OldV1SessionInfo: OldV1SessionInfo;
-    OpaqueCall: OpaqueCall;
-    OpaqueKeyOwnershipProof: OpaqueKeyOwnershipProof;
-    OpaqueMetadata: OpaqueMetadata;
-    OpaqueMultiaddr: OpaqueMultiaddr;
-    OpaqueNetworkState: OpaqueNetworkState;
-    OpaquePeerId: OpaquePeerId;
-    OpaqueTimeSlot: OpaqueTimeSlot;
-    OpenTip: OpenTip;
-    OpenTipFinderTo225: OpenTipFinderTo225;
-    OpenTipTip: OpenTipTip;
-    OpenTipTo225: OpenTipTo225;
-    OperatingMode: OperatingMode;
-    OptionBool: OptionBool;
-    Origin: Origin;
-    OriginCaller: OriginCaller;
-    OriginKindV0: OriginKindV0;
-    OriginKindV1: OriginKindV1;
-    OriginKindV2: OriginKindV2;
-    OutboundHrmpMessage: OutboundHrmpMessage;
-    OutboundLaneData: OutboundLaneData;
-    OutboundMessageFee: OutboundMessageFee;
-    OutboundPayload: OutboundPayload;
-    OutboundStatus: OutboundStatus;
-    Outcome: Outcome;
-    OuterEnums15: OuterEnums15;
-    OverweightIndex: OverweightIndex;
-    Owner: Owner;
-    PageCounter: PageCounter;
-    PageIndexData: PageIndexData;
-    PalletCallMetadataLatest: PalletCallMetadataLatest;
-    PalletCallMetadataV14: PalletCallMetadataV14;
-    PalletConstantMetadataLatest: PalletConstantMetadataLatest;
-    PalletConstantMetadataV14: PalletConstantMetadataV14;
-    PalletErrorMetadataLatest: PalletErrorMetadataLatest;
-    PalletErrorMetadataV14: PalletErrorMetadataV14;
-    PalletEventMetadataLatest: PalletEventMetadataLatest;
-    PalletEventMetadataV14: PalletEventMetadataV14;
-    PalletId: PalletId;
-    PalletMetadataLatest: PalletMetadataLatest;
-    PalletMetadataV14: PalletMetadataV14;
-    PalletMetadataV15: PalletMetadataV15;
-    PalletsOrigin: PalletsOrigin;
-    PalletStorageMetadataLatest: PalletStorageMetadataLatest;
-    PalletStorageMetadataV14: PalletStorageMetadataV14;
-    PalletVersion: PalletVersion;
-    ParachainDispatchOrigin: ParachainDispatchOrigin;
-    ParachainInherentData: ParachainInherentData;
-    ParachainProposal: ParachainProposal;
-    ParachainsInherentData: ParachainsInherentData;
-    ParaGenesisArgs: ParaGenesisArgs;
-    ParaId: ParaId;
-    ParaInfo: ParaInfo;
-    ParaLifecycle: ParaLifecycle;
-    Parameter: Parameter;
-    ParaPastCodeMeta: ParaPastCodeMeta;
-    ParaScheduling: ParaScheduling;
-    ParathreadClaim: ParathreadClaim;
-    ParathreadClaimQueue: ParathreadClaimQueue;
-    ParathreadEntry: ParathreadEntry;
-    ParaValidatorIndex: ParaValidatorIndex;
-    Pays: Pays;
-    Peer: Peer;
-    PeerEndpoint: PeerEndpoint;
-    PeerEndpointAddr: PeerEndpointAddr;
-    PeerInfo: PeerInfo;
-    PeerPing: PeerPing;
-    PendingChange: PendingChange;
-    PendingPause: PendingPause;
-    PendingResume: PendingResume;
-    PendingSlashes: PendingSlashes;
-    Perbill: Perbill;
-    Percent: Percent;
-    PerDispatchClassU32: PerDispatchClassU32;
-    PerDispatchClassWeight: PerDispatchClassWeight;
-    PerDispatchClassWeightsPerClass: PerDispatchClassWeightsPerClass;
-    Period: Period;
-    Permill: Permill;
-    PermissionLatest: PermissionLatest;
-    PermissionsV1: PermissionsV1;
-    PermissionVersions: PermissionVersions;
-    Perquintill: Perquintill;
-    PersistedValidationData: PersistedValidationData;
-    PerU16: PerU16;
-    Phantom: Phantom;
-    PhantomData: PhantomData;
-    Phase: Phase;
-    PhragmenScore: PhragmenScore;
-    Points: Points;
-    PortableType: PortableType;
-    PortableTypeV14: PortableTypeV14;
-    Precommits: Precommits;
-    PrefabWasmModule: PrefabWasmModule;
-    PrefixedStorageKey: PrefixedStorageKey;
-    PreimageStatus: PreimageStatus;
-    PreimageStatusAvailable: PreimageStatusAvailable;
-    PreRuntime: PreRuntime;
-    Prevotes: Prevotes;
-    Priority: Priority;
-    PriorLock: PriorLock;
-    PropIndex: PropIndex;
-    Proposal: Proposal;
-    ProposalIndex: ProposalIndex;
-    ProxyAnnouncement: ProxyAnnouncement;
-    ProxyDefinition: ProxyDefinition;
-    ProxyState: ProxyState;
-    ProxyType: ProxyType;
-    PvfCheckStatement: PvfCheckStatement;
-    PvfExecTimeoutKind: PvfExecTimeoutKind;
-    PvfPrepTimeoutKind: PvfPrepTimeoutKind;
-    QueryId: QueryId;
-    QueryStatus: QueryStatus;
-    QueueConfigData: QueueConfigData;
-    QueuedParathread: QueuedParathread;
-    Randomness: Randomness;
-    Raw: Raw;
-    RawAuraPreDigest: RawAuraPreDigest;
-    RawBabePreDigest: RawBabePreDigest;
-    RawBabePreDigestCompat: RawBabePreDigestCompat;
-    RawBabePreDigestPrimary: RawBabePreDigestPrimary;
-    RawBabePreDigestPrimaryTo159: RawBabePreDigestPrimaryTo159;
-    RawBabePreDigestSecondaryPlain: RawBabePreDigestSecondaryPlain;
-    RawBabePreDigestSecondaryTo159: RawBabePreDigestSecondaryTo159;
-    RawBabePreDigestSecondaryVRF: RawBabePreDigestSecondaryVRF;
-    RawBabePreDigestTo159: RawBabePreDigestTo159;
-    RawOrigin: RawOrigin;
-    RawSolution: RawSolution;
-    RawSolutionTo265: RawSolutionTo265;
-    RawSolutionWith16: RawSolutionWith16;
-    RawSolutionWith24: RawSolutionWith24;
-    RawVRFOutput: RawVRFOutput;
-    ReadProof: ReadProof;
-    ReadySolution: ReadySolution;
-    Reasons: Reasons;
-    RecoveryConfig: RecoveryConfig;
-    RefCount: RefCount;
-    RefCountTo259: RefCountTo259;
-    ReferendumIndex: ReferendumIndex;
-    ReferendumInfo: ReferendumInfo;
-    ReferendumInfoFinished: ReferendumInfoFinished;
-    ReferendumInfoTo239: ReferendumInfoTo239;
-    ReferendumStatus: ReferendumStatus;
-    RegisteredParachainInfo: RegisteredParachainInfo;
-    RegistrarIndex: RegistrarIndex;
-    RegistrarInfo: RegistrarInfo;
-    Registration: Registration;
-    RegistrationJudgement: RegistrationJudgement;
-    RegistrationTo198: RegistrationTo198;
-    RelayBlockNumber: RelayBlockNumber;
-    RelayChainBlockNumber: RelayChainBlockNumber;
-    RelayChainHash: RelayChainHash;
-    RelayerId: RelayerId;
-    RelayHash: RelayHash;
-    Releases: Releases;
-    Remark: Remark;
-    Renouncing: Renouncing;
-    RentProjection: RentProjection;
-    ReplacementTimes: ReplacementTimes;
-    ReportedRoundStates: ReportedRoundStates;
-    Reporter: Reporter;
-    ReportIdOf: ReportIdOf;
-    ReserveData: ReserveData;
-    ReserveIdentifier: ReserveIdentifier;
-    Response: Response;
-    ResponseV0: ResponseV0;
-    ResponseV1: ResponseV1;
-    ResponseV2: ResponseV2;
-    ResponseV2Error: ResponseV2Error;
-    ResponseV2Result: ResponseV2Result;
-    Retriable: Retriable;
-    RewardDestination: RewardDestination;
-    RewardPoint: RewardPoint;
-    RoundSnapshot: RoundSnapshot;
-    RoundState: RoundState;
-    RpcMethods: RpcMethods;
-    RuntimeApiMetadataLatest: RuntimeApiMetadataLatest;
-    RuntimeApiMetadataV15: RuntimeApiMetadataV15;
-    RuntimeApiMethodMetadataV15: RuntimeApiMethodMetadataV15;
-    RuntimeApiMethodParamMetadataV15: RuntimeApiMethodParamMetadataV15;
-    RuntimeCall: RuntimeCall;
-    RuntimeDbWeight: RuntimeDbWeight;
-    RuntimeDispatchInfo: RuntimeDispatchInfo;
-    RuntimeDispatchInfoV1: RuntimeDispatchInfoV1;
-    RuntimeDispatchInfoV2: RuntimeDispatchInfoV2;
-    RuntimeEvent: RuntimeEvent;
-    RuntimeVersion: RuntimeVersion;
-    RuntimeVersionApi: RuntimeVersionApi;
-    RuntimeVersionPartial: RuntimeVersionPartial;
-    RuntimeVersionPre3: RuntimeVersionPre3;
-    RuntimeVersionPre4: RuntimeVersionPre4;
-    Schedule: Schedule;
-    Scheduled: Scheduled;
-    ScheduledCore: ScheduledCore;
-    ScheduledTo254: ScheduledTo254;
-    SchedulePeriod: SchedulePeriod;
-    SchedulePriority: SchedulePriority;
-    ScheduleTo212: ScheduleTo212;
-    ScheduleTo258: ScheduleTo258;
-    ScheduleTo264: ScheduleTo264;
-    Scheduling: Scheduling;
-    ScrapedOnChainVotes: ScrapedOnChainVotes;
-    Seal: Seal;
-    SealV0: SealV0;
-    SeatHolder: SeatHolder;
-    SeedOf: SeedOf;
-    ServiceQuality: ServiceQuality;
-    SessionIndex: SessionIndex;
-    SessionInfo: SessionInfo;
-    SessionInfoValidatorGroup: SessionInfoValidatorGroup;
-    SessionKeys1: SessionKeys1;
-    SessionKeys10: SessionKeys10;
-    SessionKeys10B: SessionKeys10B;
-    SessionKeys2: SessionKeys2;
-    SessionKeys3: SessionKeys3;
-    SessionKeys4: SessionKeys4;
-    SessionKeys5: SessionKeys5;
-    SessionKeys6: SessionKeys6;
-    SessionKeys6B: SessionKeys6B;
-    SessionKeys7: SessionKeys7;
-    SessionKeys7B: SessionKeys7B;
-    SessionKeys8: SessionKeys8;
-    SessionKeys8B: SessionKeys8B;
-    SessionKeys9: SessionKeys9;
-    SessionKeys9B: SessionKeys9B;
-    SetId: SetId;
-    SetIndex: SetIndex;
-    Si0Field: Si0Field;
-    Si0LookupTypeId: Si0LookupTypeId;
-    Si0Path: Si0Path;
-    Si0Type: Si0Type;
-    Si0TypeDef: Si0TypeDef;
-    Si0TypeDefArray: Si0TypeDefArray;
-    Si0TypeDefBitSequence: Si0TypeDefBitSequence;
-    Si0TypeDefCompact: Si0TypeDefCompact;
-    Si0TypeDefComposite: Si0TypeDefComposite;
-    Si0TypeDefPhantom: Si0TypeDefPhantom;
-    Si0TypeDefPrimitive: Si0TypeDefPrimitive;
-    Si0TypeDefSequence: Si0TypeDefSequence;
-    Si0TypeDefTuple: Si0TypeDefTuple;
-    Si0TypeDefVariant: Si0TypeDefVariant;
-    Si0TypeParameter: Si0TypeParameter;
-    Si0Variant: Si0Variant;
-    Si1Field: Si1Field;
-    Si1LookupTypeId: Si1LookupTypeId;
-    Si1Path: Si1Path;
-    Si1Type: Si1Type;
-    Si1TypeDef: Si1TypeDef;
-    Si1TypeDefArray: Si1TypeDefArray;
-    Si1TypeDefBitSequence: Si1TypeDefBitSequence;
-    Si1TypeDefCompact: Si1TypeDefCompact;
-    Si1TypeDefComposite: Si1TypeDefComposite;
-    Si1TypeDefPrimitive: Si1TypeDefPrimitive;
-    Si1TypeDefSequence: Si1TypeDefSequence;
-    Si1TypeDefTuple: Si1TypeDefTuple;
-    Si1TypeDefVariant: Si1TypeDefVariant;
-    Si1TypeParameter: Si1TypeParameter;
-    Si1Variant: Si1Variant;
-    SiField: SiField;
-    Signature: Signature;
-    SignedAvailabilityBitfield: SignedAvailabilityBitfield;
-    SignedAvailabilityBitfields: SignedAvailabilityBitfields;
-    SignedBlock: SignedBlock;
-    SignedBlockWithJustification: SignedBlockWithJustification;
-    SignedBlockWithJustifications: SignedBlockWithJustifications;
-    SignedExtensionMetadataLatest: SignedExtensionMetadataLatest;
-    SignedExtensionMetadataV14: SignedExtensionMetadataV14;
-    SignedSubmission: SignedSubmission;
-    SignedSubmissionOf: SignedSubmissionOf;
-    SignedSubmissionTo276: SignedSubmissionTo276;
-    SignerPayload: SignerPayload;
-    SigningContext: SigningContext;
-    SiLookupTypeId: SiLookupTypeId;
-    SiPath: SiPath;
-    SiType: SiType;
-    SiTypeDef: SiTypeDef;
-    SiTypeDefArray: SiTypeDefArray;
-    SiTypeDefBitSequence: SiTypeDefBitSequence;
-    SiTypeDefCompact: SiTypeDefCompact;
-    SiTypeDefComposite: SiTypeDefComposite;
-    SiTypeDefPrimitive: SiTypeDefPrimitive;
-    SiTypeDefSequence: SiTypeDefSequence;
-    SiTypeDefTuple: SiTypeDefTuple;
-    SiTypeDefVariant: SiTypeDefVariant;
-    SiTypeParameter: SiTypeParameter;
-    SiVariant: SiVariant;
-    SlashingOffenceKind: SlashingOffenceKind;
-    SlashingSpans: SlashingSpans;
-    SlashingSpansTo204: SlashingSpansTo204;
-    SlashJournalEntry: SlashJournalEntry;
-    Slot: Slot;
-    SlotDuration: SlotDuration;
-    SlotNumber: SlotNumber;
-    SlotRange: SlotRange;
-    SlotRange10: SlotRange10;
-    SocietyJudgement: SocietyJudgement;
-    SocietyVote: SocietyVote;
-    SolutionOrSnapshotSize: SolutionOrSnapshotSize;
-    SolutionSupport: SolutionSupport;
-    SolutionSupports: SolutionSupports;
-    SpanIndex: SpanIndex;
-    SpanRecord: SpanRecord;
-    SpecVersion: SpecVersion;
-    Sr25519Signature: Sr25519Signature;
-    StakingLedger: StakingLedger;
-    StakingLedgerTo223: StakingLedgerTo223;
-    StakingLedgerTo240: StakingLedgerTo240;
-    Statement: Statement;
-    StatementKind: StatementKind;
-    StorageChangeSet: StorageChangeSet;
-    StorageData: StorageData;
-    StorageDeposit: StorageDeposit;
-    StorageEntryMetadataLatest: StorageEntryMetadataLatest;
-    StorageEntryMetadataV10: StorageEntryMetadataV10;
-    StorageEntryMetadataV11: StorageEntryMetadataV11;
-    StorageEntryMetadataV12: StorageEntryMetadataV12;
-    StorageEntryMetadataV13: StorageEntryMetadataV13;
-    StorageEntryMetadataV14: StorageEntryMetadataV14;
-    StorageEntryMetadataV9: StorageEntryMetadataV9;
-    StorageEntryModifierLatest: StorageEntryModifierLatest;
-    StorageEntryModifierV10: StorageEntryModifierV10;
-    StorageEntryModifierV11: StorageEntryModifierV11;
-    StorageEntryModifierV12: StorageEntryModifierV12;
-    StorageEntryModifierV13: StorageEntryModifierV13;
-    StorageEntryModifierV14: StorageEntryModifierV14;
-    StorageEntryModifierV9: StorageEntryModifierV9;
-    StorageEntryTypeLatest: StorageEntryTypeLatest;
-    StorageEntryTypeV10: StorageEntryTypeV10;
-    StorageEntryTypeV11: StorageEntryTypeV11;
-    StorageEntryTypeV12: StorageEntryTypeV12;
-    StorageEntryTypeV13: StorageEntryTypeV13;
-    StorageEntryTypeV14: StorageEntryTypeV14;
-    StorageEntryTypeV9: StorageEntryTypeV9;
-    StorageHasher: StorageHasher;
-    StorageHasherV10: StorageHasherV10;
-    StorageHasherV11: StorageHasherV11;
-    StorageHasherV12: StorageHasherV12;
-    StorageHasherV13: StorageHasherV13;
-    StorageHasherV14: StorageHasherV14;
-    StorageHasherV9: StorageHasherV9;
-    StorageInfo: StorageInfo;
-    StorageKey: StorageKey;
-    StorageKind: StorageKind;
-    StorageMetadataV10: StorageMetadataV10;
-    StorageMetadataV11: StorageMetadataV11;
-    StorageMetadataV12: StorageMetadataV12;
-    StorageMetadataV13: StorageMetadataV13;
-    StorageMetadataV9: StorageMetadataV9;
-    StorageProof: StorageProof;
-    StoredPendingChange: StoredPendingChange;
-    StoredState: StoredState;
-    StrikeCount: StrikeCount;
-    SubId: SubId;
-    SubmissionIndicesOf: SubmissionIndicesOf;
-    Supports: Supports;
-    SyncState: SyncState;
-    SystemInherentData: SystemInherentData;
-    SystemOrigin: SystemOrigin;
-    Tally: Tally;
-    TaskAddress: TaskAddress;
-    TAssetBalance: TAssetBalance;
-    TAssetDepositBalance: TAssetDepositBalance;
-    Text: Text;
-    Timepoint: Timepoint;
-    TokenError: TokenError;
-    TombstoneContractInfo: TombstoneContractInfo;
-    TraceBlockResponse: TraceBlockResponse;
-    TraceError: TraceError;
-    TransactionalError: TransactionalError;
-    TransactionInfo: TransactionInfo;
-    TransactionLongevity: TransactionLongevity;
-    TransactionPriority: TransactionPriority;
-    TransactionSource: TransactionSource;
-    TransactionStorageProof: TransactionStorageProof;
-    TransactionTag: TransactionTag;
-    TransactionV0: TransactionV0;
-    TransactionV1: TransactionV1;
-    TransactionV2: TransactionV2;
-    TransactionValidity: TransactionValidity;
-    TransactionValidityError: TransactionValidityError;
-    TransientValidationData: TransientValidationData;
-    TreasuryProposal: TreasuryProposal;
-    TrieId: TrieId;
-    TrieIndex: TrieIndex;
-    Type: Type;
-    u128: u128;
-    U128: U128;
-    u16: u16;
-    U16: U16;
-    u256: u256;
-    U256: U256;
-    u32: u32;
-    U32: U32;
-    U32F32: U32F32;
-    u64: u64;
-    U64: U64;
-    u8: u8;
-    U8: U8;
-    UnappliedSlash: UnappliedSlash;
-    UnappliedSlashOther: UnappliedSlashOther;
-    UncleEntryItem: UncleEntryItem;
-    UnknownTransaction: UnknownTransaction;
-    UnlockChunk: UnlockChunk;
-    UnrewardedRelayer: UnrewardedRelayer;
-    UnrewardedRelayersState: UnrewardedRelayersState;
-    UpgradeGoAhead: UpgradeGoAhead;
-    UpgradeRestriction: UpgradeRestriction;
-    UpwardMessage: UpwardMessage;
-    usize: usize;
-    USize: USize;
-    ValidationCode: ValidationCode;
-    ValidationCodeHash: ValidationCodeHash;
-    ValidationData: ValidationData;
-    ValidationDataType: ValidationDataType;
-    ValidationFunctionParams: ValidationFunctionParams;
-    ValidatorCount: ValidatorCount;
-    ValidatorId: ValidatorId;
-    ValidatorIdOf: ValidatorIdOf;
-    ValidatorIndex: ValidatorIndex;
-    ValidatorIndexCompact: ValidatorIndexCompact;
-    ValidatorPrefs: ValidatorPrefs;
-    ValidatorPrefsTo145: ValidatorPrefsTo145;
-    ValidatorPrefsTo196: ValidatorPrefsTo196;
-    ValidatorPrefsWithBlocked: ValidatorPrefsWithBlocked;
-    ValidatorPrefsWithCommission: ValidatorPrefsWithCommission;
-    ValidatorSet: ValidatorSet;
-    ValidatorSetId: ValidatorSetId;
-    ValidatorSignature: ValidatorSignature;
-    ValidDisputeStatementKind: ValidDisputeStatementKind;
-    ValidityAttestation: ValidityAttestation;
-    ValidTransaction: ValidTransaction;
-    VecInboundHrmpMessage: VecInboundHrmpMessage;
-    VersionedMultiAsset: VersionedMultiAsset;
-    VersionedMultiAssets: VersionedMultiAssets;
-    VersionedMultiLocation: VersionedMultiLocation;
-    VersionedResponse: VersionedResponse;
-    VersionedXcm: VersionedXcm;
-    VersionMigrationStage: VersionMigrationStage;
-    VestingInfo: VestingInfo;
-    VestingSchedule: VestingSchedule;
-    Vote: Vote;
-    VoteIndex: VoteIndex;
-    Voter: Voter;
-    VoterInfo: VoterInfo;
-    Votes: Votes;
-    VotesTo230: VotesTo230;
-    VoteThreshold: VoteThreshold;
-    VoteWeight: VoteWeight;
-    Voting: Voting;
-    VotingDelegating: VotingDelegating;
-    VotingDirect: VotingDirect;
-    VotingDirectVote: VotingDirectVote;
-    VouchingStatus: VouchingStatus;
-    VrfData: VrfData;
-    VrfOutput: VrfOutput;
-    VrfProof: VrfProof;
-    Weight: Weight;
-    WeightLimitV2: WeightLimitV2;
-    WeightMultiplier: WeightMultiplier;
-    WeightPerClass: WeightPerClass;
-    WeightToFeeCoefficient: WeightToFeeCoefficient;
-    WeightV0: WeightV0;
-    WeightV1: WeightV1;
-    WeightV2: WeightV2;
-    WildFungibility: WildFungibility;
-    WildFungibilityV0: WildFungibilityV0;
-    WildFungibilityV1: WildFungibilityV1;
-    WildFungibilityV2: WildFungibilityV2;
-    WildMultiAsset: WildMultiAsset;
-    WildMultiAssetV1: WildMultiAssetV1;
-    WildMultiAssetV2: WildMultiAssetV2;
-    WinnersData: WinnersData;
-    WinnersData10: WinnersData10;
-    WinnersDataTuple: WinnersDataTuple;
-    WinnersDataTuple10: WinnersDataTuple10;
-    WinningData: WinningData;
-    WinningData10: WinningData10;
-    WinningDataEntry: WinningDataEntry;
-    WithdrawReasons: WithdrawReasons;
-    Xcm: Xcm;
-    XcmAssetId: XcmAssetId;
-    XcmError: XcmError;
-    XcmErrorV0: XcmErrorV0;
-    XcmErrorV1: XcmErrorV1;
-    XcmErrorV2: XcmErrorV2;
-    XcmOrder: XcmOrder;
-    XcmOrderV0: XcmOrderV0;
-    XcmOrderV1: XcmOrderV1;
-    XcmOrderV2: XcmOrderV2;
-    XcmOrigin: XcmOrigin;
-    XcmOriginKind: XcmOriginKind;
-    XcmpMessageFormat: XcmpMessageFormat;
-    XcmV0: XcmV0;
-    XcmV1: XcmV1;
-    XcmV2: XcmV2;
-    XcmVersion: XcmVersion;
-  } // InterfaceTypes
-} // declare module
deletedjs-packages/types/src/default/definitions.tsdiffbeforeafterboth
--- a/js-packages/types/src/default/definitions.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import types from '../lookup.js';
-
-export default {
-  types,
-  rpc: {},
-};
deletedjs-packages/types/src/default/index.tsdiffbeforeafterboth
--- a/js-packages/types/src/default/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export * from './types.js';
deletedjs-packages/types/src/default/types.tsdiffbeforeafterboth
--- a/js-packages/types/src/default/types.ts
+++ /dev/null
@@ -1,5537 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-import type { Data } from '@polkadot/types';
-import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
-import type { ITuple } from '@polkadot/types-codec/types';
-import type { Vote } from '@polkadot/types/interfaces/elections';
-import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime';
-import type { Event } from '@polkadot/types/interfaces/system';
-
-/** @name CumulusPalletDmpQueueCall */
-export interface CumulusPalletDmpQueueCall extends Enum {
-  readonly isServiceOverweight: boolean;
-  readonly asServiceOverweight: {
-    readonly index: u64;
-    readonly weightLimit: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly type: 'ServiceOverweight';
-}
-
-/** @name CumulusPalletDmpQueueConfigData */
-export interface CumulusPalletDmpQueueConfigData extends Struct {
-  readonly maxIndividual: SpWeightsWeightV2Weight;
-}
-
-/** @name CumulusPalletDmpQueueError */
-export interface CumulusPalletDmpQueueError extends Enum {
-  readonly isUnknown: boolean;
-  readonly isOverLimit: boolean;
-  readonly type: 'Unknown' | 'OverLimit';
-}
-
-/** @name CumulusPalletDmpQueueEvent */
-export interface CumulusPalletDmpQueueEvent extends Enum {
-  readonly isInvalidFormat: boolean;
-  readonly asInvalidFormat: {
-    readonly messageHash: U8aFixed;
-  } & Struct;
-  readonly isUnsupportedVersion: boolean;
-  readonly asUnsupportedVersion: {
-    readonly messageHash: U8aFixed;
-  } & Struct;
-  readonly isExecutedDownward: boolean;
-  readonly asExecutedDownward: {
-    readonly messageHash: U8aFixed;
-    readonly messageId: U8aFixed;
-    readonly outcome: StagingXcmV3TraitsOutcome;
-  } & Struct;
-  readonly isWeightExhausted: boolean;
-  readonly asWeightExhausted: {
-    readonly messageHash: U8aFixed;
-    readonly messageId: U8aFixed;
-    readonly remainingWeight: SpWeightsWeightV2Weight;
-    readonly requiredWeight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isOverweightEnqueued: boolean;
-  readonly asOverweightEnqueued: {
-    readonly messageHash: U8aFixed;
-    readonly messageId: U8aFixed;
-    readonly overweightIndex: u64;
-    readonly requiredWeight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isOverweightServiced: boolean;
-  readonly asOverweightServiced: {
-    readonly overweightIndex: u64;
-    readonly weightUsed: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isMaxMessagesExhausted: boolean;
-  readonly asMaxMessagesExhausted: {
-    readonly messageHash: U8aFixed;
-  } & Struct;
-  readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted';
-}
-
-/** @name CumulusPalletDmpQueuePageIndexData */
-export interface CumulusPalletDmpQueuePageIndexData extends Struct {
-  readonly beginUsed: u32;
-  readonly endUsed: u32;
-  readonly overweightCount: u64;
-}
-
-/** @name CumulusPalletParachainSystemCall */
-export interface CumulusPalletParachainSystemCall extends Enum {
-  readonly isSetValidationData: boolean;
-  readonly asSetValidationData: {
-    readonly data: CumulusPrimitivesParachainInherentParachainInherentData;
-  } & Struct;
-  readonly isSudoSendUpwardMessage: boolean;
-  readonly asSudoSendUpwardMessage: {
-    readonly message: Bytes;
-  } & Struct;
-  readonly isAuthorizeUpgrade: boolean;
-  readonly asAuthorizeUpgrade: {
-    readonly codeHash: H256;
-    readonly checkVersion: bool;
-  } & Struct;
-  readonly isEnactAuthorizedUpgrade: boolean;
-  readonly asEnactAuthorizedUpgrade: {
-    readonly code: Bytes;
-  } & Struct;
-  readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade';
-}
-
-/** @name CumulusPalletParachainSystemCodeUpgradeAuthorization */
-export interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct {
-  readonly codeHash: H256;
-  readonly checkVersion: bool;
-}
-
-/** @name CumulusPalletParachainSystemError */
-export interface CumulusPalletParachainSystemError extends Enum {
-  readonly isOverlappingUpgrades: boolean;
-  readonly isProhibitedByPolkadot: boolean;
-  readonly isTooBig: boolean;
-  readonly isValidationDataNotAvailable: boolean;
-  readonly isHostConfigurationNotAvailable: boolean;
-  readonly isNotScheduled: boolean;
-  readonly isNothingAuthorized: boolean;
-  readonly isUnauthorized: boolean;
-  readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized';
-}
-
-/** @name CumulusPalletParachainSystemEvent */
-export interface CumulusPalletParachainSystemEvent extends Enum {
-  readonly isValidationFunctionStored: boolean;
-  readonly isValidationFunctionApplied: boolean;
-  readonly asValidationFunctionApplied: {
-    readonly relayChainBlockNum: u32;
-  } & Struct;
-  readonly isValidationFunctionDiscarded: boolean;
-  readonly isUpgradeAuthorized: boolean;
-  readonly asUpgradeAuthorized: {
-    readonly codeHash: H256;
-  } & Struct;
-  readonly isDownwardMessagesReceived: boolean;
-  readonly asDownwardMessagesReceived: {
-    readonly count: u32;
-  } & Struct;
-  readonly isDownwardMessagesProcessed: boolean;
-  readonly asDownwardMessagesProcessed: {
-    readonly weightUsed: SpWeightsWeightV2Weight;
-    readonly dmqHead: H256;
-  } & Struct;
-  readonly isUpwardMessageSent: boolean;
-  readonly asUpwardMessageSent: {
-    readonly messageHash: Option<U8aFixed>;
-  } & Struct;
-  readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent';
-}
-
-/** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot */
-export interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct {
-  readonly dmqMqcHead: H256;
-  readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity;
-  readonly ingressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
-  readonly egressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
-}
-
-/** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity */
-export interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct {
-  readonly remainingCount: u32;
-  readonly remainingSize: u32;
-}
-
-/** @name CumulusPalletParachainSystemUnincludedSegmentAncestor */
-export interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct {
-  readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
-  readonly paraHeadHash: Option<H256>;
-  readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
-}
-
-/** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate */
-export interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct {
-  readonly msgCount: u32;
-  readonly totalBytes: u32;
-}
-
-/** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker */
-export interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct {
-  readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
-  readonly hrmpWatermark: Option<u32>;
-  readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
-}
-
-/** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth */
-export interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct {
-  readonly umpMsgCount: u32;
-  readonly umpTotalBytes: u32;
-  readonly hrmpOutgoing: BTreeMap<u32, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate>;
-}
-
-/** @name CumulusPalletXcmCall */
-export interface CumulusPalletXcmCall extends Null {}
-
-/** @name CumulusPalletXcmError */
-export interface CumulusPalletXcmError extends Null {}
-
-/** @name CumulusPalletXcmEvent */
-export interface CumulusPalletXcmEvent extends Enum {
-  readonly isInvalidFormat: boolean;
-  readonly asInvalidFormat: U8aFixed;
-  readonly isUnsupportedVersion: boolean;
-  readonly asUnsupportedVersion: U8aFixed;
-  readonly isExecutedDownward: boolean;
-  readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>;
-  readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward';
-}
-
-/** @name CumulusPalletXcmOrigin */
-export interface CumulusPalletXcmOrigin extends Enum {
-  readonly isRelay: boolean;
-  readonly isSiblingParachain: boolean;
-  readonly asSiblingParachain: u32;
-  readonly type: 'Relay' | 'SiblingParachain';
-}
-
-/** @name CumulusPalletXcmpQueueCall */
-export interface CumulusPalletXcmpQueueCall extends Enum {
-  readonly isServiceOverweight: boolean;
-  readonly asServiceOverweight: {
-    readonly index: u64;
-    readonly weightLimit: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isSuspendXcmExecution: boolean;
-  readonly isResumeXcmExecution: boolean;
-  readonly isUpdateSuspendThreshold: boolean;
-  readonly asUpdateSuspendThreshold: {
-    readonly new_: u32;
-  } & Struct;
-  readonly isUpdateDropThreshold: boolean;
-  readonly asUpdateDropThreshold: {
-    readonly new_: u32;
-  } & Struct;
-  readonly isUpdateResumeThreshold: boolean;
-  readonly asUpdateResumeThreshold: {
-    readonly new_: u32;
-  } & Struct;
-  readonly isUpdateThresholdWeight: boolean;
-  readonly asUpdateThresholdWeight: {
-    readonly new_: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isUpdateWeightRestrictDecay: boolean;
-  readonly asUpdateWeightRestrictDecay: {
-    readonly new_: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isUpdateXcmpMaxIndividualWeight: boolean;
-  readonly asUpdateXcmpMaxIndividualWeight: {
-    readonly new_: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight';
-}
-
-/** @name CumulusPalletXcmpQueueError */
-export interface CumulusPalletXcmpQueueError extends Enum {
-  readonly isFailedToSend: boolean;
-  readonly isBadXcmOrigin: boolean;
-  readonly isBadXcm: boolean;
-  readonly isBadOverweightIndex: boolean;
-  readonly isWeightOverLimit: boolean;
-  readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
-}
-
-/** @name CumulusPalletXcmpQueueEvent */
-export interface CumulusPalletXcmpQueueEvent extends Enum {
-  readonly isSuccess: boolean;
-  readonly asSuccess: {
-    readonly messageHash: U8aFixed;
-    readonly messageId: U8aFixed;
-    readonly weight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isFail: boolean;
-  readonly asFail: {
-    readonly messageHash: U8aFixed;
-    readonly messageId: U8aFixed;
-    readonly error: StagingXcmV3TraitsError;
-    readonly weight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isBadVersion: boolean;
-  readonly asBadVersion: {
-    readonly messageHash: U8aFixed;
-  } & Struct;
-  readonly isBadFormat: boolean;
-  readonly asBadFormat: {
-    readonly messageHash: U8aFixed;
-  } & Struct;
-  readonly isXcmpMessageSent: boolean;
-  readonly asXcmpMessageSent: {
-    readonly messageHash: U8aFixed;
-  } & Struct;
-  readonly isOverweightEnqueued: boolean;
-  readonly asOverweightEnqueued: {
-    readonly sender: u32;
-    readonly sentAt: u32;
-    readonly index: u64;
-    readonly required: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isOverweightServiced: boolean;
-  readonly asOverweightServiced: {
-    readonly index: u64;
-    readonly used: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced';
-}
-
-/** @name CumulusPalletXcmpQueueInboundChannelDetails */
-export interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
-  readonly sender: u32;
-  readonly state: CumulusPalletXcmpQueueInboundState;
-  readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat]>>;
-}
-
-/** @name CumulusPalletXcmpQueueInboundState */
-export interface CumulusPalletXcmpQueueInboundState extends Enum {
-  readonly isOk: boolean;
-  readonly isSuspended: boolean;
-  readonly type: 'Ok' | 'Suspended';
-}
-
-/** @name CumulusPalletXcmpQueueOutboundChannelDetails */
-export interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
-  readonly recipient: u32;
-  readonly state: CumulusPalletXcmpQueueOutboundState;
-  readonly signalsExist: bool;
-  readonly firstIndex: u16;
-  readonly lastIndex: u16;
-}
-
-/** @name CumulusPalletXcmpQueueOutboundState */
-export interface CumulusPalletXcmpQueueOutboundState extends Enum {
-  readonly isOk: boolean;
-  readonly isSuspended: boolean;
-  readonly type: 'Ok' | 'Suspended';
-}
-
-/** @name CumulusPalletXcmpQueueQueueConfigData */
-export interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
-  readonly suspendThreshold: u32;
-  readonly dropThreshold: u32;
-  readonly resumeThreshold: u32;
-  readonly thresholdWeight: SpWeightsWeightV2Weight;
-  readonly weightRestrictDecay: SpWeightsWeightV2Weight;
-  readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight;
-}
-
-/** @name CumulusPrimitivesParachainInherentParachainInherentData */
-export interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct {
-  readonly validationData: PolkadotPrimitivesV5PersistedValidationData;
-  readonly relayChainState: SpTrieStorageProof;
-  readonly downwardMessages: Vec<PolkadotCorePrimitivesInboundDownwardMessage>;
-  readonly horizontalMessages: BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>;
-}
-
-/** @name EthbloomBloom */
-export interface EthbloomBloom extends U8aFixed {}
-
-/** @name EthereumBlock */
-export interface EthereumBlock extends Struct {
-  readonly header: EthereumHeader;
-  readonly transactions: Vec<EthereumTransactionTransactionV2>;
-  readonly ommers: Vec<EthereumHeader>;
-}
-
-/** @name EthereumHeader */
-export interface EthereumHeader extends Struct {
-  readonly parentHash: H256;
-  readonly ommersHash: H256;
-  readonly beneficiary: H160;
-  readonly stateRoot: H256;
-  readonly transactionsRoot: H256;
-  readonly receiptsRoot: H256;
-  readonly logsBloom: EthbloomBloom;
-  readonly difficulty: U256;
-  readonly number: U256;
-  readonly gasLimit: U256;
-  readonly gasUsed: U256;
-  readonly timestamp: u64;
-  readonly extraData: Bytes;
-  readonly mixHash: H256;
-  readonly nonce: EthereumTypesHashH64;
-}
-
-/** @name EthereumLog */
-export interface EthereumLog extends Struct {
-  readonly address: H160;
-  readonly topics: Vec<H256>;
-  readonly data: Bytes;
-}
-
-/** @name EthereumReceiptEip658ReceiptData */
-export interface EthereumReceiptEip658ReceiptData extends Struct {
-  readonly statusCode: u8;
-  readonly usedGas: U256;
-  readonly logsBloom: EthbloomBloom;
-  readonly logs: Vec<EthereumLog>;
-}
-
-/** @name EthereumReceiptReceiptV3 */
-export interface EthereumReceiptReceiptV3 extends Enum {
-  readonly isLegacy: boolean;
-  readonly asLegacy: EthereumReceiptEip658ReceiptData;
-  readonly isEip2930: boolean;
-  readonly asEip2930: EthereumReceiptEip658ReceiptData;
-  readonly isEip1559: boolean;
-  readonly asEip1559: EthereumReceiptEip658ReceiptData;
-  readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
-}
-
-/** @name EthereumTransactionAccessListItem */
-export interface EthereumTransactionAccessListItem extends Struct {
-  readonly address: H160;
-  readonly storageKeys: Vec<H256>;
-}
-
-/** @name EthereumTransactionEip1559Transaction */
-export interface EthereumTransactionEip1559Transaction extends Struct {
-  readonly chainId: u64;
-  readonly nonce: U256;
-  readonly maxPriorityFeePerGas: U256;
-  readonly maxFeePerGas: U256;
-  readonly gasLimit: U256;
-  readonly action: EthereumTransactionTransactionAction;
-  readonly value: U256;
-  readonly input: Bytes;
-  readonly accessList: Vec<EthereumTransactionAccessListItem>;
-  readonly oddYParity: bool;
-  readonly r: H256;
-  readonly s: H256;
-}
-
-/** @name EthereumTransactionEip2930Transaction */
-export interface EthereumTransactionEip2930Transaction extends Struct {
-  readonly chainId: u64;
-  readonly nonce: U256;
-  readonly gasPrice: U256;
-  readonly gasLimit: U256;
-  readonly action: EthereumTransactionTransactionAction;
-  readonly value: U256;
-  readonly input: Bytes;
-  readonly accessList: Vec<EthereumTransactionAccessListItem>;
-  readonly oddYParity: bool;
-  readonly r: H256;
-  readonly s: H256;
-}
-
-/** @name EthereumTransactionLegacyTransaction */
-export interface EthereumTransactionLegacyTransaction extends Struct {
-  readonly nonce: U256;
-  readonly gasPrice: U256;
-  readonly gasLimit: U256;
-  readonly action: EthereumTransactionTransactionAction;
-  readonly value: U256;
-  readonly input: Bytes;
-  readonly signature: EthereumTransactionTransactionSignature;
-}
-
-/** @name EthereumTransactionTransactionAction */
-export interface EthereumTransactionTransactionAction extends Enum {
-  readonly isCall: boolean;
-  readonly asCall: H160;
-  readonly isCreate: boolean;
-  readonly type: 'Call' | 'Create';
-}
-
-/** @name EthereumTransactionTransactionSignature */
-export interface EthereumTransactionTransactionSignature extends Struct {
-  readonly v: u64;
-  readonly r: H256;
-  readonly s: H256;
-}
-
-/** @name EthereumTransactionTransactionV2 */
-export interface EthereumTransactionTransactionV2 extends Enum {
-  readonly isLegacy: boolean;
-  readonly asLegacy: EthereumTransactionLegacyTransaction;
-  readonly isEip2930: boolean;
-  readonly asEip2930: EthereumTransactionEip2930Transaction;
-  readonly isEip1559: boolean;
-  readonly asEip1559: EthereumTransactionEip1559Transaction;
-  readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
-}
-
-/** @name EthereumTypesHashH64 */
-export interface EthereumTypesHashH64 extends U8aFixed {}
-
-/** @name EvmCoreErrorExitError */
-export interface EvmCoreErrorExitError extends Enum {
-  readonly isStackUnderflow: boolean;
-  readonly isStackOverflow: boolean;
-  readonly isInvalidJump: boolean;
-  readonly isInvalidRange: boolean;
-  readonly isDesignatedInvalid: boolean;
-  readonly isCallTooDeep: boolean;
-  readonly isCreateCollision: boolean;
-  readonly isCreateContractLimit: boolean;
-  readonly isOutOfOffset: boolean;
-  readonly isOutOfGas: boolean;
-  readonly isOutOfFund: boolean;
-  readonly isPcUnderflow: boolean;
-  readonly isCreateEmpty: boolean;
-  readonly isOther: boolean;
-  readonly asOther: Text;
-  readonly isMaxNonce: boolean;
-  readonly isInvalidCode: boolean;
-  readonly asInvalidCode: u8;
-  readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode';
-}
-
-/** @name EvmCoreErrorExitFatal */
-export interface EvmCoreErrorExitFatal extends Enum {
-  readonly isNotSupported: boolean;
-  readonly isUnhandledInterrupt: boolean;
-  readonly isCallErrorAsFatal: boolean;
-  readonly asCallErrorAsFatal: EvmCoreErrorExitError;
-  readonly isOther: boolean;
-  readonly asOther: Text;
-  readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other';
-}
-
-/** @name EvmCoreErrorExitReason */
-export interface EvmCoreErrorExitReason extends Enum {
-  readonly isSucceed: boolean;
-  readonly asSucceed: EvmCoreErrorExitSucceed;
-  readonly isError: boolean;
-  readonly asError: EvmCoreErrorExitError;
-  readonly isRevert: boolean;
-  readonly asRevert: EvmCoreErrorExitRevert;
-  readonly isFatal: boolean;
-  readonly asFatal: EvmCoreErrorExitFatal;
-  readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal';
-}
-
-/** @name EvmCoreErrorExitRevert */
-export interface EvmCoreErrorExitRevert extends Enum {
-  readonly isReverted: boolean;
-  readonly type: 'Reverted';
-}
-
-/** @name EvmCoreErrorExitSucceed */
-export interface EvmCoreErrorExitSucceed extends Enum {
-  readonly isStopped: boolean;
-  readonly isReturned: boolean;
-  readonly isSuicided: boolean;
-  readonly type: 'Stopped' | 'Returned' | 'Suicided';
-}
-
-/** @name FpRpcTransactionStatus */
-export interface FpRpcTransactionStatus extends Struct {
-  readonly transactionHash: H256;
-  readonly transactionIndex: u32;
-  readonly from: H160;
-  readonly to: Option<H160>;
-  readonly contractAddress: Option<H160>;
-  readonly logs: Vec<EthereumLog>;
-  readonly logsBloom: EthbloomBloom;
-}
-
-/** @name FrameSupportDispatchDispatchClass */
-export interface FrameSupportDispatchDispatchClass extends Enum {
-  readonly isNormal: boolean;
-  readonly isOperational: boolean;
-  readonly isMandatory: boolean;
-  readonly type: 'Normal' | 'Operational' | 'Mandatory';
-}
-
-/** @name FrameSupportDispatchDispatchInfo */
-export interface FrameSupportDispatchDispatchInfo extends Struct {
-  readonly weight: SpWeightsWeightV2Weight;
-  readonly class: FrameSupportDispatchDispatchClass;
-  readonly paysFee: FrameSupportDispatchPays;
-}
-
-/** @name FrameSupportDispatchPays */
-export interface FrameSupportDispatchPays extends Enum {
-  readonly isYes: boolean;
-  readonly isNo: boolean;
-  readonly type: 'Yes' | 'No';
-}
-
-/** @name FrameSupportDispatchPerDispatchClassU32 */
-export interface FrameSupportDispatchPerDispatchClassU32 extends Struct {
-  readonly normal: u32;
-  readonly operational: u32;
-  readonly mandatory: u32;
-}
-
-/** @name FrameSupportDispatchPerDispatchClassWeight */
-export interface FrameSupportDispatchPerDispatchClassWeight extends Struct {
-  readonly normal: SpWeightsWeightV2Weight;
-  readonly operational: SpWeightsWeightV2Weight;
-  readonly mandatory: SpWeightsWeightV2Weight;
-}
-
-/** @name FrameSupportDispatchPerDispatchClassWeightsPerClass */
-export interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct {
-  readonly normal: FrameSystemLimitsWeightsPerClass;
-  readonly operational: FrameSystemLimitsWeightsPerClass;
-  readonly mandatory: FrameSystemLimitsWeightsPerClass;
-}
-
-/** @name FrameSupportDispatchRawOrigin */
-export interface FrameSupportDispatchRawOrigin extends Enum {
-  readonly isRoot: boolean;
-  readonly isSigned: boolean;
-  readonly asSigned: AccountId32;
-  readonly isNone: boolean;
-  readonly type: 'Root' | 'Signed' | 'None';
-}
-
-/** @name FrameSupportPalletId */
-export interface FrameSupportPalletId extends U8aFixed {}
-
-/** @name FrameSupportPreimagesBounded */
-export interface FrameSupportPreimagesBounded extends Enum {
-  readonly isLegacy: boolean;
-  readonly asLegacy: {
-    readonly hash_: H256;
-  } & Struct;
-  readonly isInline: boolean;
-  readonly asInline: Bytes;
-  readonly isLookup: boolean;
-  readonly asLookup: {
-    readonly hash_: H256;
-    readonly len: u32;
-  } & Struct;
-  readonly type: 'Legacy' | 'Inline' | 'Lookup';
-}
-
-/** @name FrameSupportScheduleDispatchTime */
-export interface FrameSupportScheduleDispatchTime extends Enum {
-  readonly isAt: boolean;
-  readonly asAt: u32;
-  readonly isAfter: boolean;
-  readonly asAfter: u32;
-  readonly type: 'At' | 'After';
-}
-
-/** @name FrameSupportTokensMiscBalanceStatus */
-export interface FrameSupportTokensMiscBalanceStatus extends Enum {
-  readonly isFree: boolean;
-  readonly isReserved: boolean;
-  readonly type: 'Free' | 'Reserved';
-}
-
-/** @name FrameSystemAccountInfo */
-export interface FrameSystemAccountInfo extends Struct {
-  readonly nonce: u32;
-  readonly consumers: u32;
-  readonly providers: u32;
-  readonly sufficients: u32;
-  readonly data: PalletBalancesAccountData;
-}
-
-/** @name FrameSystemCall */
-export interface FrameSystemCall extends Enum {
-  readonly isRemark: boolean;
-  readonly asRemark: {
-    readonly remark: Bytes;
-  } & Struct;
-  readonly isSetHeapPages: boolean;
-  readonly asSetHeapPages: {
-    readonly pages: u64;
-  } & Struct;
-  readonly isSetCode: boolean;
-  readonly asSetCode: {
-    readonly code: Bytes;
-  } & Struct;
-  readonly isSetCodeWithoutChecks: boolean;
-  readonly asSetCodeWithoutChecks: {
-    readonly code: Bytes;
-  } & Struct;
-  readonly isSetStorage: boolean;
-  readonly asSetStorage: {
-    readonly items: Vec<ITuple<[Bytes, Bytes]>>;
-  } & Struct;
-  readonly isKillStorage: boolean;
-  readonly asKillStorage: {
-    readonly keys_: Vec<Bytes>;
-  } & Struct;
-  readonly isKillPrefix: boolean;
-  readonly asKillPrefix: {
-    readonly prefix: Bytes;
-    readonly subkeys: u32;
-  } & Struct;
-  readonly isRemarkWithEvent: boolean;
-  readonly asRemarkWithEvent: {
-    readonly remark: Bytes;
-  } & Struct;
-  readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent';
-}
-
-/** @name FrameSystemError */
-export interface FrameSystemError extends Enum {
-  readonly isInvalidSpecName: boolean;
-  readonly isSpecVersionNeedsToIncrease: boolean;
-  readonly isFailedToExtractRuntimeVersion: boolean;
-  readonly isNonDefaultComposite: boolean;
-  readonly isNonZeroRefCount: boolean;
-  readonly isCallFiltered: boolean;
-  readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered';
-}
-
-/** @name FrameSystemEvent */
-export interface FrameSystemEvent extends Enum {
-  readonly isExtrinsicSuccess: boolean;
-  readonly asExtrinsicSuccess: {
-    readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
-  } & Struct;
-  readonly isExtrinsicFailed: boolean;
-  readonly asExtrinsicFailed: {
-    readonly dispatchError: SpRuntimeDispatchError;
-    readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
-  } & Struct;
-  readonly isCodeUpdated: boolean;
-  readonly isNewAccount: boolean;
-  readonly asNewAccount: {
-    readonly account: AccountId32;
-  } & Struct;
-  readonly isKilledAccount: boolean;
-  readonly asKilledAccount: {
-    readonly account: AccountId32;
-  } & Struct;
-  readonly isRemarked: boolean;
-  readonly asRemarked: {
-    readonly sender: AccountId32;
-    readonly hash_: H256;
-  } & Struct;
-  readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked';
-}
-
-/** @name FrameSystemEventRecord */
-export interface FrameSystemEventRecord extends Struct {
-  readonly phase: FrameSystemPhase;
-  readonly event: Event;
-  readonly topics: Vec<H256>;
-}
-
-/** @name FrameSystemExtensionsCheckGenesis */
-export interface FrameSystemExtensionsCheckGenesis extends Null {}
-
-/** @name FrameSystemExtensionsCheckNonce */
-export interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
-
-/** @name FrameSystemExtensionsCheckSpecVersion */
-export interface FrameSystemExtensionsCheckSpecVersion extends Null {}
-
-/** @name FrameSystemExtensionsCheckTxVersion */
-export interface FrameSystemExtensionsCheckTxVersion extends Null {}
-
-/** @name FrameSystemExtensionsCheckWeight */
-export interface FrameSystemExtensionsCheckWeight extends Null {}
-
-/** @name FrameSystemLastRuntimeUpgradeInfo */
-export interface FrameSystemLastRuntimeUpgradeInfo extends Struct {
-  readonly specVersion: Compact<u32>;
-  readonly specName: Text;
-}
-
-/** @name FrameSystemLimitsBlockLength */
-export interface FrameSystemLimitsBlockLength extends Struct {
-  readonly max: FrameSupportDispatchPerDispatchClassU32;
-}
-
-/** @name FrameSystemLimitsBlockWeights */
-export interface FrameSystemLimitsBlockWeights extends Struct {
-  readonly baseBlock: SpWeightsWeightV2Weight;
-  readonly maxBlock: SpWeightsWeightV2Weight;
-  readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
-}
-
-/** @name FrameSystemLimitsWeightsPerClass */
-export interface FrameSystemLimitsWeightsPerClass extends Struct {
-  readonly baseExtrinsic: SpWeightsWeightV2Weight;
-  readonly maxExtrinsic: Option<SpWeightsWeightV2Weight>;
-  readonly maxTotal: Option<SpWeightsWeightV2Weight>;
-  readonly reserved: Option<SpWeightsWeightV2Weight>;
-}
-
-/** @name FrameSystemPhase */
-export interface FrameSystemPhase extends Enum {
-  readonly isApplyExtrinsic: boolean;
-  readonly asApplyExtrinsic: u32;
-  readonly isFinalization: boolean;
-  readonly isInitialization: boolean;
-  readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization';
-}
-
-/** @name OpalRuntimeOriginCaller */
-export interface OpalRuntimeOriginCaller extends Enum {
-  readonly isSystem: boolean;
-  readonly asSystem: FrameSupportDispatchRawOrigin;
-  readonly isVoid: boolean;
-  readonly asVoid: SpCoreVoid;
-  readonly isCouncil: boolean;
-  readonly asCouncil: PalletCollectiveRawOrigin;
-  readonly isTechnicalCommittee: boolean;
-  readonly asTechnicalCommittee: PalletCollectiveRawOrigin;
-  readonly isPolkadotXcm: boolean;
-  readonly asPolkadotXcm: PalletXcmOrigin;
-  readonly isCumulusXcm: boolean;
-  readonly asCumulusXcm: CumulusPalletXcmOrigin;
-  readonly isOrigins: boolean;
-  readonly asOrigins: PalletGovOriginsOrigin;
-  readonly isEthereum: boolean;
-  readonly asEthereum: PalletEthereumRawOrigin;
-  readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum';
-}
-
-/** @name OpalRuntimeRuntime */
-export interface OpalRuntimeRuntime extends Null {}
-
-/** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls */
-export interface OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls extends Null {}
-
-/** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance */
-export interface OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance extends Null {}
-
-/** @name OpalRuntimeRuntimeCommonSessionKeys */
-export interface OpalRuntimeRuntimeCommonSessionKeys extends Struct {
-  readonly aura: SpConsensusAuraSr25519AppSr25519Public;
-}
-
-/** @name OpalRuntimeRuntimeHoldReason */
-export interface OpalRuntimeRuntimeHoldReason extends Enum {
-  readonly isCollatorSelection: boolean;
-  readonly asCollatorSelection: PalletCollatorSelectionHoldReason;
-  readonly type: 'CollatorSelection';
-}
-
-/** @name OrmlTokensAccountData */
-export interface OrmlTokensAccountData extends Struct {
-  readonly free: u128;
-  readonly reserved: u128;
-  readonly frozen: u128;
-}
-
-/** @name OrmlTokensBalanceLock */
-export interface OrmlTokensBalanceLock extends Struct {
-  readonly id: U8aFixed;
-  readonly amount: u128;
-}
-
-/** @name OrmlTokensModuleCall */
-export interface OrmlTokensModuleCall extends Enum {
-  readonly isTransfer: boolean;
-  readonly asTransfer: {
-    readonly dest: MultiAddress;
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: Compact<u128>;
-  } & Struct;
-  readonly isTransferAll: boolean;
-  readonly asTransferAll: {
-    readonly dest: MultiAddress;
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly keepAlive: bool;
-  } & Struct;
-  readonly isTransferKeepAlive: boolean;
-  readonly asTransferKeepAlive: {
-    readonly dest: MultiAddress;
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: Compact<u128>;
-  } & Struct;
-  readonly isForceTransfer: boolean;
-  readonly asForceTransfer: {
-    readonly source: MultiAddress;
-    readonly dest: MultiAddress;
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: Compact<u128>;
-  } & Struct;
-  readonly isSetBalance: boolean;
-  readonly asSetBalance: {
-    readonly who: MultiAddress;
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly newFree: Compact<u128>;
-    readonly newReserved: Compact<u128>;
-  } & Struct;
-  readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance';
-}
-
-/** @name OrmlTokensModuleError */
-export interface OrmlTokensModuleError extends Enum {
-  readonly isBalanceTooLow: boolean;
-  readonly isAmountIntoBalanceFailed: boolean;
-  readonly isLiquidityRestrictions: boolean;
-  readonly isMaxLocksExceeded: boolean;
-  readonly isKeepAlive: boolean;
-  readonly isExistentialDeposit: boolean;
-  readonly isDeadAccount: boolean;
-  readonly isTooManyReserves: boolean;
-  readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves';
-}
-
-/** @name OrmlTokensModuleEvent */
-export interface OrmlTokensModuleEvent extends Enum {
-  readonly isEndowed: boolean;
-  readonly asEndowed: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isDustLost: boolean;
-  readonly asDustLost: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isTransfer: boolean;
-  readonly asTransfer: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly from: AccountId32;
-    readonly to: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isReserved: boolean;
-  readonly asReserved: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isUnreserved: boolean;
-  readonly asUnreserved: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isReserveRepatriated: boolean;
-  readonly asReserveRepatriated: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly from: AccountId32;
-    readonly to: AccountId32;
-    readonly amount: u128;
-    readonly status: FrameSupportTokensMiscBalanceStatus;
-  } & Struct;
-  readonly isBalanceSet: boolean;
-  readonly asBalanceSet: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly free: u128;
-    readonly reserved: u128;
-  } & Struct;
-  readonly isTotalIssuanceSet: boolean;
-  readonly asTotalIssuanceSet: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: u128;
-  } & Struct;
-  readonly isWithdrawn: boolean;
-  readonly asWithdrawn: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isSlashed: boolean;
-  readonly asSlashed: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly freeAmount: u128;
-    readonly reservedAmount: u128;
-  } & Struct;
-  readonly isDeposited: boolean;
-  readonly asDeposited: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isLockSet: boolean;
-  readonly asLockSet: {
-    readonly lockId: U8aFixed;
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isLockRemoved: boolean;
-  readonly asLockRemoved: {
-    readonly lockId: U8aFixed;
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-  } & Struct;
-  readonly isLocked: boolean;
-  readonly asLocked: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isUnlocked: boolean;
-  readonly asUnlocked: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isIssued: boolean;
-  readonly asIssued: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: u128;
-  } & Struct;
-  readonly isRescinded: boolean;
-  readonly asRescinded: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: u128;
-  } & Struct;
-  readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded';
-}
-
-/** @name OrmlTokensReserveData */
-export interface OrmlTokensReserveData extends Struct {
-  readonly id: Null;
-  readonly amount: u128;
-}
-
-/** @name OrmlVestingModuleCall */
-export interface OrmlVestingModuleCall extends Enum {
-  readonly isClaim: boolean;
-  readonly isVestedTransfer: boolean;
-  readonly asVestedTransfer: {
-    readonly dest: MultiAddress;
-    readonly schedule: OrmlVestingVestingSchedule;
-  } & Struct;
-  readonly isUpdateVestingSchedules: boolean;
-  readonly asUpdateVestingSchedules: {
-    readonly who: MultiAddress;
-    readonly vestingSchedules: Vec<OrmlVestingVestingSchedule>;
-  } & Struct;
-  readonly isClaimFor: boolean;
-  readonly asClaimFor: {
-    readonly dest: MultiAddress;
-  } & Struct;
-  readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor';
-}
-
-/** @name OrmlVestingModuleError */
-export interface OrmlVestingModuleError extends Enum {
-  readonly isZeroVestingPeriod: boolean;
-  readonly isZeroVestingPeriodCount: boolean;
-  readonly isInsufficientBalanceToLock: boolean;
-  readonly isTooManyVestingSchedules: boolean;
-  readonly isAmountLow: boolean;
-  readonly isMaxVestingSchedulesExceeded: boolean;
-  readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
-}
-
-/** @name OrmlVestingModuleEvent */
-export interface OrmlVestingModuleEvent extends Enum {
-  readonly isVestingScheduleAdded: boolean;
-  readonly asVestingScheduleAdded: {
-    readonly from: AccountId32;
-    readonly to: AccountId32;
-    readonly vestingSchedule: OrmlVestingVestingSchedule;
-  } & Struct;
-  readonly isClaimed: boolean;
-  readonly asClaimed: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isVestingSchedulesUpdated: boolean;
-  readonly asVestingSchedulesUpdated: {
-    readonly who: AccountId32;
-  } & Struct;
-  readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated';
-}
-
-/** @name OrmlVestingVestingSchedule */
-export interface OrmlVestingVestingSchedule extends Struct {
-  readonly start: u32;
-  readonly period: u32;
-  readonly periodCount: u32;
-  readonly perPeriod: Compact<u128>;
-}
-
-/** @name OrmlXtokensModuleCall */
-export interface OrmlXtokensModuleCall extends Enum {
-  readonly isTransfer: boolean;
-  readonly asTransfer: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: u128;
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly destWeightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isTransferMultiasset: boolean;
-  readonly asTransferMultiasset: {
-    readonly asset: StagingXcmVersionedMultiAsset;
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly destWeightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isTransferWithFee: boolean;
-  readonly asTransferWithFee: {
-    readonly currencyId: PalletForeignAssetsAssetId;
-    readonly amount: u128;
-    readonly fee: u128;
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly destWeightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isTransferMultiassetWithFee: boolean;
-  readonly asTransferMultiassetWithFee: {
-    readonly asset: StagingXcmVersionedMultiAsset;
-    readonly fee: StagingXcmVersionedMultiAsset;
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly destWeightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isTransferMulticurrencies: boolean;
-  readonly asTransferMulticurrencies: {
-    readonly currencies: Vec<ITuple<[PalletForeignAssetsAssetId, u128]>>;
-    readonly feeItem: u32;
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly destWeightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isTransferMultiassets: boolean;
-  readonly asTransferMultiassets: {
-    readonly assets: StagingXcmVersionedMultiAssets;
-    readonly feeItem: u32;
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly destWeightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets';
-}
-
-/** @name OrmlXtokensModuleError */
-export interface OrmlXtokensModuleError extends Enum {
-  readonly isAssetHasNoReserve: boolean;
-  readonly isNotCrossChainTransfer: boolean;
-  readonly isInvalidDest: boolean;
-  readonly isNotCrossChainTransferableCurrency: boolean;
-  readonly isUnweighableMessage: boolean;
-  readonly isXcmExecutionFailed: boolean;
-  readonly isCannotReanchor: boolean;
-  readonly isInvalidAncestry: boolean;
-  readonly isInvalidAsset: boolean;
-  readonly isDestinationNotInvertible: boolean;
-  readonly isBadVersion: boolean;
-  readonly isDistinctReserveForAssetAndFee: boolean;
-  readonly isZeroFee: boolean;
-  readonly isZeroAmount: boolean;
-  readonly isTooManyAssetsBeingSent: boolean;
-  readonly isAssetIndexNonExistent: boolean;
-  readonly isFeeNotEnough: boolean;
-  readonly isNotSupportedMultiLocation: boolean;
-  readonly isMinXcmFeeNotDefined: boolean;
-  readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined';
-}
-
-/** @name OrmlXtokensModuleEvent */
-export interface OrmlXtokensModuleEvent extends Enum {
-  readonly isTransferredMultiAssets: boolean;
-  readonly asTransferredMultiAssets: {
-    readonly sender: AccountId32;
-    readonly assets: StagingXcmV3MultiassetMultiAssets;
-    readonly fee: StagingXcmV3MultiAsset;
-    readonly dest: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly type: 'TransferredMultiAssets';
-}
-
-/** @name PalletAppPromotionCall */
-export interface PalletAppPromotionCall extends Enum {
-  readonly isSetAdminAddress: boolean;
-  readonly asSetAdminAddress: {
-    readonly admin: PalletEvmAccountBasicCrossAccountIdRepr;
-  } & Struct;
-  readonly isStake: boolean;
-  readonly asStake: {
-    readonly amount: u128;
-  } & Struct;
-  readonly isUnstakeAll: boolean;
-  readonly isSponsorCollection: boolean;
-  readonly asSponsorCollection: {
-    readonly collectionId: u32;
-  } & Struct;
-  readonly isStopSponsoringCollection: boolean;
-  readonly asStopSponsoringCollection: {
-    readonly collectionId: u32;
-  } & Struct;
-  readonly isSponsorContract: boolean;
-  readonly asSponsorContract: {
-    readonly contractId: H160;
-  } & Struct;
-  readonly isStopSponsoringContract: boolean;
-  readonly asStopSponsoringContract: {
-    readonly contractId: H160;
-  } & Struct;
-  readonly isPayoutStakers: boolean;
-  readonly asPayoutStakers: {
-    readonly stakersNumber: Option<u8>;
-  } & Struct;
-  readonly isUnstakePartial: boolean;
-  readonly asUnstakePartial: {
-    readonly amount: u128;
-  } & Struct;
-  readonly isForceUnstake: boolean;
-  readonly asForceUnstake: {
-    readonly pendingBlocks: Vec<u32>;
-  } & Struct;
-  readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake';
-}
-
-/** @name PalletAppPromotionError */
-export interface PalletAppPromotionError extends Enum {
-  readonly isAdminNotSet: boolean;
-  readonly isNoPermission: boolean;
-  readonly isNotSufficientFunds: boolean;
-  readonly isPendingForBlockOverflow: boolean;
-  readonly isSponsorNotSet: boolean;
-  readonly isInsufficientStakedBalance: boolean;
-  readonly isInconsistencyState: boolean;
-  readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState';
-}
-
-/** @name PalletAppPromotionEvent */
-export interface PalletAppPromotionEvent extends Enum {
-  readonly isStakingRecalculation: boolean;
-  readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
-  readonly isStake: boolean;
-  readonly asStake: ITuple<[AccountId32, u128]>;
-  readonly isUnstake: boolean;
-  readonly asUnstake: ITuple<[AccountId32, u128]>;
-  readonly isSetAdmin: boolean;
-  readonly asSetAdmin: AccountId32;
-  readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin';
-}
-
-/** @name PalletBalancesAccountData */
-export interface PalletBalancesAccountData extends Struct {
-  readonly free: u128;
-  readonly reserved: u128;
-  readonly frozen: u128;
-  readonly flags: u128;
-}
-
-/** @name PalletBalancesBalanceLock */
-export interface PalletBalancesBalanceLock extends Struct {
-  readonly id: U8aFixed;
-  readonly amount: u128;
-  readonly reasons: PalletBalancesReasons;
-}
-
-/** @name PalletBalancesCall */
-export interface PalletBalancesCall extends Enum {
-  readonly isTransferAllowDeath: boolean;
-  readonly asTransferAllowDeath: {
-    readonly dest: MultiAddress;
-    readonly value: Compact<u128>;
-  } & Struct;
-  readonly isSetBalanceDeprecated: boolean;
-  readonly asSetBalanceDeprecated: {
-    readonly who: MultiAddress;
-    readonly newFree: Compact<u128>;
-    readonly oldReserved: Compact<u128>;
-  } & Struct;
-  readonly isForceTransfer: boolean;
-  readonly asForceTransfer: {
-    readonly source: MultiAddress;
-    readonly dest: MultiAddress;
-    readonly value: Compact<u128>;
-  } & Struct;
-  readonly isTransferKeepAlive: boolean;
-  readonly asTransferKeepAlive: {
-    readonly dest: MultiAddress;
-    readonly value: Compact<u128>;
-  } & Struct;
-  readonly isTransferAll: boolean;
-  readonly asTransferAll: {
-    readonly dest: MultiAddress;
-    readonly keepAlive: bool;
-  } & Struct;
-  readonly isForceUnreserve: boolean;
-  readonly asForceUnreserve: {
-    readonly who: MultiAddress;
-    readonly amount: u128;
-  } & Struct;
-  readonly isUpgradeAccounts: boolean;
-  readonly asUpgradeAccounts: {
-    readonly who: Vec<AccountId32>;
-  } & Struct;
-  readonly isTransfer: boolean;
-  readonly asTransfer: {
-    readonly dest: MultiAddress;
-    readonly value: Compact<u128>;
-  } & Struct;
-  readonly isForceSetBalance: boolean;
-  readonly asForceSetBalance: {
-    readonly who: MultiAddress;
-    readonly newFree: Compact<u128>;
-  } & Struct;
-  readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance';
-}
-
-/** @name PalletBalancesError */
-export interface PalletBalancesError extends Enum {
-  readonly isVestingBalance: boolean;
-  readonly isLiquidityRestrictions: boolean;
-  readonly isInsufficientBalance: boolean;
-  readonly isExistentialDeposit: boolean;
-  readonly isExpendability: boolean;
-  readonly isExistingVestingSchedule: boolean;
-  readonly isDeadAccount: boolean;
-  readonly isTooManyReserves: boolean;
-  readonly isTooManyHolds: boolean;
-  readonly isTooManyFreezes: boolean;
-  readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes';
-}
-
-/** @name PalletBalancesEvent */
-export interface PalletBalancesEvent extends Enum {
-  readonly isEndowed: boolean;
-  readonly asEndowed: {
-    readonly account: AccountId32;
-    readonly freeBalance: u128;
-  } & Struct;
-  readonly isDustLost: boolean;
-  readonly asDustLost: {
-    readonly account: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isTransfer: boolean;
-  readonly asTransfer: {
-    readonly from: AccountId32;
-    readonly to: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isBalanceSet: boolean;
-  readonly asBalanceSet: {
-    readonly who: AccountId32;
-    readonly free: u128;
-  } & Struct;
-  readonly isReserved: boolean;
-  readonly asReserved: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isUnreserved: boolean;
-  readonly asUnreserved: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isReserveRepatriated: boolean;
-  readonly asReserveRepatriated: {
-    readonly from: AccountId32;
-    readonly to: AccountId32;
-    readonly amount: u128;
-    readonly destinationStatus: FrameSupportTokensMiscBalanceStatus;
-  } & Struct;
-  readonly isDeposit: boolean;
-  readonly asDeposit: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isWithdraw: boolean;
-  readonly asWithdraw: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isSlashed: boolean;
-  readonly asSlashed: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isMinted: boolean;
-  readonly asMinted: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isBurned: boolean;
-  readonly asBurned: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isSuspended: boolean;
-  readonly asSuspended: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isRestored: boolean;
-  readonly asRestored: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isUpgraded: boolean;
-  readonly asUpgraded: {
-    readonly who: AccountId32;
-  } & Struct;
-  readonly isIssued: boolean;
-  readonly asIssued: {
-    readonly amount: u128;
-  } & Struct;
-  readonly isRescinded: boolean;
-  readonly asRescinded: {
-    readonly amount: u128;
-  } & Struct;
-  readonly isLocked: boolean;
-  readonly asLocked: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isUnlocked: boolean;
-  readonly asUnlocked: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isFrozen: boolean;
-  readonly asFrozen: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isThawed: boolean;
-  readonly asThawed: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed';
-}
-
-/** @name PalletBalancesIdAmount */
-export interface PalletBalancesIdAmount extends Struct {
-  readonly id: U8aFixed;
-  readonly amount: u128;
-}
-
-/** @name PalletBalancesReasons */
-export interface PalletBalancesReasons extends Enum {
-  readonly isFee: boolean;
-  readonly isMisc: boolean;
-  readonly isAll: boolean;
-  readonly type: 'Fee' | 'Misc' | 'All';
-}
-
-/** @name PalletBalancesReserveData */
-export interface PalletBalancesReserveData extends Struct {
-  readonly id: U8aFixed;
-  readonly amount: u128;
-}
-
-/** @name PalletCollatorSelectionCall */
-export interface PalletCollatorSelectionCall extends Enum {
-  readonly isAddInvulnerable: boolean;
-  readonly asAddInvulnerable: {
-    readonly new_: AccountId32;
-  } & Struct;
-  readonly isRemoveInvulnerable: boolean;
-  readonly asRemoveInvulnerable: {
-    readonly who: AccountId32;
-  } & Struct;
-  readonly isGetLicense: boolean;
-  readonly isOnboard: boolean;
-  readonly isOffboard: boolean;
-  readonly isReleaseLicense: boolean;
-  readonly isForceReleaseLicense: boolean;
-  readonly asForceReleaseLicense: {
-    readonly who: AccountId32;
-  } & Struct;
-  readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense';
-}
-
-/** @name PalletCollatorSelectionError */
-export interface PalletCollatorSelectionError extends Enum {
-  readonly isTooManyCandidates: boolean;
-  readonly isUnknown: boolean;
-  readonly isPermission: boolean;
-  readonly isAlreadyHoldingLicense: boolean;
-  readonly isNoLicense: boolean;
-  readonly isAlreadyCandidate: boolean;
-  readonly isNotCandidate: boolean;
-  readonly isTooManyInvulnerables: boolean;
-  readonly isTooFewInvulnerables: boolean;
-  readonly isAlreadyInvulnerable: boolean;
-  readonly isNotInvulnerable: boolean;
-  readonly isNoAssociatedValidatorId: boolean;
-  readonly isValidatorNotRegistered: boolean;
-  readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered';
-}
-
-/** @name PalletCollatorSelectionEvent */
-export interface PalletCollatorSelectionEvent extends Enum {
-  readonly isInvulnerableAdded: boolean;
-  readonly asInvulnerableAdded: {
-    readonly invulnerable: AccountId32;
-  } & Struct;
-  readonly isInvulnerableRemoved: boolean;
-  readonly asInvulnerableRemoved: {
-    readonly invulnerable: AccountId32;
-  } & Struct;
-  readonly isLicenseObtained: boolean;
-  readonly asLicenseObtained: {
-    readonly accountId: AccountId32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isLicenseReleased: boolean;
-  readonly asLicenseReleased: {
-    readonly accountId: AccountId32;
-    readonly depositReturned: u128;
-  } & Struct;
-  readonly isCandidateAdded: boolean;
-  readonly asCandidateAdded: {
-    readonly accountId: AccountId32;
-  } & Struct;
-  readonly isCandidateRemoved: boolean;
-  readonly asCandidateRemoved: {
-    readonly accountId: AccountId32;
-  } & Struct;
-  readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved';
-}
-
-/** @name PalletCollatorSelectionHoldReason */
-export interface PalletCollatorSelectionHoldReason extends Enum {
-  readonly isLicenseBond: boolean;
-  readonly type: 'LicenseBond';
-}
-
-/** @name PalletCollectiveCall */
-export interface PalletCollectiveCall extends Enum {
-  readonly isSetMembers: boolean;
-  readonly asSetMembers: {
-    readonly newMembers: Vec<AccountId32>;
-    readonly prime: Option<AccountId32>;
-    readonly oldCount: u32;
-  } & Struct;
-  readonly isExecute: boolean;
-  readonly asExecute: {
-    readonly proposal: Call;
-    readonly lengthBound: Compact<u32>;
-  } & Struct;
-  readonly isPropose: boolean;
-  readonly asPropose: {
-    readonly threshold: Compact<u32>;
-    readonly proposal: Call;
-    readonly lengthBound: Compact<u32>;
-  } & Struct;
-  readonly isVote: boolean;
-  readonly asVote: {
-    readonly proposal: H256;
-    readonly index: Compact<u32>;
-    readonly approve: bool;
-  } & Struct;
-  readonly isDisapproveProposal: boolean;
-  readonly asDisapproveProposal: {
-    readonly proposalHash: H256;
-  } & Struct;
-  readonly isClose: boolean;
-  readonly asClose: {
-    readonly proposalHash: H256;
-    readonly index: Compact<u32>;
-    readonly proposalWeightBound: SpWeightsWeightV2Weight;
-    readonly lengthBound: Compact<u32>;
-  } & Struct;
-  readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close';
-}
-
-/** @name PalletCollectiveError */
-export interface PalletCollectiveError extends Enum {
-  readonly isNotMember: boolean;
-  readonly isDuplicateProposal: boolean;
-  readonly isProposalMissing: boolean;
-  readonly isWrongIndex: boolean;
-  readonly isDuplicateVote: boolean;
-  readonly isAlreadyInitialized: boolean;
-  readonly isTooEarly: boolean;
-  readonly isTooManyProposals: boolean;
-  readonly isWrongProposalWeight: boolean;
-  readonly isWrongProposalLength: boolean;
-  readonly isPrimeAccountNotMember: boolean;
-  readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember';
-}
-
-/** @name PalletCollectiveEvent */
-export interface PalletCollectiveEvent extends Enum {
-  readonly isProposed: boolean;
-  readonly asProposed: {
-    readonly account: AccountId32;
-    readonly proposalIndex: u32;
-    readonly proposalHash: H256;
-    readonly threshold: u32;
-  } & Struct;
-  readonly isVoted: boolean;
-  readonly asVoted: {
-    readonly account: AccountId32;
-    readonly proposalHash: H256;
-    readonly voted: bool;
-    readonly yes: u32;
-    readonly no: u32;
-  } & Struct;
-  readonly isApproved: boolean;
-  readonly asApproved: {
-    readonly proposalHash: H256;
-  } & Struct;
-  readonly isDisapproved: boolean;
-  readonly asDisapproved: {
-    readonly proposalHash: H256;
-  } & Struct;
-  readonly isExecuted: boolean;
-  readonly asExecuted: {
-    readonly proposalHash: H256;
-    readonly result: Result<Null, SpRuntimeDispatchError>;
-  } & Struct;
-  readonly isMemberExecuted: boolean;
-  readonly asMemberExecuted: {
-    readonly proposalHash: H256;
-    readonly result: Result<Null, SpRuntimeDispatchError>;
-  } & Struct;
-  readonly isClosed: boolean;
-  readonly asClosed: {
-    readonly proposalHash: H256;
-    readonly yes: u32;
-    readonly no: u32;
-  } & Struct;
-  readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed';
-}
-
-/** @name PalletCollectiveRawOrigin */
-export interface PalletCollectiveRawOrigin extends Enum {
-  readonly isMembers: boolean;
-  readonly asMembers: ITuple<[u32, u32]>;
-  readonly isMember: boolean;
-  readonly asMember: AccountId32;
-  readonly isPhantom: boolean;
-  readonly type: 'Members' | 'Member' | 'Phantom';
-}
-
-/** @name PalletCollectiveVotes */
-export interface PalletCollectiveVotes extends Struct {
-  readonly index: u32;
-  readonly threshold: u32;
-  readonly ayes: Vec<AccountId32>;
-  readonly nays: Vec<AccountId32>;
-  readonly end: u32;
-}
-
-/** @name PalletCommonError */
-export interface PalletCommonError extends Enum {
-  readonly isCollectionNotFound: boolean;
-  readonly isMustBeTokenOwner: boolean;
-  readonly isNoPermission: boolean;
-  readonly isCantDestroyNotEmptyCollection: boolean;
-  readonly isPublicMintingNotAllowed: boolean;
-  readonly isAddressNotInAllowlist: boolean;
-  readonly isCollectionNameLimitExceeded: boolean;
-  readonly isCollectionDescriptionLimitExceeded: boolean;
-  readonly isCollectionTokenPrefixLimitExceeded: boolean;
-  readonly isTotalCollectionsLimitExceeded: boolean;
-  readonly isCollectionAdminCountExceeded: boolean;
-  readonly isCollectionLimitBoundsExceeded: boolean;
-  readonly isOwnerPermissionsCantBeReverted: boolean;
-  readonly isTransferNotAllowed: boolean;
-  readonly isAccountTokenLimitExceeded: boolean;
-  readonly isCollectionTokenLimitExceeded: boolean;
-  readonly isMetadataFlagFrozen: boolean;
-  readonly isTokenNotFound: boolean;
-  readonly isTokenValueTooLow: boolean;
-  readonly isApprovedValueTooLow: boolean;
-  readonly isCantApproveMoreThanOwned: boolean;
-  readonly isAddressIsNotEthMirror: boolean;
-  readonly isAddressIsZero: boolean;
-  readonly isUnsupportedOperation: boolean;
-  readonly isNotSufficientFounds: boolean;
-  readonly isUserIsNotAllowedToNest: boolean;
-  readonly isSourceCollectionIsNotAllowedToNest: boolean;
-  readonly isCollectionFieldSizeExceeded: boolean;
-  readonly isNoSpaceForProperty: boolean;
-  readonly isPropertyLimitReached: boolean;
-  readonly isPropertyKeyIsTooLong: boolean;
-  readonly isInvalidCharacterInPropertyKey: boolean;
-  readonly isEmptyPropertyKey: boolean;
-  readonly isCollectionIsExternal: boolean;
-  readonly isCollectionIsInternal: boolean;
-  readonly isConfirmSponsorshipFail: boolean;
-  readonly isUserIsNotCollectionAdmin: boolean;
-  readonly isFungibleItemsHaveNoId: boolean;
-  readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId';
-}
-
-/** @name PalletCommonEvent */
-export interface PalletCommonEvent extends Enum {
-  readonly isCollectionCreated: boolean;
-  readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>;
-  readonly isCollectionDestroyed: boolean;
-  readonly asCollectionDestroyed: u32;
-  readonly isItemCreated: boolean;
-  readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-  readonly isItemDestroyed: boolean;
-  readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-  readonly isTransfer: boolean;
-  readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-  readonly isApproved: boolean;
-  readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-  readonly isApprovedForAll: boolean;
-  readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
-  readonly isCollectionPropertySet: boolean;
-  readonly asCollectionPropertySet: ITuple<[u32, Bytes]>;
-  readonly isCollectionPropertyDeleted: boolean;
-  readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>;
-  readonly isTokenPropertySet: boolean;
-  readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>;
-  readonly isTokenPropertyDeleted: boolean;
-  readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>;
-  readonly isPropertyPermissionSet: boolean;
-  readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>;
-  readonly isAllowListAddressAdded: boolean;
-  readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-  readonly isAllowListAddressRemoved: boolean;
-  readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-  readonly isCollectionAdminAdded: boolean;
-  readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-  readonly isCollectionAdminRemoved: boolean;
-  readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-  readonly isCollectionLimitSet: boolean;
-  readonly asCollectionLimitSet: u32;
-  readonly isCollectionOwnerChanged: boolean;
-  readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>;
-  readonly isCollectionPermissionSet: boolean;
-  readonly asCollectionPermissionSet: u32;
-  readonly isCollectionSponsorSet: boolean;
-  readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>;
-  readonly isSponsorshipConfirmed: boolean;
-  readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>;
-  readonly isCollectionSponsorRemoved: boolean;
-  readonly asCollectionSponsorRemoved: u32;
-  readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved';
-}
-
-/** @name PalletConfigurationAppPromotionConfiguration */
-export interface PalletConfigurationAppPromotionConfiguration extends Struct {
-  readonly recalculationInterval: Option<u32>;
-  readonly pendingInterval: Option<u32>;
-  readonly intervalIncome: Option<Perbill>;
-  readonly maxStakersPerCalculation: Option<u8>;
-}
-
-/** @name PalletConfigurationCall */
-export interface PalletConfigurationCall extends Enum {
-  readonly isSetWeightToFeeCoefficientOverride: boolean;
-  readonly asSetWeightToFeeCoefficientOverride: {
-    readonly coeff: Option<u64>;
-  } & Struct;
-  readonly isSetMinGasPriceOverride: boolean;
-  readonly asSetMinGasPriceOverride: {
-    readonly coeff: Option<u64>;
-  } & Struct;
-  readonly isSetAppPromotionConfigurationOverride: boolean;
-  readonly asSetAppPromotionConfigurationOverride: {
-    readonly configuration: PalletConfigurationAppPromotionConfiguration;
-  } & Struct;
-  readonly isSetCollatorSelectionDesiredCollators: boolean;
-  readonly asSetCollatorSelectionDesiredCollators: {
-    readonly max: Option<u32>;
-  } & Struct;
-  readonly isSetCollatorSelectionLicenseBond: boolean;
-  readonly asSetCollatorSelectionLicenseBond: {
-    readonly amount: Option<u128>;
-  } & Struct;
-  readonly isSetCollatorSelectionKickThreshold: boolean;
-  readonly asSetCollatorSelectionKickThreshold: {
-    readonly threshold: Option<u32>;
-  } & Struct;
-  readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold';
-}
-
-/** @name PalletConfigurationError */
-export interface PalletConfigurationError extends Enum {
-  readonly isInconsistentConfiguration: boolean;
-  readonly type: 'InconsistentConfiguration';
-}
-
-/** @name PalletConfigurationEvent */
-export interface PalletConfigurationEvent extends Enum {
-  readonly isNewDesiredCollators: boolean;
-  readonly asNewDesiredCollators: {
-    readonly desiredCollators: Option<u32>;
-  } & Struct;
-  readonly isNewCollatorLicenseBond: boolean;
-  readonly asNewCollatorLicenseBond: {
-    readonly bondCost: Option<u128>;
-  } & Struct;
-  readonly isNewCollatorKickThreshold: boolean;
-  readonly asNewCollatorKickThreshold: {
-    readonly lengthInBlocks: Option<u32>;
-  } & Struct;
-  readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold';
-}
-
-/** @name PalletDemocracyCall */
-export interface PalletDemocracyCall extends Enum {
-  readonly isPropose: boolean;
-  readonly asPropose: {
-    readonly proposal: FrameSupportPreimagesBounded;
-    readonly value: Compact<u128>;
-  } & Struct;
-  readonly isSecond: boolean;
-  readonly asSecond: {
-    readonly proposal: Compact<u32>;
-  } & Struct;
-  readonly isVote: boolean;
-  readonly asVote: {
-    readonly refIndex: Compact<u32>;
-    readonly vote: PalletDemocracyVoteAccountVote;
-  } & Struct;
-  readonly isEmergencyCancel: boolean;
-  readonly asEmergencyCancel: {
-    readonly refIndex: u32;
-  } & Struct;
-  readonly isExternalPropose: boolean;
-  readonly asExternalPropose: {
-    readonly proposal: FrameSupportPreimagesBounded;
-  } & Struct;
-  readonly isExternalProposeMajority: boolean;
-  readonly asExternalProposeMajority: {
-    readonly proposal: FrameSupportPreimagesBounded;
-  } & Struct;
-  readonly isExternalProposeDefault: boolean;
-  readonly asExternalProposeDefault: {
-    readonly proposal: FrameSupportPreimagesBounded;
-  } & Struct;
-  readonly isFastTrack: boolean;
-  readonly asFastTrack: {
-    readonly proposalHash: H256;
-    readonly votingPeriod: u32;
-    readonly delay: u32;
-  } & Struct;
-  readonly isVetoExternal: boolean;
-  readonly asVetoExternal: {
-    readonly proposalHash: H256;
-  } & Struct;
-  readonly isCancelReferendum: boolean;
-  readonly asCancelReferendum: {
-    readonly refIndex: Compact<u32>;
-  } & Struct;
-  readonly isDelegate: boolean;
-  readonly asDelegate: {
-    readonly to: MultiAddress;
-    readonly conviction: PalletDemocracyConviction;
-    readonly balance: u128;
-  } & Struct;
-  readonly isUndelegate: boolean;
-  readonly isClearPublicProposals: boolean;
-  readonly isUnlock: boolean;
-  readonly asUnlock: {
-    readonly target: MultiAddress;
-  } & Struct;
-  readonly isRemoveVote: boolean;
-  readonly asRemoveVote: {
-    readonly index: u32;
-  } & Struct;
-  readonly isRemoveOtherVote: boolean;
-  readonly asRemoveOtherVote: {
-    readonly target: MultiAddress;
-    readonly index: u32;
-  } & Struct;
-  readonly isBlacklist: boolean;
-  readonly asBlacklist: {
-    readonly proposalHash: H256;
-    readonly maybeRefIndex: Option<u32>;
-  } & Struct;
-  readonly isCancelProposal: boolean;
-  readonly asCancelProposal: {
-    readonly propIndex: Compact<u32>;
-  } & Struct;
-  readonly isSetMetadata: boolean;
-  readonly asSetMetadata: {
-    readonly owner: PalletDemocracyMetadataOwner;
-    readonly maybeHash: Option<H256>;
-  } & Struct;
-  readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata';
-}
-
-/** @name PalletDemocracyConviction */
-export interface PalletDemocracyConviction extends Enum {
-  readonly isNone: boolean;
-  readonly isLocked1x: boolean;
-  readonly isLocked2x: boolean;
-  readonly isLocked3x: boolean;
-  readonly isLocked4x: boolean;
-  readonly isLocked5x: boolean;
-  readonly isLocked6x: boolean;
-  readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x';
-}
-
-/** @name PalletDemocracyDelegations */
-export interface PalletDemocracyDelegations extends Struct {
-  readonly votes: u128;
-  readonly capital: u128;
-}
-
-/** @name PalletDemocracyError */
-export interface PalletDemocracyError extends Enum {
-  readonly isValueLow: boolean;
-  readonly isProposalMissing: boolean;
-  readonly isAlreadyCanceled: boolean;
-  readonly isDuplicateProposal: boolean;
-  readonly isProposalBlacklisted: boolean;
-  readonly isNotSimpleMajority: boolean;
-  readonly isInvalidHash: boolean;
-  readonly isNoProposal: boolean;
-  readonly isAlreadyVetoed: boolean;
-  readonly isReferendumInvalid: boolean;
-  readonly isNoneWaiting: boolean;
-  readonly isNotVoter: boolean;
-  readonly isNoPermission: boolean;
-  readonly isAlreadyDelegating: boolean;
-  readonly isInsufficientFunds: boolean;
-  readonly isNotDelegating: boolean;
-  readonly isVotesExist: boolean;
-  readonly isInstantNotAllowed: boolean;
-  readonly isNonsense: boolean;
-  readonly isWrongUpperBound: boolean;
-  readonly isMaxVotesReached: boolean;
-  readonly isTooMany: boolean;
-  readonly isVotingPeriodLow: boolean;
-  readonly isPreimageNotExist: boolean;
-  readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist';
-}
-
-/** @name PalletDemocracyEvent */
-export interface PalletDemocracyEvent extends Enum {
-  readonly isProposed: boolean;
-  readonly asProposed: {
-    readonly proposalIndex: u32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isTabled: boolean;
-  readonly asTabled: {
-    readonly proposalIndex: u32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isExternalTabled: boolean;
-  readonly isStarted: boolean;
-  readonly asStarted: {
-    readonly refIndex: u32;
-    readonly threshold: PalletDemocracyVoteThreshold;
-  } & Struct;
-  readonly isPassed: boolean;
-  readonly asPassed: {
-    readonly refIndex: u32;
-  } & Struct;
-  readonly isNotPassed: boolean;
-  readonly asNotPassed: {
-    readonly refIndex: u32;
-  } & Struct;
-  readonly isCancelled: boolean;
-  readonly asCancelled: {
-    readonly refIndex: u32;
-  } & Struct;
-  readonly isDelegated: boolean;
-  readonly asDelegated: {
-    readonly who: AccountId32;
-    readonly target: AccountId32;
-  } & Struct;
-  readonly isUndelegated: boolean;
-  readonly asUndelegated: {
-    readonly account: AccountId32;
-  } & Struct;
-  readonly isVetoed: boolean;
-  readonly asVetoed: {
-    readonly who: AccountId32;
-    readonly proposalHash: H256;
-    readonly until: u32;
-  } & Struct;
-  readonly isBlacklisted: boolean;
-  readonly asBlacklisted: {
-    readonly proposalHash: H256;
-  } & Struct;
-  readonly isVoted: boolean;
-  readonly asVoted: {
-    readonly voter: AccountId32;
-    readonly refIndex: u32;
-    readonly vote: PalletDemocracyVoteAccountVote;
-  } & Struct;
-  readonly isSeconded: boolean;
-  readonly asSeconded: {
-    readonly seconder: AccountId32;
-    readonly propIndex: u32;
-  } & Struct;
-  readonly isProposalCanceled: boolean;
-  readonly asProposalCanceled: {
-    readonly propIndex: u32;
-  } & Struct;
-  readonly isMetadataSet: boolean;
-  readonly asMetadataSet: {
-    readonly owner: PalletDemocracyMetadataOwner;
-    readonly hash_: H256;
-  } & Struct;
-  readonly isMetadataCleared: boolean;
-  readonly asMetadataCleared: {
-    readonly owner: PalletDemocracyMetadataOwner;
-    readonly hash_: H256;
-  } & Struct;
-  readonly isMetadataTransferred: boolean;
-  readonly asMetadataTransferred: {
-    readonly prevOwner: PalletDemocracyMetadataOwner;
-    readonly owner: PalletDemocracyMetadataOwner;
-    readonly hash_: H256;
-  } & Struct;
-  readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred';
-}
-
-/** @name PalletDemocracyMetadataOwner */
-export interface PalletDemocracyMetadataOwner extends Enum {
-  readonly isExternal: boolean;
-  readonly isProposal: boolean;
-  readonly asProposal: u32;
-  readonly isReferendum: boolean;
-  readonly asReferendum: u32;
-  readonly type: 'External' | 'Proposal' | 'Referendum';
-}
-
-/** @name PalletDemocracyReferendumInfo */
-export interface PalletDemocracyReferendumInfo extends Enum {
-  readonly isOngoing: boolean;
-  readonly asOngoing: PalletDemocracyReferendumStatus;
-  readonly isFinished: boolean;
-  readonly asFinished: {
-    readonly approved: bool;
-    readonly end: u32;
-  } & Struct;
-  readonly type: 'Ongoing' | 'Finished';
-}
-
-/** @name PalletDemocracyReferendumStatus */
-export interface PalletDemocracyReferendumStatus extends Struct {
-  readonly end: u32;
-  readonly proposal: FrameSupportPreimagesBounded;
-  readonly threshold: PalletDemocracyVoteThreshold;
-  readonly delay: u32;
-  readonly tally: PalletDemocracyTally;
-}
-
-/** @name PalletDemocracyTally */
-export interface PalletDemocracyTally extends Struct {
-  readonly ayes: u128;
-  readonly nays: u128;
-  readonly turnout: u128;
-}
-
-/** @name PalletDemocracyVoteAccountVote */
-export interface PalletDemocracyVoteAccountVote extends Enum {
-  readonly isStandard: boolean;
-  readonly asStandard: {
-    readonly vote: Vote;
-    readonly balance: u128;
-  } & Struct;
-  readonly isSplit: boolean;
-  readonly asSplit: {
-    readonly aye: u128;
-    readonly nay: u128;
-  } & Struct;
-  readonly type: 'Standard' | 'Split';
-}
-
-/** @name PalletDemocracyVotePriorLock */
-export interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {}
-
-/** @name PalletDemocracyVoteThreshold */
-export interface PalletDemocracyVoteThreshold extends Enum {
-  readonly isSuperMajorityApprove: boolean;
-  readonly isSuperMajorityAgainst: boolean;
-  readonly isSimpleMajority: boolean;
-  readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority';
-}
-
-/** @name PalletDemocracyVoteVoting */
-export interface PalletDemocracyVoteVoting extends Enum {
-  readonly isDirect: boolean;
-  readonly asDirect: {
-    readonly votes: Vec<ITuple<[u32, PalletDemocracyVoteAccountVote]>>;
-    readonly delegations: PalletDemocracyDelegations;
-    readonly prior: PalletDemocracyVotePriorLock;
-  } & Struct;
-  readonly isDelegating: boolean;
-  readonly asDelegating: {
-    readonly balance: u128;
-    readonly target: AccountId32;
-    readonly conviction: PalletDemocracyConviction;
-    readonly delegations: PalletDemocracyDelegations;
-    readonly prior: PalletDemocracyVotePriorLock;
-  } & Struct;
-  readonly type: 'Direct' | 'Delegating';
-}
-
-/** @name PalletEthereumCall */
-export interface PalletEthereumCall extends Enum {
-  readonly isTransact: boolean;
-  readonly asTransact: {
-    readonly transaction: EthereumTransactionTransactionV2;
-  } & Struct;
-  readonly type: 'Transact';
-}
-
-/** @name PalletEthereumError */
-export interface PalletEthereumError extends Enum {
-  readonly isInvalidSignature: boolean;
-  readonly isPreLogExists: boolean;
-  readonly type: 'InvalidSignature' | 'PreLogExists';
-}
-
-/** @name PalletEthereumEvent */
-export interface PalletEthereumEvent extends Enum {
-  readonly isExecuted: boolean;
-  readonly asExecuted: {
-    readonly from: H160;
-    readonly to: H160;
-    readonly transactionHash: H256;
-    readonly exitReason: EvmCoreErrorExitReason;
-    readonly extraData: Bytes;
-  } & Struct;
-  readonly type: 'Executed';
-}
-
-/** @name PalletEthereumFakeTransactionFinalizer */
-export interface PalletEthereumFakeTransactionFinalizer extends Null {}
-
-/** @name PalletEthereumRawOrigin */
-export interface PalletEthereumRawOrigin extends Enum {
-  readonly isEthereumTransaction: boolean;
-  readonly asEthereumTransaction: H160;
-  readonly type: 'EthereumTransaction';
-}
-
-/** @name PalletEvmAccountBasicCrossAccountIdRepr */
-export interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum {
-  readonly isSubstrate: boolean;
-  readonly asSubstrate: AccountId32;
-  readonly isEthereum: boolean;
-  readonly asEthereum: H160;
-  readonly type: 'Substrate' | 'Ethereum';
-}
-
-/** @name PalletEvmCall */
-export interface PalletEvmCall extends Enum {
-  readonly isWithdraw: boolean;
-  readonly asWithdraw: {
-    readonly address: H160;
-    readonly value: u128;
-  } & Struct;
-  readonly isCall: boolean;
-  readonly asCall: {
-    readonly source: H160;
-    readonly target: H160;
-    readonly input: Bytes;
-    readonly value: U256;
-    readonly gasLimit: u64;
-    readonly maxFeePerGas: U256;
-    readonly maxPriorityFeePerGas: Option<U256>;
-    readonly nonce: Option<U256>;
-    readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
-  } & Struct;
-  readonly isCreate: boolean;
-  readonly asCreate: {
-    readonly source: H160;
-    readonly init: Bytes;
-    readonly value: U256;
-    readonly gasLimit: u64;
-    readonly maxFeePerGas: U256;
-    readonly maxPriorityFeePerGas: Option<U256>;
-    readonly nonce: Option<U256>;
-    readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
-  } & Struct;
-  readonly isCreate2: boolean;
-  readonly asCreate2: {
-    readonly source: H160;
-    readonly init: Bytes;
-    readonly salt: H256;
-    readonly value: U256;
-    readonly gasLimit: u64;
-    readonly maxFeePerGas: U256;
-    readonly maxPriorityFeePerGas: Option<U256>;
-    readonly nonce: Option<U256>;
-    readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
-  } & Struct;
-  readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
-}
-
-/** @name PalletEvmCodeMetadata */
-export interface PalletEvmCodeMetadata extends Struct {
-  readonly size_: u64;
-  readonly hash_: H256;
-}
-
-/** @name PalletEvmCoderSubstrateError */
-export interface PalletEvmCoderSubstrateError extends Enum {
-  readonly isOutOfGas: boolean;
-  readonly isOutOfFund: boolean;
-  readonly type: 'OutOfGas' | 'OutOfFund';
-}
-
-/** @name PalletEvmContractHelpersCall */
-export interface PalletEvmContractHelpersCall extends Enum {
-  readonly isMigrateFromSelfSponsoring: boolean;
-  readonly asMigrateFromSelfSponsoring: {
-    readonly addresses: Vec<H160>;
-  } & Struct;
-  readonly type: 'MigrateFromSelfSponsoring';
-}
-
-/** @name PalletEvmContractHelpersError */
-export interface PalletEvmContractHelpersError extends Enum {
-  readonly isNoPermission: boolean;
-  readonly isNoPendingSponsor: boolean;
-  readonly isTooManyMethodsHaveSponsoredLimit: boolean;
-  readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit';
-}
-
-/** @name PalletEvmContractHelpersEvent */
-export interface PalletEvmContractHelpersEvent extends Enum {
-  readonly isContractSponsorSet: boolean;
-  readonly asContractSponsorSet: ITuple<[H160, AccountId32]>;
-  readonly isContractSponsorshipConfirmed: boolean;
-  readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>;
-  readonly isContractSponsorRemoved: boolean;
-  readonly asContractSponsorRemoved: H160;
-  readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved';
-}
-
-/** @name PalletEvmContractHelpersSponsoringModeT */
-export interface PalletEvmContractHelpersSponsoringModeT extends Enum {
-  readonly isDisabled: boolean;
-  readonly isAllowlisted: boolean;
-  readonly isGenerous: boolean;
-  readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
-}
-
-/** @name PalletEvmError */
-export interface PalletEvmError extends Enum {
-  readonly isBalanceLow: boolean;
-  readonly isFeeOverflow: boolean;
-  readonly isPaymentOverflow: boolean;
-  readonly isWithdrawFailed: boolean;
-  readonly isGasPriceTooLow: boolean;
-  readonly isInvalidNonce: boolean;
-  readonly isGasLimitTooLow: boolean;
-  readonly isGasLimitTooHigh: boolean;
-  readonly isUndefined: boolean;
-  readonly isReentrancy: boolean;
-  readonly isTransactionMustComeFromEOA: boolean;
-  readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA';
-}
-
-/** @name PalletEvmEvent */
-export interface PalletEvmEvent extends Enum {
-  readonly isLog: boolean;
-  readonly asLog: {
-    readonly log: EthereumLog;
-  } & Struct;
-  readonly isCreated: boolean;
-  readonly asCreated: {
-    readonly address: H160;
-  } & Struct;
-  readonly isCreatedFailed: boolean;
-  readonly asCreatedFailed: {
-    readonly address: H160;
-  } & Struct;
-  readonly isExecuted: boolean;
-  readonly asExecuted: {
-    readonly address: H160;
-  } & Struct;
-  readonly isExecutedFailed: boolean;
-  readonly asExecutedFailed: {
-    readonly address: H160;
-  } & Struct;
-  readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed';
-}
-
-/** @name PalletEvmMigrationCall */
-export interface PalletEvmMigrationCall extends Enum {
-  readonly isBegin: boolean;
-  readonly asBegin: {
-    readonly address: H160;
-  } & Struct;
-  readonly isSetData: boolean;
-  readonly asSetData: {
-    readonly address: H160;
-    readonly data: Vec<ITuple<[H256, H256]>>;
-  } & Struct;
-  readonly isFinish: boolean;
-  readonly asFinish: {
-    readonly address: H160;
-    readonly code: Bytes;
-  } & Struct;
-  readonly isInsertEthLogs: boolean;
-  readonly asInsertEthLogs: {
-    readonly logs: Vec<EthereumLog>;
-  } & Struct;
-  readonly isInsertEvents: boolean;
-  readonly asInsertEvents: {
-    readonly events: Vec<Bytes>;
-  } & Struct;
-  readonly isRemoveRmrkData: boolean;
-  readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData';
-}
-
-/** @name PalletEvmMigrationError */
-export interface PalletEvmMigrationError extends Enum {
-  readonly isAccountNotEmpty: boolean;
-  readonly isAccountIsNotMigrating: boolean;
-  readonly isBadEvent: boolean;
-  readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent';
-}
-
-/** @name PalletEvmMigrationEvent */
-export interface PalletEvmMigrationEvent extends Enum {
-  readonly isTestEvent: boolean;
-  readonly type: 'TestEvent';
-}
-
-/** @name PalletForeignAssetsAssetId */
-export interface PalletForeignAssetsAssetId extends Enum {
-  readonly isForeignAssetId: boolean;
-  readonly asForeignAssetId: u32;
-  readonly isNativeAssetId: boolean;
-  readonly asNativeAssetId: PalletForeignAssetsNativeCurrency;
-  readonly type: 'ForeignAssetId' | 'NativeAssetId';
-}
-
-/** @name PalletForeignAssetsModuleAssetMetadata */
-export interface PalletForeignAssetsModuleAssetMetadata extends Struct {
-  readonly name: Bytes;
-  readonly symbol: Bytes;
-  readonly decimals: u8;
-  readonly minimalBalance: u128;
-}
-
-/** @name PalletForeignAssetsModuleCall */
-export interface PalletForeignAssetsModuleCall extends Enum {
-  readonly isRegisterForeignAsset: boolean;
-  readonly asRegisterForeignAsset: {
-    readonly owner: AccountId32;
-    readonly location: StagingXcmVersionedMultiLocation;
-    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-  } & Struct;
-  readonly isUpdateForeignAsset: boolean;
-  readonly asUpdateForeignAsset: {
-    readonly foreignAssetId: u32;
-    readonly location: StagingXcmVersionedMultiLocation;
-    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-  } & Struct;
-  readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset';
-}
-
-/** @name PalletForeignAssetsModuleError */
-export interface PalletForeignAssetsModuleError extends Enum {
-  readonly isBadLocation: boolean;
-  readonly isMultiLocationExisted: boolean;
-  readonly isAssetIdNotExists: boolean;
-  readonly isAssetIdExisted: boolean;
-  readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted';
-}
-
-/** @name PalletForeignAssetsModuleEvent */
-export interface PalletForeignAssetsModuleEvent extends Enum {
-  readonly isForeignAssetRegistered: boolean;
-  readonly asForeignAssetRegistered: {
-    readonly assetId: u32;
-    readonly assetAddress: StagingXcmV3MultiLocation;
-    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-  } & Struct;
-  readonly isForeignAssetUpdated: boolean;
-  readonly asForeignAssetUpdated: {
-    readonly assetId: u32;
-    readonly assetAddress: StagingXcmV3MultiLocation;
-    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-  } & Struct;
-  readonly isAssetRegistered: boolean;
-  readonly asAssetRegistered: {
-    readonly assetId: PalletForeignAssetsAssetId;
-    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-  } & Struct;
-  readonly isAssetUpdated: boolean;
-  readonly asAssetUpdated: {
-    readonly assetId: PalletForeignAssetsAssetId;
-    readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-  } & Struct;
-  readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated';
-}
-
-/** @name PalletForeignAssetsNativeCurrency */
-export interface PalletForeignAssetsNativeCurrency extends Enum {
-  readonly isHere: boolean;
-  readonly isParent: boolean;
-  readonly type: 'Here' | 'Parent';
-}
-
-/** @name PalletFungibleError */
-export interface PalletFungibleError extends Enum {
-  readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
-  readonly isFungibleItemsDontHaveData: boolean;
-  readonly isFungibleDisallowsNesting: boolean;
-  readonly isSettingPropertiesNotAllowed: boolean;
-  readonly isSettingAllowanceForAllNotAllowed: boolean;
-  readonly isFungibleTokensAreAlwaysValid: boolean;
-  readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid';
-}
-
-/** @name PalletGovOriginsOrigin */
-export interface PalletGovOriginsOrigin extends Enum {
-  readonly isFellowshipProposition: boolean;
-  readonly type: 'FellowshipProposition';
-}
-
-/** @name PalletIdentityBitFlags */
-export interface PalletIdentityBitFlags extends Struct {
-  readonly _bitLength: 64;
-  readonly Display: 1;
-  readonly Legal: 2;
-  readonly Web: 4;
-  readonly Riot: 8;
-  readonly Email: 16;
-  readonly PgpFingerprint: 32;
-  readonly Image: 64;
-  readonly Twitter: 128;
-}
-
-/** @name PalletIdentityCall */
-export interface PalletIdentityCall extends Enum {
-  readonly isAddRegistrar: boolean;
-  readonly asAddRegistrar: {
-    readonly account: MultiAddress;
-  } & Struct;
-  readonly isSetIdentity: boolean;
-  readonly asSetIdentity: {
-    readonly info: PalletIdentityIdentityInfo;
-  } & Struct;
-  readonly isSetSubs: boolean;
-  readonly asSetSubs: {
-    readonly subs: Vec<ITuple<[AccountId32, Data]>>;
-  } & Struct;
-  readonly isClearIdentity: boolean;
-  readonly isRequestJudgement: boolean;
-  readonly asRequestJudgement: {
-    readonly regIndex: Compact<u32>;
-    readonly maxFee: Compact<u128>;
-  } & Struct;
-  readonly isCancelRequest: boolean;
-  readonly asCancelRequest: {
-    readonly regIndex: u32;
-  } & Struct;
-  readonly isSetFee: boolean;
-  readonly asSetFee: {
-    readonly index: Compact<u32>;
-    readonly fee: Compact<u128>;
-  } & Struct;
-  readonly isSetAccountId: boolean;
-  readonly asSetAccountId: {
-    readonly index: Compact<u32>;
-    readonly new_: MultiAddress;
-  } & Struct;
-  readonly isSetFields: boolean;
-  readonly asSetFields: {
-    readonly index: Compact<u32>;
-    readonly fields: PalletIdentityBitFlags;
-  } & Struct;
-  readonly isProvideJudgement: boolean;
-  readonly asProvideJudgement: {
-    readonly regIndex: Compact<u32>;
-    readonly target: MultiAddress;
-    readonly judgement: PalletIdentityJudgement;
-    readonly identity: H256;
-  } & Struct;
-  readonly isKillIdentity: boolean;
-  readonly asKillIdentity: {
-    readonly target: MultiAddress;
-  } & Struct;
-  readonly isAddSub: boolean;
-  readonly asAddSub: {
-    readonly sub: MultiAddress;
-    readonly data: Data;
-  } & Struct;
-  readonly isRenameSub: boolean;
-  readonly asRenameSub: {
-    readonly sub: MultiAddress;
-    readonly data: Data;
-  } & Struct;
-  readonly isRemoveSub: boolean;
-  readonly asRemoveSub: {
-    readonly sub: MultiAddress;
-  } & Struct;
-  readonly isQuitSub: boolean;
-  readonly isForceInsertIdentities: boolean;
-  readonly asForceInsertIdentities: {
-    readonly identities: Vec<ITuple<[AccountId32, PalletIdentityRegistration]>>;
-  } & Struct;
-  readonly isForceRemoveIdentities: boolean;
-  readonly asForceRemoveIdentities: {
-    readonly identities: Vec<AccountId32>;
-  } & Struct;
-  readonly isForceSetSubs: boolean;
-  readonly asForceSetSubs: {
-    readonly subs: Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>>;
-  } & Struct;
-  readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs';
-}
-
-/** @name PalletIdentityError */
-export interface PalletIdentityError extends Enum {
-  readonly isTooManySubAccounts: boolean;
-  readonly isNotFound: boolean;
-  readonly isNotNamed: boolean;
-  readonly isEmptyIndex: boolean;
-  readonly isFeeChanged: boolean;
-  readonly isNoIdentity: boolean;
-  readonly isStickyJudgement: boolean;
-  readonly isJudgementGiven: boolean;
-  readonly isInvalidJudgement: boolean;
-  readonly isInvalidIndex: boolean;
-  readonly isInvalidTarget: boolean;
-  readonly isTooManyFields: boolean;
-  readonly isTooManyRegistrars: boolean;
-  readonly isAlreadyClaimed: boolean;
-  readonly isNotSub: boolean;
-  readonly isNotOwned: boolean;
-  readonly isJudgementForDifferentIdentity: boolean;
-  readonly isJudgementPaymentFailed: boolean;
-  readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed';
-}
-
-/** @name PalletIdentityEvent */
-export interface PalletIdentityEvent extends Enum {
-  readonly isIdentitySet: boolean;
-  readonly asIdentitySet: {
-    readonly who: AccountId32;
-  } & Struct;
-  readonly isIdentityCleared: boolean;
-  readonly asIdentityCleared: {
-    readonly who: AccountId32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isIdentityKilled: boolean;
-  readonly asIdentityKilled: {
-    readonly who: AccountId32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isIdentitiesInserted: boolean;
-  readonly asIdentitiesInserted: {
-    readonly amount: u32;
-  } & Struct;
-  readonly isIdentitiesRemoved: boolean;
-  readonly asIdentitiesRemoved: {
-    readonly amount: u32;
-  } & Struct;
-  readonly isJudgementRequested: boolean;
-  readonly asJudgementRequested: {
-    readonly who: AccountId32;
-    readonly registrarIndex: u32;
-  } & Struct;
-  readonly isJudgementUnrequested: boolean;
-  readonly asJudgementUnrequested: {
-    readonly who: AccountId32;
-    readonly registrarIndex: u32;
-  } & Struct;
-  readonly isJudgementGiven: boolean;
-  readonly asJudgementGiven: {
-    readonly target: AccountId32;
-    readonly registrarIndex: u32;
-  } & Struct;
-  readonly isRegistrarAdded: boolean;
-  readonly asRegistrarAdded: {
-    readonly registrarIndex: u32;
-  } & Struct;
-  readonly isSubIdentityAdded: boolean;
-  readonly asSubIdentityAdded: {
-    readonly sub: AccountId32;
-    readonly main: AccountId32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isSubIdentityRemoved: boolean;
-  readonly asSubIdentityRemoved: {
-    readonly sub: AccountId32;
-    readonly main: AccountId32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isSubIdentityRevoked: boolean;
-  readonly asSubIdentityRevoked: {
-    readonly sub: AccountId32;
-    readonly main: AccountId32;
-    readonly deposit: u128;
-  } & Struct;
-  readonly isSubIdentitiesInserted: boolean;
-  readonly asSubIdentitiesInserted: {
-    readonly amount: u32;
-  } & Struct;
-  readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted';
-}
-
-/** @name PalletIdentityIdentityField */
-export interface PalletIdentityIdentityField extends Enum {
-  readonly isDisplay: boolean;
-  readonly isLegal: boolean;
-  readonly isWeb: boolean;
-  readonly isRiot: boolean;
-  readonly isEmail: boolean;
-  readonly isPgpFingerprint: boolean;
-  readonly isImage: boolean;
-  readonly isTwitter: boolean;
-  readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter';
-}
-
-/** @name PalletIdentityIdentityInfo */
-export interface PalletIdentityIdentityInfo extends Struct {
-  readonly additional: Vec<ITuple<[Data, Data]>>;
-  readonly display: Data;
-  readonly legal: Data;
-  readonly web: Data;
-  readonly riot: Data;
-  readonly email: Data;
-  readonly pgpFingerprint: Option<U8aFixed>;
-  readonly image: Data;
-  readonly twitter: Data;
-}
-
-/** @name PalletIdentityJudgement */
-export interface PalletIdentityJudgement extends Enum {
-  readonly isUnknown: boolean;
-  readonly isFeePaid: boolean;
-  readonly asFeePaid: u128;
-  readonly isReasonable: boolean;
-  readonly isKnownGood: boolean;
-  readonly isOutOfDate: boolean;
-  readonly isLowQuality: boolean;
-  readonly isErroneous: boolean;
-  readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous';
-}
-
-/** @name PalletIdentityRegistrarInfo */
-export interface PalletIdentityRegistrarInfo extends Struct {
-  readonly account: AccountId32;
-  readonly fee: u128;
-  readonly fields: PalletIdentityBitFlags;
-}
-
-/** @name PalletIdentityRegistration */
-export interface PalletIdentityRegistration extends Struct {
-  readonly judgements: Vec<ITuple<[u32, PalletIdentityJudgement]>>;
-  readonly deposit: u128;
-  readonly info: PalletIdentityIdentityInfo;
-}
-
-/** @name PalletInflationCall */
-export interface PalletInflationCall extends Enum {
-  readonly isStartInflation: boolean;
-  readonly asStartInflation: {
-    readonly inflationStartRelayBlock: u32;
-  } & Struct;
-  readonly type: 'StartInflation';
-}
-
-/** @name PalletMaintenanceCall */
-export interface PalletMaintenanceCall extends Enum {
-  readonly isEnable: boolean;
-  readonly isDisable: boolean;
-  readonly type: 'Enable' | 'Disable';
-}
-
-/** @name PalletMaintenanceError */
-export interface PalletMaintenanceError extends Null {}
-
-/** @name PalletMaintenanceEvent */
-export interface PalletMaintenanceEvent extends Enum {
-  readonly isMaintenanceEnabled: boolean;
-  readonly isMaintenanceDisabled: boolean;
-  readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled';
-}
-
-/** @name PalletMembershipCall */
-export interface PalletMembershipCall extends Enum {
-  readonly isAddMember: boolean;
-  readonly asAddMember: {
-    readonly who: MultiAddress;
-  } & Struct;
-  readonly isRemoveMember: boolean;
-  readonly asRemoveMember: {
-    readonly who: MultiAddress;
-  } & Struct;
-  readonly isSwapMember: boolean;
-  readonly asSwapMember: {
-    readonly remove: MultiAddress;
-    readonly add: MultiAddress;
-  } & Struct;
-  readonly isResetMembers: boolean;
-  readonly asResetMembers: {
-    readonly members: Vec<AccountId32>;
-  } & Struct;
-  readonly isChangeKey: boolean;
-  readonly asChangeKey: {
-    readonly new_: MultiAddress;
-  } & Struct;
-  readonly isSetPrime: boolean;
-  readonly asSetPrime: {
-    readonly who: MultiAddress;
-  } & Struct;
-  readonly isClearPrime: boolean;
-  readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime';
-}
-
-/** @name PalletMembershipError */
-export interface PalletMembershipError extends Enum {
-  readonly isAlreadyMember: boolean;
-  readonly isNotMember: boolean;
-  readonly isTooManyMembers: boolean;
-  readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers';
-}
-
-/** @name PalletMembershipEvent */
-export interface PalletMembershipEvent extends Enum {
-  readonly isMemberAdded: boolean;
-  readonly isMemberRemoved: boolean;
-  readonly isMembersSwapped: boolean;
-  readonly isMembersReset: boolean;
-  readonly isKeyChanged: boolean;
-  readonly isDummy: boolean;
-  readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy';
-}
-
-/** @name PalletNonfungibleError */
-export interface PalletNonfungibleError extends Enum {
-  readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
-  readonly isNonfungibleItemsHaveNoAmount: boolean;
-  readonly isCantBurnNftWithChildren: boolean;
-  readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren';
-}
-
-/** @name PalletNonfungibleItemData */
-export interface PalletNonfungibleItemData extends Struct {
-  readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-}
-
-/** @name PalletPreimageCall */
-export interface PalletPreimageCall extends Enum {
-  readonly isNotePreimage: boolean;
-  readonly asNotePreimage: {
-    readonly bytes: Bytes;
-  } & Struct;
-  readonly isUnnotePreimage: boolean;
-  readonly asUnnotePreimage: {
-    readonly hash_: H256;
-  } & Struct;
-  readonly isRequestPreimage: boolean;
-  readonly asRequestPreimage: {
-    readonly hash_: H256;
-  } & Struct;
-  readonly isUnrequestPreimage: boolean;
-  readonly asUnrequestPreimage: {
-    readonly hash_: H256;
-  } & Struct;
-  readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage';
-}
-
-/** @name PalletPreimageError */
-export interface PalletPreimageError extends Enum {
-  readonly isTooBig: boolean;
-  readonly isAlreadyNoted: boolean;
-  readonly isNotAuthorized: boolean;
-  readonly isNotNoted: boolean;
-  readonly isRequested: boolean;
-  readonly isNotRequested: boolean;
-  readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested';
-}
-
-/** @name PalletPreimageEvent */
-export interface PalletPreimageEvent extends Enum {
-  readonly isNoted: boolean;
-  readonly asNoted: {
-    readonly hash_: H256;
-  } & Struct;
-  readonly isRequested: boolean;
-  readonly asRequested: {
-    readonly hash_: H256;
-  } & Struct;
-  readonly isCleared: boolean;
-  readonly asCleared: {
-    readonly hash_: H256;
-  } & Struct;
-  readonly type: 'Noted' | 'Requested' | 'Cleared';
-}
-
-/** @name PalletPreimageRequestStatus */
-export interface PalletPreimageRequestStatus extends Enum {
-  readonly isUnrequested: boolean;
-  readonly asUnrequested: {
-    readonly deposit: ITuple<[AccountId32, u128]>;
-    readonly len: u32;
-  } & Struct;
-  readonly isRequested: boolean;
-  readonly asRequested: {
-    readonly deposit: Option<ITuple<[AccountId32, u128]>>;
-    readonly count: u32;
-    readonly len: Option<u32>;
-  } & Struct;
-  readonly type: 'Unrequested' | 'Requested';
-}
-
-/** @name PalletRankedCollectiveCall */
-export interface PalletRankedCollectiveCall extends Enum {
-  readonly isAddMember: boolean;
-  readonly asAddMember: {
-    readonly who: MultiAddress;
-  } & Struct;
-  readonly isPromoteMember: boolean;
-  readonly asPromoteMember: {
-    readonly who: MultiAddress;
-  } & Struct;
-  readonly isDemoteMember: boolean;
-  readonly asDemoteMember: {
-    readonly who: MultiAddress;
-  } & Struct;
-  readonly isRemoveMember: boolean;
-  readonly asRemoveMember: {
-    readonly who: MultiAddress;
-    readonly minRank: u16;
-  } & Struct;
-  readonly isVote: boolean;
-  readonly asVote: {
-    readonly poll: u32;
-    readonly aye: bool;
-  } & Struct;
-  readonly isCleanupPoll: boolean;
-  readonly asCleanupPoll: {
-    readonly pollIndex: u32;
-    readonly max: u32;
-  } & Struct;
-  readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll';
-}
-
-/** @name PalletRankedCollectiveError */
-export interface PalletRankedCollectiveError extends Enum {
-  readonly isAlreadyMember: boolean;
-  readonly isNotMember: boolean;
-  readonly isNotPolling: boolean;
-  readonly isOngoing: boolean;
-  readonly isNoneRemaining: boolean;
-  readonly isCorruption: boolean;
-  readonly isRankTooLow: boolean;
-  readonly isInvalidWitness: boolean;
-  readonly isNoPermission: boolean;
-  readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission';
-}
-
-/** @name PalletRankedCollectiveEvent */
-export interface PalletRankedCollectiveEvent extends Enum {
-  readonly isMemberAdded: boolean;
-  readonly asMemberAdded: {
-    readonly who: AccountId32;
-  } & Struct;
-  readonly isRankChanged: boolean;
-  readonly asRankChanged: {
-    readonly who: AccountId32;
-    readonly rank: u16;
-  } & Struct;
-  readonly isMemberRemoved: boolean;
-  readonly asMemberRemoved: {
-    readonly who: AccountId32;
-    readonly rank: u16;
-  } & Struct;
-  readonly isVoted: boolean;
-  readonly asVoted: {
-    readonly who: AccountId32;
-    readonly poll: u32;
-    readonly vote: PalletRankedCollectiveVoteRecord;
-    readonly tally: PalletRankedCollectiveTally;
-  } & Struct;
-  readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted';
-}
-
-/** @name PalletRankedCollectiveMemberRecord */
-export interface PalletRankedCollectiveMemberRecord extends Struct {
-  readonly rank: u16;
-}
-
-/** @name PalletRankedCollectiveTally */
-export interface PalletRankedCollectiveTally extends Struct {
-  readonly bareAyes: u32;
-  readonly ayes: u32;
-  readonly nays: u32;
-}
-
-/** @name PalletRankedCollectiveVoteRecord */
-export interface PalletRankedCollectiveVoteRecord extends Enum {
-  readonly isAye: boolean;
-  readonly asAye: u32;
-  readonly isNay: boolean;
-  readonly asNay: u32;
-  readonly type: 'Aye' | 'Nay';
-}
-
-/** @name PalletReferendaCall */
-export interface PalletReferendaCall extends Enum {
-  readonly isSubmit: boolean;
-  readonly asSubmit: {
-    readonly proposalOrigin: OpalRuntimeOriginCaller;
-    readonly proposal: FrameSupportPreimagesBounded;
-    readonly enactmentMoment: FrameSupportScheduleDispatchTime;
-  } & Struct;
-  readonly isPlaceDecisionDeposit: boolean;
-  readonly asPlaceDecisionDeposit: {
-    readonly index: u32;
-  } & Struct;
-  readonly isRefundDecisionDeposit: boolean;
-  readonly asRefundDecisionDeposit: {
-    readonly index: u32;
-  } & Struct;
-  readonly isCancel: boolean;
-  readonly asCancel: {
-    readonly index: u32;
-  } & Struct;
-  readonly isKill: boolean;
-  readonly asKill: {
-    readonly index: u32;
-  } & Struct;
-  readonly isNudgeReferendum: boolean;
-  readonly asNudgeReferendum: {
-    readonly index: u32;
-  } & Struct;
-  readonly isOneFewerDeciding: boolean;
-  readonly asOneFewerDeciding: {
-    readonly track: u16;
-  } & Struct;
-  readonly isRefundSubmissionDeposit: boolean;
-  readonly asRefundSubmissionDeposit: {
-    readonly index: u32;
-  } & Struct;
-  readonly isSetMetadata: boolean;
-  readonly asSetMetadata: {
-    readonly index: u32;
-    readonly maybeHash: Option<H256>;
-  } & Struct;
-  readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata';
-}
-
-/** @name PalletReferendaCurve */
-export interface PalletReferendaCurve extends Enum {
-  readonly isLinearDecreasing: boolean;
-  readonly asLinearDecreasing: {
-    readonly length: Perbill;
-    readonly floor: Perbill;
-    readonly ceil: Perbill;
-  } & Struct;
-  readonly isSteppedDecreasing: boolean;
-  readonly asSteppedDecreasing: {
-    readonly begin: Perbill;
-    readonly end: Perbill;
-    readonly step: Perbill;
-    readonly period: Perbill;
-  } & Struct;
-  readonly isReciprocal: boolean;
-  readonly asReciprocal: {
-    readonly factor: i64;
-    readonly xOffset: i64;
-    readonly yOffset: i64;
-  } & Struct;
-  readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal';
-}
-
-/** @name PalletReferendaDecidingStatus */
-export interface PalletReferendaDecidingStatus extends Struct {
-  readonly since: u32;
-  readonly confirming: Option<u32>;
-}
-
-/** @name PalletReferendaDeposit */
-export interface PalletReferendaDeposit extends Struct {
-  readonly who: AccountId32;
-  readonly amount: u128;
-}
-
-/** @name PalletReferendaError */
-export interface PalletReferendaError extends Enum {
-  readonly isNotOngoing: boolean;
-  readonly isHasDeposit: boolean;
-  readonly isBadTrack: boolean;
-  readonly isFull: boolean;
-  readonly isQueueEmpty: boolean;
-  readonly isBadReferendum: boolean;
-  readonly isNothingToDo: boolean;
-  readonly isNoTrack: boolean;
-  readonly isUnfinished: boolean;
-  readonly isNoPermission: boolean;
-  readonly isNoDeposit: boolean;
-  readonly isBadStatus: boolean;
-  readonly isPreimageNotExist: boolean;
-  readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist';
-}
-
-/** @name PalletReferendaEvent */
-export interface PalletReferendaEvent extends Enum {
-  readonly isSubmitted: boolean;
-  readonly asSubmitted: {
-    readonly index: u32;
-    readonly track: u16;
-    readonly proposal: FrameSupportPreimagesBounded;
-  } & Struct;
-  readonly isDecisionDepositPlaced: boolean;
-  readonly asDecisionDepositPlaced: {
-    readonly index: u32;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isDecisionDepositRefunded: boolean;
-  readonly asDecisionDepositRefunded: {
-    readonly index: u32;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isDepositSlashed: boolean;
-  readonly asDepositSlashed: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isDecisionStarted: boolean;
-  readonly asDecisionStarted: {
-    readonly index: u32;
-    readonly track: u16;
-    readonly proposal: FrameSupportPreimagesBounded;
-    readonly tally: PalletRankedCollectiveTally;
-  } & Struct;
-  readonly isConfirmStarted: boolean;
-  readonly asConfirmStarted: {
-    readonly index: u32;
-  } & Struct;
-  readonly isConfirmAborted: boolean;
-  readonly asConfirmAborted: {
-    readonly index: u32;
-  } & Struct;
-  readonly isConfirmed: boolean;
-  readonly asConfirmed: {
-    readonly index: u32;
-    readonly tally: PalletRankedCollectiveTally;
-  } & Struct;
-  readonly isApproved: boolean;
-  readonly asApproved: {
-    readonly index: u32;
-  } & Struct;
-  readonly isRejected: boolean;
-  readonly asRejected: {
-    readonly index: u32;
-    readonly tally: PalletRankedCollectiveTally;
-  } & Struct;
-  readonly isTimedOut: boolean;
-  readonly asTimedOut: {
-    readonly index: u32;
-    readonly tally: PalletRankedCollectiveTally;
-  } & Struct;
-  readonly isCancelled: boolean;
-  readonly asCancelled: {
-    readonly index: u32;
-    readonly tally: PalletRankedCollectiveTally;
-  } & Struct;
-  readonly isKilled: boolean;
-  readonly asKilled: {
-    readonly index: u32;
-    readonly tally: PalletRankedCollectiveTally;
-  } & Struct;
-  readonly isSubmissionDepositRefunded: boolean;
-  readonly asSubmissionDepositRefunded: {
-    readonly index: u32;
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isMetadataSet: boolean;
-  readonly asMetadataSet: {
-    readonly index: u32;
-    readonly hash_: H256;
-  } & Struct;
-  readonly isMetadataCleared: boolean;
-  readonly asMetadataCleared: {
-    readonly index: u32;
-    readonly hash_: H256;
-  } & Struct;
-  readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared';
-}
-
-/** @name PalletReferendaReferendumInfo */
-export interface PalletReferendaReferendumInfo extends Enum {
-  readonly isOngoing: boolean;
-  readonly asOngoing: PalletReferendaReferendumStatus;
-  readonly isApproved: boolean;
-  readonly asApproved: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-  readonly isRejected: boolean;
-  readonly asRejected: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-  readonly isCancelled: boolean;
-  readonly asCancelled: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-  readonly isTimedOut: boolean;
-  readonly asTimedOut: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-  readonly isKilled: boolean;
-  readonly asKilled: u32;
-  readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed';
-}
-
-/** @name PalletReferendaReferendumStatus */
-export interface PalletReferendaReferendumStatus extends Struct {
-  readonly track: u16;
-  readonly origin: OpalRuntimeOriginCaller;
-  readonly proposal: FrameSupportPreimagesBounded;
-  readonly enactment: FrameSupportScheduleDispatchTime;
-  readonly submitted: u32;
-  readonly submissionDeposit: PalletReferendaDeposit;
-  readonly decisionDeposit: Option<PalletReferendaDeposit>;
-  readonly deciding: Option<PalletReferendaDecidingStatus>;
-  readonly tally: PalletRankedCollectiveTally;
-  readonly inQueue: bool;
-  readonly alarm: Option<ITuple<[u32, ITuple<[u32, u32]>]>>;
-}
-
-/** @name PalletReferendaTrackInfo */
-export interface PalletReferendaTrackInfo extends Struct {
-  readonly name: Text;
-  readonly maxDeciding: u32;
-  readonly decisionDeposit: u128;
-  readonly preparePeriod: u32;
-  readonly decisionPeriod: u32;
-  readonly confirmPeriod: u32;
-  readonly minEnactmentPeriod: u32;
-  readonly minApproval: PalletReferendaCurve;
-  readonly minSupport: PalletReferendaCurve;
-}
-
-/** @name PalletRefungibleError */
-export interface PalletRefungibleError extends Enum {
-  readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
-  readonly isWrongRefungiblePieces: boolean;
-  readonly isRepartitionWhileNotOwningAllPieces: boolean;
-  readonly isRefungibleDisallowsNesting: boolean;
-  readonly isSettingPropertiesNotAllowed: boolean;
-  readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
-}
-
-/** @name PalletSchedulerCall */
-export interface PalletSchedulerCall extends Enum {
-  readonly isSchedule: boolean;
-  readonly asSchedule: {
-    readonly when: u32;
-    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-    readonly priority: u8;
-    readonly call: Call;
-  } & Struct;
-  readonly isCancel: boolean;
-  readonly asCancel: {
-    readonly when: u32;
-    readonly index: u32;
-  } & Struct;
-  readonly isScheduleNamed: boolean;
-  readonly asScheduleNamed: {
-    readonly id: U8aFixed;
-    readonly when: u32;
-    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-    readonly priority: u8;
-    readonly call: Call;
-  } & Struct;
-  readonly isCancelNamed: boolean;
-  readonly asCancelNamed: {
-    readonly id: U8aFixed;
-  } & Struct;
-  readonly isScheduleAfter: boolean;
-  readonly asScheduleAfter: {
-    readonly after: u32;
-    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-    readonly priority: u8;
-    readonly call: Call;
-  } & Struct;
-  readonly isScheduleNamedAfter: boolean;
-  readonly asScheduleNamedAfter: {
-    readonly id: U8aFixed;
-    readonly after: u32;
-    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-    readonly priority: u8;
-    readonly call: Call;
-  } & Struct;
-  readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter';
-}
-
-/** @name PalletSchedulerError */
-export interface PalletSchedulerError extends Enum {
-  readonly isFailedToSchedule: boolean;
-  readonly isNotFound: boolean;
-  readonly isTargetBlockNumberInPast: boolean;
-  readonly isRescheduleNoChange: boolean;
-  readonly isNamed: boolean;
-  readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named';
-}
-
-/** @name PalletSchedulerEvent */
-export interface PalletSchedulerEvent extends Enum {
-  readonly isScheduled: boolean;
-  readonly asScheduled: {
-    readonly when: u32;
-    readonly index: u32;
-  } & Struct;
-  readonly isCanceled: boolean;
-  readonly asCanceled: {
-    readonly when: u32;
-    readonly index: u32;
-  } & Struct;
-  readonly isDispatched: boolean;
-  readonly asDispatched: {
-    readonly task: ITuple<[u32, u32]>;
-    readonly id: Option<U8aFixed>;
-    readonly result: Result<Null, SpRuntimeDispatchError>;
-  } & Struct;
-  readonly isCallUnavailable: boolean;
-  readonly asCallUnavailable: {
-    readonly task: ITuple<[u32, u32]>;
-    readonly id: Option<U8aFixed>;
-  } & Struct;
-  readonly isPeriodicFailed: boolean;
-  readonly asPeriodicFailed: {
-    readonly task: ITuple<[u32, u32]>;
-    readonly id: Option<U8aFixed>;
-  } & Struct;
-  readonly isPermanentlyOverweight: boolean;
-  readonly asPermanentlyOverweight: {
-    readonly task: ITuple<[u32, u32]>;
-    readonly id: Option<U8aFixed>;
-  } & Struct;
-  readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight';
-}
-
-/** @name PalletSchedulerScheduled */
-export interface PalletSchedulerScheduled extends Struct {
-  readonly maybeId: Option<U8aFixed>;
-  readonly priority: u8;
-  readonly call: FrameSupportPreimagesBounded;
-  readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-  readonly origin: OpalRuntimeOriginCaller;
-}
-
-/** @name PalletSessionCall */
-export interface PalletSessionCall extends Enum {
-  readonly isSetKeys: boolean;
-  readonly asSetKeys: {
-    readonly keys_: OpalRuntimeRuntimeCommonSessionKeys;
-    readonly proof: Bytes;
-  } & Struct;
-  readonly isPurgeKeys: boolean;
-  readonly type: 'SetKeys' | 'PurgeKeys';
-}
-
-/** @name PalletSessionError */
-export interface PalletSessionError extends Enum {
-  readonly isInvalidProof: boolean;
-  readonly isNoAssociatedValidatorId: boolean;
-  readonly isDuplicatedKey: boolean;
-  readonly isNoKeys: boolean;
-  readonly isNoAccount: boolean;
-  readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount';
-}
-
-/** @name PalletSessionEvent */
-export interface PalletSessionEvent extends Enum {
-  readonly isNewSession: boolean;
-  readonly asNewSession: {
-    readonly sessionIndex: u32;
-  } & Struct;
-  readonly type: 'NewSession';
-}
-
-/** @name PalletStateTrieMigrationCall */
-export interface PalletStateTrieMigrationCall extends Enum {
-  readonly isControlAutoMigration: boolean;
-  readonly asControlAutoMigration: {
-    readonly maybeConfig: Option<PalletStateTrieMigrationMigrationLimits>;
-  } & Struct;
-  readonly isContinueMigrate: boolean;
-  readonly asContinueMigrate: {
-    readonly limits: PalletStateTrieMigrationMigrationLimits;
-    readonly realSizeUpper: u32;
-    readonly witnessTask: PalletStateTrieMigrationMigrationTask;
-  } & Struct;
-  readonly isMigrateCustomTop: boolean;
-  readonly asMigrateCustomTop: {
-    readonly keys_: Vec<Bytes>;
-    readonly witnessSize: u32;
-  } & Struct;
-  readonly isMigrateCustomChild: boolean;
-  readonly asMigrateCustomChild: {
-    readonly root: Bytes;
-    readonly childKeys: Vec<Bytes>;
-    readonly totalSize: u32;
-  } & Struct;
-  readonly isSetSignedMaxLimits: boolean;
-  readonly asSetSignedMaxLimits: {
-    readonly limits: PalletStateTrieMigrationMigrationLimits;
-  } & Struct;
-  readonly isForceSetProgress: boolean;
-  readonly asForceSetProgress: {
-    readonly progressTop: PalletStateTrieMigrationProgress;
-    readonly progressChild: PalletStateTrieMigrationProgress;
-  } & Struct;
-  readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress';
-}
-
-/** @name PalletStateTrieMigrationError */
-export interface PalletStateTrieMigrationError extends Enum {
-  readonly isMaxSignedLimits: boolean;
-  readonly isKeyTooLong: boolean;
-  readonly isNotEnoughFunds: boolean;
-  readonly isBadWitness: boolean;
-  readonly isSignedMigrationNotAllowed: boolean;
-  readonly isBadChildRoot: boolean;
-  readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot';
-}
-
-/** @name PalletStateTrieMigrationEvent */
-export interface PalletStateTrieMigrationEvent extends Enum {
-  readonly isMigrated: boolean;
-  readonly asMigrated: {
-    readonly top: u32;
-    readonly child: u32;
-    readonly compute: PalletStateTrieMigrationMigrationCompute;
-  } & Struct;
-  readonly isSlashed: boolean;
-  readonly asSlashed: {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isAutoMigrationFinished: boolean;
-  readonly isHalted: boolean;
-  readonly asHalted: {
-    readonly error: PalletStateTrieMigrationError;
-  } & Struct;
-  readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted';
-}
-
-/** @name PalletStateTrieMigrationMigrationCompute */
-export interface PalletStateTrieMigrationMigrationCompute extends Enum {
-  readonly isSigned: boolean;
-  readonly isAuto: boolean;
-  readonly type: 'Signed' | 'Auto';
-}
-
-/** @name PalletStateTrieMigrationMigrationLimits */
-export interface PalletStateTrieMigrationMigrationLimits extends Struct {
-  readonly size_: u32;
-  readonly item: u32;
-}
-
-/** @name PalletStateTrieMigrationMigrationTask */
-export interface PalletStateTrieMigrationMigrationTask extends Struct {
-  readonly progressTop: PalletStateTrieMigrationProgress;
-  readonly progressChild: PalletStateTrieMigrationProgress;
-  readonly size_: u32;
-  readonly topItems: u32;
-  readonly childItems: u32;
-}
-
-/** @name PalletStateTrieMigrationProgress */
-export interface PalletStateTrieMigrationProgress extends Enum {
-  readonly isToStart: boolean;
-  readonly isLastKey: boolean;
-  readonly asLastKey: Bytes;
-  readonly isComplete: boolean;
-  readonly type: 'ToStart' | 'LastKey' | 'Complete';
-}
-
-/** @name PalletStructureCall */
-export interface PalletStructureCall extends Null {}
-
-/** @name PalletStructureError */
-export interface PalletStructureError extends Enum {
-  readonly isOuroborosDetected: boolean;
-  readonly isDepthLimit: boolean;
-  readonly isBreadthLimit: boolean;
-  readonly isTokenNotFound: boolean;
-  readonly isCantNestTokenUnderCollection: boolean;
-  readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection';
-}
-
-/** @name PalletStructureEvent */
-export interface PalletStructureEvent extends Enum {
-  readonly isExecuted: boolean;
-  readonly asExecuted: Result<Null, SpRuntimeDispatchError>;
-  readonly type: 'Executed';
-}
-
-/** @name PalletSudoCall */
-export interface PalletSudoCall extends Enum {
-  readonly isSudo: boolean;
-  readonly asSudo: {
-    readonly call: Call;
-  } & Struct;
-  readonly isSudoUncheckedWeight: boolean;
-  readonly asSudoUncheckedWeight: {
-    readonly call: Call;
-    readonly weight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isSetKey: boolean;
-  readonly asSetKey: {
-    readonly new_: MultiAddress;
-  } & Struct;
-  readonly isSudoAs: boolean;
-  readonly asSudoAs: {
-    readonly who: MultiAddress;
-    readonly call: Call;
-  } & Struct;
-  readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs';
-}
-
-/** @name PalletSudoError */
-export interface PalletSudoError extends Enum {
-  readonly isRequireSudo: boolean;
-  readonly type: 'RequireSudo';
-}
-
-/** @name PalletSudoEvent */
-export interface PalletSudoEvent extends Enum {
-  readonly isSudid: boolean;
-  readonly asSudid: {
-    readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
-  } & Struct;
-  readonly isKeyChanged: boolean;
-  readonly asKeyChanged: {
-    readonly oldSudoer: Option<AccountId32>;
-  } & Struct;
-  readonly isSudoAsDone: boolean;
-  readonly asSudoAsDone: {
-    readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
-  } & Struct;
-  readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone';
-}
-
-/** @name PalletTemplateTransactionPaymentChargeTransactionPayment */
-export interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
-
-/** @name PalletTestUtilsCall */
-export interface PalletTestUtilsCall extends Enum {
-  readonly isEnable: boolean;
-  readonly isSetTestValue: boolean;
-  readonly asSetTestValue: {
-    readonly value: u32;
-  } & Struct;
-  readonly isSetTestValueAndRollback: boolean;
-  readonly asSetTestValueAndRollback: {
-    readonly value: u32;
-  } & Struct;
-  readonly isIncTestValue: boolean;
-  readonly isJustTakeFee: boolean;
-  readonly isBatchAll: boolean;
-  readonly asBatchAll: {
-    readonly calls: Vec<Call>;
-  } & Struct;
-  readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll';
-}
-
-/** @name PalletTestUtilsError */
-export interface PalletTestUtilsError extends Enum {
-  readonly isTestPalletDisabled: boolean;
-  readonly isTriggerRollback: boolean;
-  readonly type: 'TestPalletDisabled' | 'TriggerRollback';
-}
-
-/** @name PalletTestUtilsEvent */
-export interface PalletTestUtilsEvent extends Enum {
-  readonly isValueIsSet: boolean;
-  readonly isShouldRollback: boolean;
-  readonly isBatchCompleted: boolean;
-  readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted';
-}
-
-/** @name PalletTimestampCall */
-export interface PalletTimestampCall extends Enum {
-  readonly isSet: boolean;
-  readonly asSet: {
-    readonly now: Compact<u64>;
-  } & Struct;
-  readonly type: 'Set';
-}
-
-/** @name PalletTransactionPaymentEvent */
-export interface PalletTransactionPaymentEvent extends Enum {
-  readonly isTransactionFeePaid: boolean;
-  readonly asTransactionFeePaid: {
-    readonly who: AccountId32;
-    readonly actualFee: u128;
-    readonly tip: u128;
-  } & Struct;
-  readonly type: 'TransactionFeePaid';
-}
-
-/** @name PalletTransactionPaymentReleases */
-export interface PalletTransactionPaymentReleases extends Enum {
-  readonly isV1Ancient: boolean;
-  readonly isV2: boolean;
-  readonly type: 'V1Ancient' | 'V2';
-}
-
-/** @name PalletTreasuryCall */
-export interface PalletTreasuryCall extends Enum {
-  readonly isProposeSpend: boolean;
-  readonly asProposeSpend: {
-    readonly value: Compact<u128>;
-    readonly beneficiary: MultiAddress;
-  } & Struct;
-  readonly isRejectProposal: boolean;
-  readonly asRejectProposal: {
-    readonly proposalId: Compact<u32>;
-  } & Struct;
-  readonly isApproveProposal: boolean;
-  readonly asApproveProposal: {
-    readonly proposalId: Compact<u32>;
-  } & Struct;
-  readonly isSpend: boolean;
-  readonly asSpend: {
-    readonly amount: Compact<u128>;
-    readonly beneficiary: MultiAddress;
-  } & Struct;
-  readonly isRemoveApproval: boolean;
-  readonly asRemoveApproval: {
-    readonly proposalId: Compact<u32>;
-  } & Struct;
-  readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval';
-}
-
-/** @name PalletTreasuryError */
-export interface PalletTreasuryError extends Enum {
-  readonly isInsufficientProposersBalance: boolean;
-  readonly isInvalidIndex: boolean;
-  readonly isTooManyApprovals: boolean;
-  readonly isInsufficientPermission: boolean;
-  readonly isProposalNotApproved: boolean;
-  readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved';
-}
-
-/** @name PalletTreasuryEvent */
-export interface PalletTreasuryEvent extends Enum {
-  readonly isProposed: boolean;
-  readonly asProposed: {
-    readonly proposalIndex: u32;
-  } & Struct;
-  readonly isSpending: boolean;
-  readonly asSpending: {
-    readonly budgetRemaining: u128;
-  } & Struct;
-  readonly isAwarded: boolean;
-  readonly asAwarded: {
-    readonly proposalIndex: u32;
-    readonly award: u128;
-    readonly account: AccountId32;
-  } & Struct;
-  readonly isRejected: boolean;
-  readonly asRejected: {
-    readonly proposalIndex: u32;
-    readonly slashed: u128;
-  } & Struct;
-  readonly isBurnt: boolean;
-  readonly asBurnt: {
-    readonly burntFunds: u128;
-  } & Struct;
-  readonly isRollover: boolean;
-  readonly asRollover: {
-    readonly rolloverBalance: u128;
-  } & Struct;
-  readonly isDeposit: boolean;
-  readonly asDeposit: {
-    readonly value: u128;
-  } & Struct;
-  readonly isSpendApproved: boolean;
-  readonly asSpendApproved: {
-    readonly proposalIndex: u32;
-    readonly amount: u128;
-    readonly beneficiary: AccountId32;
-  } & Struct;
-  readonly isUpdatedInactive: boolean;
-  readonly asUpdatedInactive: {
-    readonly reactivated: u128;
-    readonly deactivated: u128;
-  } & Struct;
-  readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive';
-}
-
-/** @name PalletTreasuryProposal */
-export interface PalletTreasuryProposal extends Struct {
-  readonly proposer: AccountId32;
-  readonly value: u128;
-  readonly beneficiary: AccountId32;
-  readonly bond: u128;
-}
-
-/** @name PalletUniqueCall */
-export interface PalletUniqueCall extends Enum {
-  readonly isCreateCollection: boolean;
-  readonly asCreateCollection: {
-    readonly collectionName: Vec<u16>;
-    readonly collectionDescription: Vec<u16>;
-    readonly tokenPrefix: Bytes;
-    readonly mode: UpDataStructsCollectionMode;
-  } & Struct;
-  readonly isCreateCollectionEx: boolean;
-  readonly asCreateCollectionEx: {
-    readonly data: UpDataStructsCreateCollectionData;
-  } & Struct;
-  readonly isDestroyCollection: boolean;
-  readonly asDestroyCollection: {
-    readonly collectionId: u32;
-  } & Struct;
-  readonly isAddToAllowList: boolean;
-  readonly asAddToAllowList: {
-    readonly collectionId: u32;
-    readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
-  } & Struct;
-  readonly isRemoveFromAllowList: boolean;
-  readonly asRemoveFromAllowList: {
-    readonly collectionId: u32;
-    readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
-  } & Struct;
-  readonly isChangeCollectionOwner: boolean;
-  readonly asChangeCollectionOwner: {
-    readonly collectionId: u32;
-    readonly newOwner: AccountId32;
-  } & Struct;
-  readonly isAddCollectionAdmin: boolean;
-  readonly asAddCollectionAdmin: {
-    readonly collectionId: u32;
-    readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr;
-  } & Struct;
-  readonly isRemoveCollectionAdmin: boolean;
-  readonly asRemoveCollectionAdmin: {
-    readonly collectionId: u32;
-    readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr;
-  } & Struct;
-  readonly isSetCollectionSponsor: boolean;
-  readonly asSetCollectionSponsor: {
-    readonly collectionId: u32;
-    readonly newSponsor: AccountId32;
-  } & Struct;
-  readonly isConfirmSponsorship: boolean;
-  readonly asConfirmSponsorship: {
-    readonly collectionId: u32;
-  } & Struct;
-  readonly isRemoveCollectionSponsor: boolean;
-  readonly asRemoveCollectionSponsor: {
-    readonly collectionId: u32;
-  } & Struct;
-  readonly isCreateItem: boolean;
-  readonly asCreateItem: {
-    readonly collectionId: u32;
-    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly data: UpDataStructsCreateItemData;
-  } & Struct;
-  readonly isCreateMultipleItems: boolean;
-  readonly asCreateMultipleItems: {
-    readonly collectionId: u32;
-    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly itemsData: Vec<UpDataStructsCreateItemData>;
-  } & Struct;
-  readonly isSetCollectionProperties: boolean;
-  readonly asSetCollectionProperties: {
-    readonly collectionId: u32;
-    readonly properties: Vec<UpDataStructsProperty>;
-  } & Struct;
-  readonly isDeleteCollectionProperties: boolean;
-  readonly asDeleteCollectionProperties: {
-    readonly collectionId: u32;
-    readonly propertyKeys: Vec<Bytes>;
-  } & Struct;
-  readonly isSetTokenProperties: boolean;
-  readonly asSetTokenProperties: {
-    readonly collectionId: u32;
-    readonly tokenId: u32;
-    readonly properties: Vec<UpDataStructsProperty>;
-  } & Struct;
-  readonly isDeleteTokenProperties: boolean;
-  readonly asDeleteTokenProperties: {
-    readonly collectionId: u32;
-    readonly tokenId: u32;
-    readonly propertyKeys: Vec<Bytes>;
-  } & Struct;
-  readonly isSetTokenPropertyPermissions: boolean;
-  readonly asSetTokenPropertyPermissions: {
-    readonly collectionId: u32;
-    readonly propertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
-  } & Struct;
-  readonly isCreateMultipleItemsEx: boolean;
-  readonly asCreateMultipleItemsEx: {
-    readonly collectionId: u32;
-    readonly data: UpDataStructsCreateItemExData;
-  } & Struct;
-  readonly isSetTransfersEnabledFlag: boolean;
-  readonly asSetTransfersEnabledFlag: {
-    readonly collectionId: u32;
-    readonly value: bool;
-  } & Struct;
-  readonly isBurnItem: boolean;
-  readonly asBurnItem: {
-    readonly collectionId: u32;
-    readonly itemId: u32;
-    readonly value: u128;
-  } & Struct;
-  readonly isBurnFrom: boolean;
-  readonly asBurnFrom: {
-    readonly collectionId: u32;
-    readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly itemId: u32;
-    readonly value: u128;
-  } & Struct;
-  readonly isTransfer: boolean;
-  readonly asTransfer: {
-    readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly collectionId: u32;
-    readonly itemId: u32;
-    readonly value: u128;
-  } & Struct;
-  readonly isApprove: boolean;
-  readonly asApprove: {
-    readonly spender: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly collectionId: u32;
-    readonly itemId: u32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isApproveFrom: boolean;
-  readonly asApproveFrom: {
-    readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly to: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly collectionId: u32;
-    readonly itemId: u32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isTransferFrom: boolean;
-  readonly asTransferFrom: {
-    readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly collectionId: u32;
-    readonly itemId: u32;
-    readonly value: u128;
-  } & Struct;
-  readonly isSetCollectionLimits: boolean;
-  readonly asSetCollectionLimits: {
-    readonly collectionId: u32;
-    readonly newLimit: UpDataStructsCollectionLimits;
-  } & Struct;
-  readonly isSetCollectionPermissions: boolean;
-  readonly asSetCollectionPermissions: {
-    readonly collectionId: u32;
-    readonly newPermission: UpDataStructsCollectionPermissions;
-  } & Struct;
-  readonly isRepartition: boolean;
-  readonly asRepartition: {
-    readonly collectionId: u32;
-    readonly tokenId: u32;
-    readonly amount: u128;
-  } & Struct;
-  readonly isSetAllowanceForAll: boolean;
-  readonly asSetAllowanceForAll: {
-    readonly collectionId: u32;
-    readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly approve: bool;
-  } & Struct;
-  readonly isForceRepairCollection: boolean;
-  readonly asForceRepairCollection: {
-    readonly collectionId: u32;
-  } & Struct;
-  readonly isForceRepairItem: boolean;
-  readonly asForceRepairItem: {
-    readonly collectionId: u32;
-    readonly itemId: u32;
-  } & Struct;
-  readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem';
-}
-
-/** @name PalletUniqueError */
-export interface PalletUniqueError extends Enum {
-  readonly isCollectionDecimalPointLimitExceeded: boolean;
-  readonly isEmptyArgument: boolean;
-  readonly isRepartitionCalledOnNonRefungibleCollection: boolean;
-  readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection';
-}
-
-/** @name PalletUtilityCall */
-export interface PalletUtilityCall extends Enum {
-  readonly isBatch: boolean;
-  readonly asBatch: {
-    readonly calls: Vec<Call>;
-  } & Struct;
-  readonly isAsDerivative: boolean;
-  readonly asAsDerivative: {
-    readonly index: u16;
-    readonly call: Call;
-  } & Struct;
-  readonly isBatchAll: boolean;
-  readonly asBatchAll: {
-    readonly calls: Vec<Call>;
-  } & Struct;
-  readonly isDispatchAs: boolean;
-  readonly asDispatchAs: {
-    readonly asOrigin: OpalRuntimeOriginCaller;
-    readonly call: Call;
-  } & Struct;
-  readonly isForceBatch: boolean;
-  readonly asForceBatch: {
-    readonly calls: Vec<Call>;
-  } & Struct;
-  readonly isWithWeight: boolean;
-  readonly asWithWeight: {
-    readonly call: Call;
-    readonly weight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight';
-}
-
-/** @name PalletUtilityError */
-export interface PalletUtilityError extends Enum {
-  readonly isTooManyCalls: boolean;
-  readonly type: 'TooManyCalls';
-}
-
-/** @name PalletUtilityEvent */
-export interface PalletUtilityEvent extends Enum {
-  readonly isBatchInterrupted: boolean;
-  readonly asBatchInterrupted: {
-    readonly index: u32;
-    readonly error: SpRuntimeDispatchError;
-  } & Struct;
-  readonly isBatchCompleted: boolean;
-  readonly isBatchCompletedWithErrors: boolean;
-  readonly isItemCompleted: boolean;
-  readonly isItemFailed: boolean;
-  readonly asItemFailed: {
-    readonly error: SpRuntimeDispatchError;
-  } & Struct;
-  readonly isDispatchedAs: boolean;
-  readonly asDispatchedAs: {
-    readonly result: Result<Null, SpRuntimeDispatchError>;
-  } & Struct;
-  readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs';
-}
-
-/** @name PalletXcmCall */
-export interface PalletXcmCall extends Enum {
-  readonly isSend: boolean;
-  readonly asSend: {
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly message: StagingXcmVersionedXcm;
-  } & Struct;
-  readonly isTeleportAssets: boolean;
-  readonly asTeleportAssets: {
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly beneficiary: StagingXcmVersionedMultiLocation;
-    readonly assets: StagingXcmVersionedMultiAssets;
-    readonly feeAssetItem: u32;
-  } & Struct;
-  readonly isReserveTransferAssets: boolean;
-  readonly asReserveTransferAssets: {
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly beneficiary: StagingXcmVersionedMultiLocation;
-    readonly assets: StagingXcmVersionedMultiAssets;
-    readonly feeAssetItem: u32;
-  } & Struct;
-  readonly isExecute: boolean;
-  readonly asExecute: {
-    readonly message: StagingXcmVersionedXcm;
-    readonly maxWeight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isForceXcmVersion: boolean;
-  readonly asForceXcmVersion: {
-    readonly location: StagingXcmV3MultiLocation;
-    readonly version: u32;
-  } & Struct;
-  readonly isForceDefaultXcmVersion: boolean;
-  readonly asForceDefaultXcmVersion: {
-    readonly maybeXcmVersion: Option<u32>;
-  } & Struct;
-  readonly isForceSubscribeVersionNotify: boolean;
-  readonly asForceSubscribeVersionNotify: {
-    readonly location: StagingXcmVersionedMultiLocation;
-  } & Struct;
-  readonly isForceUnsubscribeVersionNotify: boolean;
-  readonly asForceUnsubscribeVersionNotify: {
-    readonly location: StagingXcmVersionedMultiLocation;
-  } & Struct;
-  readonly isLimitedReserveTransferAssets: boolean;
-  readonly asLimitedReserveTransferAssets: {
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly beneficiary: StagingXcmVersionedMultiLocation;
-    readonly assets: StagingXcmVersionedMultiAssets;
-    readonly feeAssetItem: u32;
-    readonly weightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isLimitedTeleportAssets: boolean;
-  readonly asLimitedTeleportAssets: {
-    readonly dest: StagingXcmVersionedMultiLocation;
-    readonly beneficiary: StagingXcmVersionedMultiLocation;
-    readonly assets: StagingXcmVersionedMultiAssets;
-    readonly feeAssetItem: u32;
-    readonly weightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isForceSuspension: boolean;
-  readonly asForceSuspension: {
-    readonly suspended: bool;
-  } & Struct;
-  readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension';
-}
-
-/** @name PalletXcmError */
-export interface PalletXcmError extends Enum {
-  readonly isUnreachable: boolean;
-  readonly isSendFailure: boolean;
-  readonly isFiltered: boolean;
-  readonly isUnweighableMessage: boolean;
-  readonly isDestinationNotInvertible: boolean;
-  readonly isEmpty: boolean;
-  readonly isCannotReanchor: boolean;
-  readonly isTooManyAssets: boolean;
-  readonly isInvalidOrigin: boolean;
-  readonly isBadVersion: boolean;
-  readonly isBadLocation: boolean;
-  readonly isNoSubscription: boolean;
-  readonly isAlreadySubscribed: boolean;
-  readonly isInvalidAsset: boolean;
-  readonly isLowBalance: boolean;
-  readonly isTooManyLocks: boolean;
-  readonly isAccountNotSovereign: boolean;
-  readonly isFeesNotMet: boolean;
-  readonly isLockNotFound: boolean;
-  readonly isInUse: boolean;
-  readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse';
-}
-
-/** @name PalletXcmEvent */
-export interface PalletXcmEvent extends Enum {
-  readonly isAttempted: boolean;
-  readonly asAttempted: {
-    readonly outcome: StagingXcmV3TraitsOutcome;
-  } & Struct;
-  readonly isSent: boolean;
-  readonly asSent: {
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly destination: StagingXcmV3MultiLocation;
-    readonly message: StagingXcmV3Xcm;
-    readonly messageId: U8aFixed;
-  } & Struct;
-  readonly isUnexpectedResponse: boolean;
-  readonly asUnexpectedResponse: {
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly queryId: u64;
-  } & Struct;
-  readonly isResponseReady: boolean;
-  readonly asResponseReady: {
-    readonly queryId: u64;
-    readonly response: StagingXcmV3Response;
-  } & Struct;
-  readonly isNotified: boolean;
-  readonly asNotified: {
-    readonly queryId: u64;
-    readonly palletIndex: u8;
-    readonly callIndex: u8;
-  } & Struct;
-  readonly isNotifyOverweight: boolean;
-  readonly asNotifyOverweight: {
-    readonly queryId: u64;
-    readonly palletIndex: u8;
-    readonly callIndex: u8;
-    readonly actualWeight: SpWeightsWeightV2Weight;
-    readonly maxBudgetedWeight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isNotifyDispatchError: boolean;
-  readonly asNotifyDispatchError: {
-    readonly queryId: u64;
-    readonly palletIndex: u8;
-    readonly callIndex: u8;
-  } & Struct;
-  readonly isNotifyDecodeFailed: boolean;
-  readonly asNotifyDecodeFailed: {
-    readonly queryId: u64;
-    readonly palletIndex: u8;
-    readonly callIndex: u8;
-  } & Struct;
-  readonly isInvalidResponder: boolean;
-  readonly asInvalidResponder: {
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly queryId: u64;
-    readonly expectedLocation: Option<StagingXcmV3MultiLocation>;
-  } & Struct;
-  readonly isInvalidResponderVersion: boolean;
-  readonly asInvalidResponderVersion: {
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly queryId: u64;
-  } & Struct;
-  readonly isResponseTaken: boolean;
-  readonly asResponseTaken: {
-    readonly queryId: u64;
-  } & Struct;
-  readonly isAssetsTrapped: boolean;
-  readonly asAssetsTrapped: {
-    readonly hash_: H256;
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly assets: StagingXcmVersionedMultiAssets;
-  } & Struct;
-  readonly isVersionChangeNotified: boolean;
-  readonly asVersionChangeNotified: {
-    readonly destination: StagingXcmV3MultiLocation;
-    readonly result: u32;
-    readonly cost: StagingXcmV3MultiassetMultiAssets;
-    readonly messageId: U8aFixed;
-  } & Struct;
-  readonly isSupportedVersionChanged: boolean;
-  readonly asSupportedVersionChanged: {
-    readonly location: StagingXcmV3MultiLocation;
-    readonly version: u32;
-  } & Struct;
-  readonly isNotifyTargetSendFail: boolean;
-  readonly asNotifyTargetSendFail: {
-    readonly location: StagingXcmV3MultiLocation;
-    readonly queryId: u64;
-    readonly error: StagingXcmV3TraitsError;
-  } & Struct;
-  readonly isNotifyTargetMigrationFail: boolean;
-  readonly asNotifyTargetMigrationFail: {
-    readonly location: StagingXcmVersionedMultiLocation;
-    readonly queryId: u64;
-  } & Struct;
-  readonly isInvalidQuerierVersion: boolean;
-  readonly asInvalidQuerierVersion: {
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly queryId: u64;
-  } & Struct;
-  readonly isInvalidQuerier: boolean;
-  readonly asInvalidQuerier: {
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly queryId: u64;
-    readonly expectedQuerier: StagingXcmV3MultiLocation;
-    readonly maybeActualQuerier: Option<StagingXcmV3MultiLocation>;
-  } & Struct;
-  readonly isVersionNotifyStarted: boolean;
-  readonly asVersionNotifyStarted: {
-    readonly destination: StagingXcmV3MultiLocation;
-    readonly cost: StagingXcmV3MultiassetMultiAssets;
-    readonly messageId: U8aFixed;
-  } & Struct;
-  readonly isVersionNotifyRequested: boolean;
-  readonly asVersionNotifyRequested: {
-    readonly destination: StagingXcmV3MultiLocation;
-    readonly cost: StagingXcmV3MultiassetMultiAssets;
-    readonly messageId: U8aFixed;
-  } & Struct;
-  readonly isVersionNotifyUnrequested: boolean;
-  readonly asVersionNotifyUnrequested: {
-    readonly destination: StagingXcmV3MultiLocation;
-    readonly cost: StagingXcmV3MultiassetMultiAssets;
-    readonly messageId: U8aFixed;
-  } & Struct;
-  readonly isFeesPaid: boolean;
-  readonly asFeesPaid: {
-    readonly paying: StagingXcmV3MultiLocation;
-    readonly fees: StagingXcmV3MultiassetMultiAssets;
-  } & Struct;
-  readonly isAssetsClaimed: boolean;
-  readonly asAssetsClaimed: {
-    readonly hash_: H256;
-    readonly origin: StagingXcmV3MultiLocation;
-    readonly assets: StagingXcmVersionedMultiAssets;
-  } & Struct;
-  readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed';
-}
-
-/** @name PalletXcmOrigin */
-export interface PalletXcmOrigin extends Enum {
-  readonly isXcm: boolean;
-  readonly asXcm: StagingXcmV3MultiLocation;
-  readonly isResponse: boolean;
-  readonly asResponse: StagingXcmV3MultiLocation;
-  readonly type: 'Xcm' | 'Response';
-}
-
-/** @name PalletXcmQueryStatus */
-export interface PalletXcmQueryStatus extends Enum {
-  readonly isPending: boolean;
-  readonly asPending: {
-    readonly responder: StagingXcmVersionedMultiLocation;
-    readonly maybeMatchQuerier: Option<StagingXcmVersionedMultiLocation>;
-    readonly maybeNotify: Option<ITuple<[u8, u8]>>;
-    readonly timeout: u32;
-  } & Struct;
-  readonly isVersionNotifier: boolean;
-  readonly asVersionNotifier: {
-    readonly origin: StagingXcmVersionedMultiLocation;
-    readonly isActive: bool;
-  } & Struct;
-  readonly isReady: boolean;
-  readonly asReady: {
-    readonly response: StagingXcmVersionedResponse;
-    readonly at: u32;
-  } & Struct;
-  readonly type: 'Pending' | 'VersionNotifier' | 'Ready';
-}
-
-/** @name PalletXcmRemoteLockedFungibleRecord */
-export interface PalletXcmRemoteLockedFungibleRecord extends Struct {
-  readonly amount: u128;
-  readonly owner: StagingXcmVersionedMultiLocation;
-  readonly locker: StagingXcmVersionedMultiLocation;
-  readonly consumers: Vec<ITuple<[Null, u128]>>;
-}
-
-/** @name PalletXcmVersionMigrationStage */
-export interface PalletXcmVersionMigrationStage extends Enum {
-  readonly isMigrateSupportedVersion: boolean;
-  readonly isMigrateVersionNotifiers: boolean;
-  readonly isNotifyCurrentTargets: boolean;
-  readonly asNotifyCurrentTargets: Option<Bytes>;
-  readonly isMigrateAndNotifyOldTargets: boolean;
-  readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets';
-}
-
-/** @name ParachainInfoCall */
-export interface ParachainInfoCall extends Null {}
-
-/** @name PhantomTypeUpDataStructs */
-export interface PhantomTypeUpDataStructs extends Vec<ITuple<[UpDataStructsTokenData, UpDataStructsRpcCollection, UpPovEstimateRpcPovInfo]>> {}
-
-/** @name PolkadotCorePrimitivesInboundDownwardMessage */
-export interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct {
-  readonly sentAt: u32;
-  readonly msg: Bytes;
-}
-
-/** @name PolkadotCorePrimitivesInboundHrmpMessage */
-export interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct {
-  readonly sentAt: u32;
-  readonly data: Bytes;
-}
-
-/** @name PolkadotCorePrimitivesOutboundHrmpMessage */
-export interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct {
-  readonly recipient: u32;
-  readonly data: Bytes;
-}
-
-/** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat */
-export interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum {
-  readonly isConcatenatedVersionedXcm: boolean;
-  readonly isConcatenatedEncodedBlob: boolean;
-  readonly isSignals: boolean;
-  readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
-}
-
-/** @name PolkadotPrimitivesV5AbridgedHostConfiguration */
-export interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct {
-  readonly maxCodeSize: u32;
-  readonly maxHeadDataSize: u32;
-  readonly maxUpwardQueueCount: u32;
-  readonly maxUpwardQueueSize: u32;
-  readonly maxUpwardMessageSize: u32;
-  readonly maxUpwardMessageNumPerCandidate: u32;
-  readonly hrmpMaxMessageNumPerCandidate: u32;
-  readonly validationUpgradeCooldown: u32;
-  readonly validationUpgradeDelay: u32;
-  readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams;
-}
-
-/** @name PolkadotPrimitivesV5AbridgedHrmpChannel */
-export interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct {
-  readonly maxCapacity: u32;
-  readonly maxTotalSize: u32;
-  readonly maxMessageSize: u32;
-  readonly msgCount: u32;
-  readonly totalSize: u32;
-  readonly mqcHead: Option<H256>;
-}
-
-/** @name PolkadotPrimitivesV5PersistedValidationData */
-export interface PolkadotPrimitivesV5PersistedValidationData extends Struct {
-  readonly parentHead: Bytes;
-  readonly relayParentNumber: u32;
-  readonly relayParentStorageRoot: H256;
-  readonly maxPovSize: u32;
-}
-
-/** @name PolkadotPrimitivesV5UpgradeGoAhead */
-export interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum {
-  readonly isAbort: boolean;
-  readonly isGoAhead: boolean;
-  readonly type: 'Abort' | 'GoAhead';
-}
-
-/** @name PolkadotPrimitivesV5UpgradeRestriction */
-export interface PolkadotPrimitivesV5UpgradeRestriction extends Enum {
-  readonly isPresent: boolean;
-  readonly type: 'Present';
-}
-
-/** @name PolkadotPrimitivesVstagingAsyncBackingParams */
-export interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct {
-  readonly maxCandidateDepth: u32;
-  readonly allowedAncestryLen: u32;
-}
-
-/** @name SpArithmeticArithmeticError */
-export interface SpArithmeticArithmeticError extends Enum {
-  readonly isUnderflow: boolean;
-  readonly isOverflow: boolean;
-  readonly isDivisionByZero: boolean;
-  readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero';
-}
-
-/** @name SpConsensusAuraSr25519AppSr25519Public */
-export interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {}
-
-/** @name SpCoreCryptoKeyTypeId */
-export interface SpCoreCryptoKeyTypeId extends U8aFixed {}
-
-/** @name SpCoreEcdsaSignature */
-export interface SpCoreEcdsaSignature extends U8aFixed {}
-
-/** @name SpCoreEd25519Signature */
-export interface SpCoreEd25519Signature extends U8aFixed {}
-
-/** @name SpCoreSr25519Public */
-export interface SpCoreSr25519Public extends U8aFixed {}
-
-/** @name SpCoreSr25519Signature */
-export interface SpCoreSr25519Signature extends U8aFixed {}
-
-/** @name SpCoreVoid */
-export interface SpCoreVoid extends Null {}
-
-/** @name SpRuntimeDigest */
-export interface SpRuntimeDigest extends Struct {
-  readonly logs: Vec<SpRuntimeDigestDigestItem>;
-}
-
-/** @name SpRuntimeDigestDigestItem */
-export interface SpRuntimeDigestDigestItem extends Enum {
-  readonly isOther: boolean;
-  readonly asOther: Bytes;
-  readonly isConsensus: boolean;
-  readonly asConsensus: ITuple<[U8aFixed, Bytes]>;
-  readonly isSeal: boolean;
-  readonly asSeal: ITuple<[U8aFixed, Bytes]>;
-  readonly isPreRuntime: boolean;
-  readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>;
-  readonly isRuntimeEnvironmentUpdated: boolean;
-  readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated';
-}
-
-/** @name SpRuntimeDispatchError */
-export interface SpRuntimeDispatchError extends Enum {
-  readonly isOther: boolean;
-  readonly isCannotLookup: boolean;
-  readonly isBadOrigin: boolean;
-  readonly isModule: boolean;
-  readonly asModule: SpRuntimeModuleError;
-  readonly isConsumerRemaining: boolean;
-  readonly isNoProviders: boolean;
-  readonly isTooManyConsumers: boolean;
-  readonly isToken: boolean;
-  readonly asToken: SpRuntimeTokenError;
-  readonly isArithmetic: boolean;
-  readonly asArithmetic: SpArithmeticArithmeticError;
-  readonly isTransactional: boolean;
-  readonly asTransactional: SpRuntimeTransactionalError;
-  readonly isExhausted: boolean;
-  readonly isCorruption: boolean;
-  readonly isUnavailable: boolean;
-  readonly isRootNotAllowed: boolean;
-  readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed';
-}
-
-/** @name SpRuntimeModuleError */
-export interface SpRuntimeModuleError extends Struct {
-  readonly index: u8;
-  readonly error: U8aFixed;
-}
-
-/** @name SpRuntimeMultiSignature */
-export interface SpRuntimeMultiSignature extends Enum {
-  readonly isEd25519: boolean;
-  readonly asEd25519: SpCoreEd25519Signature;
-  readonly isSr25519: boolean;
-  readonly asSr25519: SpCoreSr25519Signature;
-  readonly isEcdsa: boolean;
-  readonly asEcdsa: SpCoreEcdsaSignature;
-  readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
-}
-
-/** @name SpRuntimeTokenError */
-export interface SpRuntimeTokenError extends Enum {
-  readonly isFundsUnavailable: boolean;
-  readonly isOnlyProvider: boolean;
-  readonly isBelowMinimum: boolean;
-  readonly isCannotCreate: boolean;
-  readonly isUnknownAsset: boolean;
-  readonly isFrozen: boolean;
-  readonly isUnsupported: boolean;
-  readonly isCannotCreateHold: boolean;
-  readonly isNotExpendable: boolean;
-  readonly isBlocked: boolean;
-  readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked';
-}
-
-/** @name SpRuntimeTransactionalError */
-export interface SpRuntimeTransactionalError extends Enum {
-  readonly isLimitReached: boolean;
-  readonly isNoLayer: boolean;
-  readonly type: 'LimitReached' | 'NoLayer';
-}
-
-/** @name SpRuntimeTransactionValidityInvalidTransaction */
-export interface SpRuntimeTransactionValidityInvalidTransaction extends Enum {
-  readonly isCall: boolean;
-  readonly isPayment: boolean;
-  readonly isFuture: boolean;
-  readonly isStale: boolean;
-  readonly isBadProof: boolean;
-  readonly isAncientBirthBlock: boolean;
-  readonly isExhaustsResources: boolean;
-  readonly isCustom: boolean;
-  readonly asCustom: u8;
-  readonly isBadMandatory: boolean;
-  readonly isMandatoryValidation: boolean;
-  readonly isBadSigner: boolean;
-  readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner';
-}
-
-/** @name SpRuntimeTransactionValidityTransactionValidityError */
-export interface SpRuntimeTransactionValidityTransactionValidityError extends Enum {
-  readonly isInvalid: boolean;
-  readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction;
-  readonly isUnknown: boolean;
-  readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction;
-  readonly type: 'Invalid' | 'Unknown';
-}
-
-/** @name SpRuntimeTransactionValidityUnknownTransaction */
-export interface SpRuntimeTransactionValidityUnknownTransaction extends Enum {
-  readonly isCannotLookup: boolean;
-  readonly isNoUnsignedValidator: boolean;
-  readonly isCustom: boolean;
-  readonly asCustom: u8;
-  readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom';
-}
-
-/** @name SpTrieStorageProof */
-export interface SpTrieStorageProof extends Struct {
-  readonly trieNodes: BTreeSet<Bytes>;
-}
-
-/** @name SpVersionRuntimeVersion */
-export interface SpVersionRuntimeVersion extends Struct {
-  readonly specName: Text;
-  readonly implName: Text;
-  readonly authoringVersion: u32;
-  readonly specVersion: u32;
-  readonly implVersion: u32;
-  readonly apis: Vec<ITuple<[U8aFixed, u32]>>;
-  readonly transactionVersion: u32;
-  readonly stateVersion: u8;
-}
-
-/** @name SpWeightsRuntimeDbWeight */
-export interface SpWeightsRuntimeDbWeight extends Struct {
-  readonly read: u64;
-  readonly write: u64;
-}
-
-/** @name SpWeightsWeightV2Weight */
-export interface SpWeightsWeightV2Weight extends Struct {
-  readonly refTime: Compact<u64>;
-  readonly proofSize: Compact<u64>;
-}
-
-/** @name StagingXcmDoubleEncoded */
-export interface StagingXcmDoubleEncoded extends Struct {
-  readonly encoded: Bytes;
-}
-
-/** @name StagingXcmV2BodyId */
-export interface StagingXcmV2BodyId extends Enum {
-  readonly isUnit: boolean;
-  readonly isNamed: boolean;
-  readonly asNamed: Bytes;
-  readonly isIndex: boolean;
-  readonly asIndex: Compact<u32>;
-  readonly isExecutive: boolean;
-  readonly isTechnical: boolean;
-  readonly isLegislative: boolean;
-  readonly isJudicial: boolean;
-  readonly isDefense: boolean;
-  readonly isAdministration: boolean;
-  readonly isTreasury: boolean;
-  readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
-}
-
-/** @name StagingXcmV2BodyPart */
-export interface StagingXcmV2BodyPart extends Enum {
-  readonly isVoice: boolean;
-  readonly isMembers: boolean;
-  readonly asMembers: {
-    readonly count: Compact<u32>;
-  } & Struct;
-  readonly isFraction: boolean;
-  readonly asFraction: {
-    readonly nom: Compact<u32>;
-    readonly denom: Compact<u32>;
-  } & Struct;
-  readonly isAtLeastProportion: boolean;
-  readonly asAtLeastProportion: {
-    readonly nom: Compact<u32>;
-    readonly denom: Compact<u32>;
-  } & Struct;
-  readonly isMoreThanProportion: boolean;
-  readonly asMoreThanProportion: {
-    readonly nom: Compact<u32>;
-    readonly denom: Compact<u32>;
-  } & Struct;
-  readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
-}
-
-/** @name StagingXcmV2Instruction */
-export interface StagingXcmV2Instruction extends Enum {
-  readonly isWithdrawAsset: boolean;
-  readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets;
-  readonly isReserveAssetDeposited: boolean;
-  readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets;
-  readonly isReceiveTeleportedAsset: boolean;
-  readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets;
-  readonly isQueryResponse: boolean;
-  readonly asQueryResponse: {
-    readonly queryId: Compact<u64>;
-    readonly response: StagingXcmV2Response;
-    readonly maxWeight: Compact<u64>;
-  } & Struct;
-  readonly isTransferAsset: boolean;
-  readonly asTransferAsset: {
-    readonly assets: StagingXcmV2MultiassetMultiAssets;
-    readonly beneficiary: StagingXcmV2MultiLocation;
-  } & Struct;
-  readonly isTransferReserveAsset: boolean;
-  readonly asTransferReserveAsset: {
-    readonly assets: StagingXcmV2MultiassetMultiAssets;
-    readonly dest: StagingXcmV2MultiLocation;
-    readonly xcm: StagingXcmV2Xcm;
-  } & Struct;
-  readonly isTransact: boolean;
-  readonly asTransact: {
-    readonly originType: StagingXcmV2OriginKind;
-    readonly requireWeightAtMost: Compact<u64>;
-    readonly call: StagingXcmDoubleEncoded;
-  } & Struct;
-  readonly isHrmpNewChannelOpenRequest: boolean;
-  readonly asHrmpNewChannelOpenRequest: {
-    readonly sender: Compact<u32>;
-    readonly maxMessageSize: Compact<u32>;
-    readonly maxCapacity: Compact<u32>;
-  } & Struct;
-  readonly isHrmpChannelAccepted: boolean;
-  readonly asHrmpChannelAccepted: {
-    readonly recipient: Compact<u32>;
-  } & Struct;
-  readonly isHrmpChannelClosing: boolean;
-  readonly asHrmpChannelClosing: {
-    readonly initiator: Compact<u32>;
-    readonly sender: Compact<u32>;
-    readonly recipient: Compact<u32>;
-  } & Struct;
-  readonly isClearOrigin: boolean;
-  readonly isDescendOrigin: boolean;
-  readonly asDescendOrigin: StagingXcmV2MultilocationJunctions;
-  readonly isReportError: boolean;
-  readonly asReportError: {
-    readonly queryId: Compact<u64>;
-    readonly dest: StagingXcmV2MultiLocation;
-    readonly maxResponseWeight: Compact<u64>;
-  } & Struct;
-  readonly isDepositAsset: boolean;
-  readonly asDepositAsset: {
-    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-    readonly maxAssets: Compact<u32>;
-    readonly beneficiary: StagingXcmV2MultiLocation;
-  } & Struct;
-  readonly isDepositReserveAsset: boolean;
-  readonly asDepositReserveAsset: {
-    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-    readonly maxAssets: Compact<u32>;
-    readonly dest: StagingXcmV2MultiLocation;
-    readonly xcm: StagingXcmV2Xcm;
-  } & Struct;
-  readonly isExchangeAsset: boolean;
-  readonly asExchangeAsset: {
-    readonly give: StagingXcmV2MultiassetMultiAssetFilter;
-    readonly receive: StagingXcmV2MultiassetMultiAssets;
-  } & Struct;
-  readonly isInitiateReserveWithdraw: boolean;
-  readonly asInitiateReserveWithdraw: {
-    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-    readonly reserve: StagingXcmV2MultiLocation;
-    readonly xcm: StagingXcmV2Xcm;
-  } & Struct;
-  readonly isInitiateTeleport: boolean;
-  readonly asInitiateTeleport: {
-    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-    readonly dest: StagingXcmV2MultiLocation;
-    readonly xcm: StagingXcmV2Xcm;
-  } & Struct;
-  readonly isQueryHolding: boolean;
-  readonly asQueryHolding: {
-    readonly queryId: Compact<u64>;
-    readonly dest: StagingXcmV2MultiLocation;
-    readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-    readonly maxResponseWeight: Compact<u64>;
-  } & Struct;
-  readonly isBuyExecution: boolean;
-  readonly asBuyExecution: {
-    readonly fees: StagingXcmV2MultiAsset;
-    readonly weightLimit: StagingXcmV2WeightLimit;
-  } & Struct;
-  readonly isRefundSurplus: boolean;
-  readonly isSetErrorHandler: boolean;
-  readonly asSetErrorHandler: StagingXcmV2Xcm;
-  readonly isSetAppendix: boolean;
-  readonly asSetAppendix: StagingXcmV2Xcm;
-  readonly isClearError: boolean;
-  readonly isClaimAsset: boolean;
-  readonly asClaimAsset: {
-    readonly assets: StagingXcmV2MultiassetMultiAssets;
-    readonly ticket: StagingXcmV2MultiLocation;
-  } & Struct;
-  readonly isTrap: boolean;
-  readonly asTrap: Compact<u64>;
-  readonly isSubscribeVersion: boolean;
-  readonly asSubscribeVersion: {
-    readonly queryId: Compact<u64>;
-    readonly maxResponseWeight: Compact<u64>;
-  } & Struct;
-  readonly isUnsubscribeVersion: boolean;
-  readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion';
-}
-
-/** @name StagingXcmV2Junction */
-export interface StagingXcmV2Junction extends Enum {
-  readonly isParachain: boolean;
-  readonly asParachain: Compact<u32>;
-  readonly isAccountId32: boolean;
-  readonly asAccountId32: {
-    readonly network: StagingXcmV2NetworkId;
-    readonly id: U8aFixed;
-  } & Struct;
-  readonly isAccountIndex64: boolean;
-  readonly asAccountIndex64: {
-    readonly network: StagingXcmV2NetworkId;
-    readonly index: Compact<u64>;
-  } & Struct;
-  readonly isAccountKey20: boolean;
-  readonly asAccountKey20: {
-    readonly network: StagingXcmV2NetworkId;
-    readonly key: U8aFixed;
-  } & Struct;
-  readonly isPalletInstance: boolean;
-  readonly asPalletInstance: u8;
-  readonly isGeneralIndex: boolean;
-  readonly asGeneralIndex: Compact<u128>;
-  readonly isGeneralKey: boolean;
-  readonly asGeneralKey: Bytes;
-  readonly isOnlyChild: boolean;
-  readonly isPlurality: boolean;
-  readonly asPlurality: {
-    readonly id: StagingXcmV2BodyId;
-    readonly part: StagingXcmV2BodyPart;
-  } & Struct;
-  readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality';
-}
-
-/** @name StagingXcmV2MultiAsset */
-export interface StagingXcmV2MultiAsset extends Struct {
-  readonly id: StagingXcmV2MultiassetAssetId;
-  readonly fun: StagingXcmV2MultiassetFungibility;
-}
-
-/** @name StagingXcmV2MultiassetAssetId */
-export interface StagingXcmV2MultiassetAssetId extends Enum {
-  readonly isConcrete: boolean;
-  readonly asConcrete: StagingXcmV2MultiLocation;
-  readonly isAbstract: boolean;
-  readonly asAbstract: Bytes;
-  readonly type: 'Concrete' | 'Abstract';
-}
-
-/** @name StagingXcmV2MultiassetAssetInstance */
-export interface StagingXcmV2MultiassetAssetInstance extends Enum {
-  readonly isUndefined: boolean;
-  readonly isIndex: boolean;
-  readonly asIndex: Compact<u128>;
-  readonly isArray4: boolean;
-  readonly asArray4: U8aFixed;
-  readonly isArray8: boolean;
-  readonly asArray8: U8aFixed;
-  readonly isArray16: boolean;
-  readonly asArray16: U8aFixed;
-  readonly isArray32: boolean;
-  readonly asArray32: U8aFixed;
-  readonly isBlob: boolean;
-  readonly asBlob: Bytes;
-  readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob';
-}
-
-/** @name StagingXcmV2MultiassetFungibility */
-export interface StagingXcmV2MultiassetFungibility extends Enum {
-  readonly isFungible: boolean;
-  readonly asFungible: Compact<u128>;
-  readonly isNonFungible: boolean;
-  readonly asNonFungible: StagingXcmV2MultiassetAssetInstance;
-  readonly type: 'Fungible' | 'NonFungible';
-}
-
-/** @name StagingXcmV2MultiassetMultiAssetFilter */
-export interface StagingXcmV2MultiassetMultiAssetFilter extends Enum {
-  readonly isDefinite: boolean;
-  readonly asDefinite: StagingXcmV2MultiassetMultiAssets;
-  readonly isWild: boolean;
-  readonly asWild: StagingXcmV2MultiassetWildMultiAsset;
-  readonly type: 'Definite' | 'Wild';
-}
-
-/** @name StagingXcmV2MultiassetMultiAssets */
-export interface StagingXcmV2MultiassetMultiAssets extends Vec<StagingXcmV2MultiAsset> {}
-
-/** @name StagingXcmV2MultiassetWildFungibility */
-export interface StagingXcmV2MultiassetWildFungibility extends Enum {
-  readonly isFungible: boolean;
-  readonly isNonFungible: boolean;
-  readonly type: 'Fungible' | 'NonFungible';
-}
-
-/** @name StagingXcmV2MultiassetWildMultiAsset */
-export interface StagingXcmV2MultiassetWildMultiAsset extends Enum {
-  readonly isAll: boolean;
-  readonly isAllOf: boolean;
-  readonly asAllOf: {
-    readonly id: StagingXcmV2MultiassetAssetId;
-    readonly fun: StagingXcmV2MultiassetWildFungibility;
-  } & Struct;
-  readonly type: 'All' | 'AllOf';
-}
-
-/** @name StagingXcmV2MultiLocation */
-export interface StagingXcmV2MultiLocation extends Struct {
-  readonly parents: u8;
-  readonly interior: StagingXcmV2MultilocationJunctions;
-}
-
-/** @name StagingXcmV2MultilocationJunctions */
-export interface StagingXcmV2MultilocationJunctions extends Enum {
-  readonly isHere: boolean;
-  readonly isX1: boolean;
-  readonly asX1: StagingXcmV2Junction;
-  readonly isX2: boolean;
-  readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>;
-  readonly isX3: boolean;
-  readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-  readonly isX4: boolean;
-  readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-  readonly isX5: boolean;
-  readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-  readonly isX6: boolean;
-  readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-  readonly isX7: boolean;
-  readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-  readonly isX8: boolean;
-  readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-  readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
-}
-
-/** @name StagingXcmV2NetworkId */
-export interface StagingXcmV2NetworkId extends Enum {
-  readonly isAny: boolean;
-  readonly isNamed: boolean;
-  readonly asNamed: Bytes;
-  readonly isPolkadot: boolean;
-  readonly isKusama: boolean;
-  readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama';
-}
-
-/** @name StagingXcmV2OriginKind */
-export interface StagingXcmV2OriginKind extends Enum {
-  readonly isNative: boolean;
-  readonly isSovereignAccount: boolean;
-  readonly isSuperuser: boolean;
-  readonly isXcm: boolean;
-  readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm';
-}
-
-/** @name StagingXcmV2Response */
-export interface StagingXcmV2Response extends Enum {
-  readonly isNull: boolean;
-  readonly isAssets: boolean;
-  readonly asAssets: StagingXcmV2MultiassetMultiAssets;
-  readonly isExecutionResult: boolean;
-  readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV2TraitsError]>>;
-  readonly isVersion: boolean;
-  readonly asVersion: u32;
-  readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version';
-}
-
-/** @name StagingXcmV2TraitsError */
-export interface StagingXcmV2TraitsError extends Enum {
-  readonly isOverflow: boolean;
-  readonly isUnimplemented: boolean;
-  readonly isUntrustedReserveLocation: boolean;
-  readonly isUntrustedTeleportLocation: boolean;
-  readonly isMultiLocationFull: boolean;
-  readonly isMultiLocationNotInvertible: boolean;
-  readonly isBadOrigin: boolean;
-  readonly isInvalidLocation: boolean;
-  readonly isAssetNotFound: boolean;
-  readonly isFailedToTransactAsset: boolean;
-  readonly isNotWithdrawable: boolean;
-  readonly isLocationCannotHold: boolean;
-  readonly isExceedsMaxMessageSize: boolean;
-  readonly isDestinationUnsupported: boolean;
-  readonly isTransport: boolean;
-  readonly isUnroutable: boolean;
-  readonly isUnknownClaim: boolean;
-  readonly isFailedToDecode: boolean;
-  readonly isMaxWeightInvalid: boolean;
-  readonly isNotHoldingFees: boolean;
-  readonly isTooExpensive: boolean;
-  readonly isTrap: boolean;
-  readonly asTrap: u64;
-  readonly isUnhandledXcmVersion: boolean;
-  readonly isWeightLimitReached: boolean;
-  readonly asWeightLimitReached: u64;
-  readonly isBarrier: boolean;
-  readonly isWeightNotComputable: boolean;
-  readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable';
-}
-
-/** @name StagingXcmV2WeightLimit */
-export interface StagingXcmV2WeightLimit extends Enum {
-  readonly isUnlimited: boolean;
-  readonly isLimited: boolean;
-  readonly asLimited: Compact<u64>;
-  readonly type: 'Unlimited' | 'Limited';
-}
-
-/** @name StagingXcmV2Xcm */
-export interface StagingXcmV2Xcm extends Vec<StagingXcmV2Instruction> {}
-
-/** @name StagingXcmV3Instruction */
-export interface StagingXcmV3Instruction extends Enum {
-  readonly isWithdrawAsset: boolean;
-  readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets;
-  readonly isReserveAssetDeposited: boolean;
-  readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets;
-  readonly isReceiveTeleportedAsset: boolean;
-  readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets;
-  readonly isQueryResponse: boolean;
-  readonly asQueryResponse: {
-    readonly queryId: Compact<u64>;
-    readonly response: StagingXcmV3Response;
-    readonly maxWeight: SpWeightsWeightV2Weight;
-    readonly querier: Option<StagingXcmV3MultiLocation>;
-  } & Struct;
-  readonly isTransferAsset: boolean;
-  readonly asTransferAsset: {
-    readonly assets: StagingXcmV3MultiassetMultiAssets;
-    readonly beneficiary: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly isTransferReserveAsset: boolean;
-  readonly asTransferReserveAsset: {
-    readonly assets: StagingXcmV3MultiassetMultiAssets;
-    readonly dest: StagingXcmV3MultiLocation;
-    readonly xcm: StagingXcmV3Xcm;
-  } & Struct;
-  readonly isTransact: boolean;
-  readonly asTransact: {
-    readonly originKind: StagingXcmV2OriginKind;
-    readonly requireWeightAtMost: SpWeightsWeightV2Weight;
-    readonly call: StagingXcmDoubleEncoded;
-  } & Struct;
-  readonly isHrmpNewChannelOpenRequest: boolean;
-  readonly asHrmpNewChannelOpenRequest: {
-    readonly sender: Compact<u32>;
-    readonly maxMessageSize: Compact<u32>;
-    readonly maxCapacity: Compact<u32>;
-  } & Struct;
-  readonly isHrmpChannelAccepted: boolean;
-  readonly asHrmpChannelAccepted: {
-    readonly recipient: Compact<u32>;
-  } & Struct;
-  readonly isHrmpChannelClosing: boolean;
-  readonly asHrmpChannelClosing: {
-    readonly initiator: Compact<u32>;
-    readonly sender: Compact<u32>;
-    readonly recipient: Compact<u32>;
-  } & Struct;
-  readonly isClearOrigin: boolean;
-  readonly isDescendOrigin: boolean;
-  readonly asDescendOrigin: StagingXcmV3Junctions;
-  readonly isReportError: boolean;
-  readonly asReportError: StagingXcmV3QueryResponseInfo;
-  readonly isDepositAsset: boolean;
-  readonly asDepositAsset: {
-    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-    readonly beneficiary: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly isDepositReserveAsset: boolean;
-  readonly asDepositReserveAsset: {
-    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-    readonly dest: StagingXcmV3MultiLocation;
-    readonly xcm: StagingXcmV3Xcm;
-  } & Struct;
-  readonly isExchangeAsset: boolean;
-  readonly asExchangeAsset: {
-    readonly give: StagingXcmV3MultiassetMultiAssetFilter;
-    readonly want: StagingXcmV3MultiassetMultiAssets;
-    readonly maximal: bool;
-  } & Struct;
-  readonly isInitiateReserveWithdraw: boolean;
-  readonly asInitiateReserveWithdraw: {
-    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-    readonly reserve: StagingXcmV3MultiLocation;
-    readonly xcm: StagingXcmV3Xcm;
-  } & Struct;
-  readonly isInitiateTeleport: boolean;
-  readonly asInitiateTeleport: {
-    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-    readonly dest: StagingXcmV3MultiLocation;
-    readonly xcm: StagingXcmV3Xcm;
-  } & Struct;
-  readonly isReportHolding: boolean;
-  readonly asReportHolding: {
-    readonly responseInfo: StagingXcmV3QueryResponseInfo;
-    readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-  } & Struct;
-  readonly isBuyExecution: boolean;
-  readonly asBuyExecution: {
-    readonly fees: StagingXcmV3MultiAsset;
-    readonly weightLimit: StagingXcmV3WeightLimit;
-  } & Struct;
-  readonly isRefundSurplus: boolean;
-  readonly isSetErrorHandler: boolean;
-  readonly asSetErrorHandler: StagingXcmV3Xcm;
-  readonly isSetAppendix: boolean;
-  readonly asSetAppendix: StagingXcmV3Xcm;
-  readonly isClearError: boolean;
-  readonly isClaimAsset: boolean;
-  readonly asClaimAsset: {
-    readonly assets: StagingXcmV3MultiassetMultiAssets;
-    readonly ticket: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly isTrap: boolean;
-  readonly asTrap: Compact<u64>;
-  readonly isSubscribeVersion: boolean;
-  readonly asSubscribeVersion: {
-    readonly queryId: Compact<u64>;
-    readonly maxResponseWeight: SpWeightsWeightV2Weight;
-  } & Struct;
-  readonly isUnsubscribeVersion: boolean;
-  readonly isBurnAsset: boolean;
-  readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets;
-  readonly isExpectAsset: boolean;
-  readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets;
-  readonly isExpectOrigin: boolean;
-  readonly asExpectOrigin: Option<StagingXcmV3MultiLocation>;
-  readonly isExpectError: boolean;
-  readonly asExpectError: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
-  readonly isExpectTransactStatus: boolean;
-  readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode;
-  readonly isQueryPallet: boolean;
-  readonly asQueryPallet: {
-    readonly moduleName: Bytes;
-    readonly responseInfo: StagingXcmV3QueryResponseInfo;
-  } & Struct;
-  readonly isExpectPallet: boolean;
-  readonly asExpectPallet: {
-    readonly index: Compact<u32>;
-    readonly name: Bytes;
-    readonly moduleName: Bytes;
-    readonly crateMajor: Compact<u32>;
-    readonly minCrateMinor: Compact<u32>;
-  } & Struct;
-  readonly isReportTransactStatus: boolean;
-  readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo;
-  readonly isClearTransactStatus: boolean;
-  readonly isUniversalOrigin: boolean;
-  readonly asUniversalOrigin: StagingXcmV3Junction;
-  readonly isExportMessage: boolean;
-  readonly asExportMessage: {
-    readonly network: StagingXcmV3JunctionNetworkId;
-    readonly destination: StagingXcmV3Junctions;
-    readonly xcm: StagingXcmV3Xcm;
-  } & Struct;
-  readonly isLockAsset: boolean;
-  readonly asLockAsset: {
-    readonly asset: StagingXcmV3MultiAsset;
-    readonly unlocker: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly isUnlockAsset: boolean;
-  readonly asUnlockAsset: {
-    readonly asset: StagingXcmV3MultiAsset;
-    readonly target: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly isNoteUnlockable: boolean;
-  readonly asNoteUnlockable: {
-    readonly asset: StagingXcmV3MultiAsset;
-    readonly owner: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly isRequestUnlock: boolean;
-  readonly asRequestUnlock: {
-    readonly asset: StagingXcmV3MultiAsset;
-    readonly locker: StagingXcmV3MultiLocation;
-  } & Struct;
-  readonly isSetFeesMode: boolean;
-  readonly asSetFeesMode: {
-    readonly jitWithdraw: bool;
-  } & Struct;
-  readonly isSetTopic: boolean;
-  readonly asSetTopic: U8aFixed;
-  readonly isClearTopic: boolean;
-  readonly isAliasOrigin: boolean;
-  readonly asAliasOrigin: StagingXcmV3MultiLocation;
-  readonly isUnpaidExecution: boolean;
-  readonly asUnpaidExecution: {
-    readonly weightLimit: StagingXcmV3WeightLimit;
-    readonly checkOrigin: Option<StagingXcmV3MultiLocation>;
-  } & Struct;
-  readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution';
-}
-
-/** @name StagingXcmV3Junction */
-export interface StagingXcmV3Junction extends Enum {
-  readonly isParachain: boolean;
-  readonly asParachain: Compact<u32>;
-  readonly isAccountId32: boolean;
-  readonly asAccountId32: {
-    readonly network: Option<StagingXcmV3JunctionNetworkId>;
-    readonly id: U8aFixed;
-  } & Struct;
-  readonly isAccountIndex64: boolean;
-  readonly asAccountIndex64: {
-    readonly network: Option<StagingXcmV3JunctionNetworkId>;
-    readonly index: Compact<u64>;
-  } & Struct;
-  readonly isAccountKey20: boolean;
-  readonly asAccountKey20: {
-    readonly network: Option<StagingXcmV3JunctionNetworkId>;
-    readonly key: U8aFixed;
-  } & Struct;
-  readonly isPalletInstance: boolean;
-  readonly asPalletInstance: u8;
-  readonly isGeneralIndex: boolean;
-  readonly asGeneralIndex: Compact<u128>;
-  readonly isGeneralKey: boolean;
-  readonly asGeneralKey: {
-    readonly length: u8;
-    readonly data: U8aFixed;
-  } & Struct;
-  readonly isOnlyChild: boolean;
-  readonly isPlurality: boolean;
-  readonly asPlurality: {
-    readonly id: StagingXcmV3JunctionBodyId;
-    readonly part: StagingXcmV3JunctionBodyPart;
-  } & Struct;
-  readonly isGlobalConsensus: boolean;
-  readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId;
-  readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus';
-}
-
-/** @name StagingXcmV3JunctionBodyId */
-export interface StagingXcmV3JunctionBodyId extends Enum {
-  readonly isUnit: boolean;
-  readonly isMoniker: boolean;
-  readonly asMoniker: U8aFixed;
-  readonly isIndex: boolean;
-  readonly asIndex: Compact<u32>;
-  readonly isExecutive: boolean;
-  readonly isTechnical: boolean;
-  readonly isLegislative: boolean;
-  readonly isJudicial: boolean;
-  readonly isDefense: boolean;
-  readonly isAdministration: boolean;
-  readonly isTreasury: boolean;
-  readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
-}
-
-/** @name StagingXcmV3JunctionBodyPart */
-export interface StagingXcmV3JunctionBodyPart extends Enum {
-  readonly isVoice: boolean;
-  readonly isMembers: boolean;
-  readonly asMembers: {
-    readonly count: Compact<u32>;
-  } & Struct;
-  readonly isFraction: boolean;
-  readonly asFraction: {
-    readonly nom: Compact<u32>;
-    readonly denom: Compact<u32>;
-  } & Struct;
-  readonly isAtLeastProportion: boolean;
-  readonly asAtLeastProportion: {
-    readonly nom: Compact<u32>;
-    readonly denom: Compact<u32>;
-  } & Struct;
-  readonly isMoreThanProportion: boolean;
-  readonly asMoreThanProportion: {
-    readonly nom: Compact<u32>;
-    readonly denom: Compact<u32>;
-  } & Struct;
-  readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
-}
-
-/** @name StagingXcmV3JunctionNetworkId */
-export interface StagingXcmV3JunctionNetworkId extends Enum {
-  readonly isByGenesis: boolean;
-  readonly asByGenesis: U8aFixed;
-  readonly isByFork: boolean;
-  readonly asByFork: {
-    readonly blockNumber: u64;
-    readonly blockHash: U8aFixed;
-  } & Struct;
-  readonly isPolkadot: boolean;
-  readonly isKusama: boolean;
-  readonly isWestend: boolean;
-  readonly isRococo: boolean;
-  readonly isWococo: boolean;
-  readonly isEthereum: boolean;
-  readonly asEthereum: {
-    readonly chainId: Compact<u64>;
-  } & Struct;
-  readonly isBitcoinCore: boolean;
-  readonly isBitcoinCash: boolean;
-  readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash';
-}
-
-/** @name StagingXcmV3Junctions */
-export interface StagingXcmV3Junctions extends Enum {
-  readonly isHere: boolean;
-  readonly isX1: boolean;
-  readonly asX1: StagingXcmV3Junction;
-  readonly isX2: boolean;
-  readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>;
-  readonly isX3: boolean;
-  readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-  readonly isX4: boolean;
-  readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-  readonly isX5: boolean;
-  readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-  readonly isX6: boolean;
-  readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-  readonly isX7: boolean;
-  readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-  readonly isX8: boolean;
-  readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-  readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
-}
-
-/** @name StagingXcmV3MaybeErrorCode */
-export interface StagingXcmV3MaybeErrorCode extends Enum {
-  readonly isSuccess: boolean;
-  readonly isError: boolean;
-  readonly asError: Bytes;
-  readonly isTruncatedError: boolean;
-  readonly asTruncatedError: Bytes;
-  readonly type: 'Success' | 'Error' | 'TruncatedError';
-}
-
-/** @name StagingXcmV3MultiAsset */
-export interface StagingXcmV3MultiAsset extends Struct {
-  readonly id: StagingXcmV3MultiassetAssetId;
-  readonly fun: StagingXcmV3MultiassetFungibility;
-}
-
-/** @name StagingXcmV3MultiassetAssetId */
-export interface StagingXcmV3MultiassetAssetId extends Enum {
-  readonly isConcrete: boolean;
-  readonly asConcrete: StagingXcmV3MultiLocation;
-  readonly isAbstract: boolean;
-  readonly asAbstract: U8aFixed;
-  readonly type: 'Concrete' | 'Abstract';
-}
-
-/** @name StagingXcmV3MultiassetAssetInstance */
-export interface StagingXcmV3MultiassetAssetInstance extends Enum {
-  readonly isUndefined: boolean;
-  readonly isIndex: boolean;
-  readonly asIndex: Compact<u128>;
-  readonly isArray4: boolean;
-  readonly asArray4: U8aFixed;
-  readonly isArray8: boolean;
-  readonly asArray8: U8aFixed;
-  readonly isArray16: boolean;
-  readonly asArray16: U8aFixed;
-  readonly isArray32: boolean;
-  readonly asArray32: U8aFixed;
-  readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32';
-}
-
-/** @name StagingXcmV3MultiassetFungibility */
-export interface StagingXcmV3MultiassetFungibility extends Enum {
-  readonly isFungible: boolean;
-  readonly asFungible: Compact<u128>;
-  readonly isNonFungible: boolean;
-  readonly asNonFungible: StagingXcmV3MultiassetAssetInstance;
-  readonly type: 'Fungible' | 'NonFungible';
-}
-
-/** @name StagingXcmV3MultiassetMultiAssetFilter */
-export interface StagingXcmV3MultiassetMultiAssetFilter extends Enum {
-  readonly isDefinite: boolean;
-  readonly asDefinite: StagingXcmV3MultiassetMultiAssets;
-  readonly isWild: boolean;
-  readonly asWild: StagingXcmV3MultiassetWildMultiAsset;
-  readonly type: 'Definite' | 'Wild';
-}
-
-/** @name StagingXcmV3MultiassetMultiAssets */
-export interface StagingXcmV3MultiassetMultiAssets extends Vec<StagingXcmV3MultiAsset> {}
-
-/** @name StagingXcmV3MultiassetWildFungibility */
-export interface StagingXcmV3MultiassetWildFungibility extends Enum {
-  readonly isFungible: boolean;
-  readonly isNonFungible: boolean;
-  readonly type: 'Fungible' | 'NonFungible';
-}
-
-/** @name StagingXcmV3MultiassetWildMultiAsset */
-export interface StagingXcmV3MultiassetWildMultiAsset extends Enum {
-  readonly isAll: boolean;
-  readonly isAllOf: boolean;
-  readonly asAllOf: {
-    readonly id: StagingXcmV3MultiassetAssetId;
-    readonly fun: StagingXcmV3MultiassetWildFungibility;
-  } & Struct;
-  readonly isAllCounted: boolean;
-  readonly asAllCounted: Compact<u32>;
-  readonly isAllOfCounted: boolean;
-  readonly asAllOfCounted: {
-    readonly id: StagingXcmV3MultiassetAssetId;
-    readonly fun: StagingXcmV3MultiassetWildFungibility;
-    readonly count: Compact<u32>;
-  } & Struct;
-  readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted';
-}
-
-/** @name StagingXcmV3MultiLocation */
-export interface StagingXcmV3MultiLocation extends Struct {
-  readonly parents: u8;
-  readonly interior: StagingXcmV3Junctions;
-}
-
-/** @name StagingXcmV3PalletInfo */
-export interface StagingXcmV3PalletInfo extends Struct {
-  readonly index: Compact<u32>;
-  readonly name: Bytes;
-  readonly moduleName: Bytes;
-  readonly major: Compact<u32>;
-  readonly minor: Compact<u32>;
-  readonly patch: Compact<u32>;
-}
-
-/** @name StagingXcmV3QueryResponseInfo */
-export interface StagingXcmV3QueryResponseInfo extends Struct {
-  readonly destination: StagingXcmV3MultiLocation;
-  readonly queryId: Compact<u64>;
-  readonly maxWeight: SpWeightsWeightV2Weight;
-}
-
-/** @name StagingXcmV3Response */
-export interface StagingXcmV3Response extends Enum {
-  readonly isNull: boolean;
-  readonly isAssets: boolean;
-  readonly asAssets: StagingXcmV3MultiassetMultiAssets;
-  readonly isExecutionResult: boolean;
-  readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
-  readonly isVersion: boolean;
-  readonly asVersion: u32;
-  readonly isPalletsInfo: boolean;
-  readonly asPalletsInfo: Vec<StagingXcmV3PalletInfo>;
-  readonly isDispatchResult: boolean;
-  readonly asDispatchResult: StagingXcmV3MaybeErrorCode;
-  readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult';
-}
-
-/** @name StagingXcmV3TraitsError */
-export interface StagingXcmV3TraitsError extends Enum {
-  readonly isOverflow: boolean;
-  readonly isUnimplemented: boolean;
-  readonly isUntrustedReserveLocation: boolean;
-  readonly isUntrustedTeleportLocation: boolean;
-  readonly isLocationFull: boolean;
-  readonly isLocationNotInvertible: boolean;
-  readonly isBadOrigin: boolean;
-  readonly isInvalidLocation: boolean;
-  readonly isAssetNotFound: boolean;
-  readonly isFailedToTransactAsset: boolean;
-  readonly isNotWithdrawable: boolean;
-  readonly isLocationCannotHold: boolean;
-  readonly isExceedsMaxMessageSize: boolean;
-  readonly isDestinationUnsupported: boolean;
-  readonly isTransport: boolean;
-  readonly isUnroutable: boolean;
-  readonly isUnknownClaim: boolean;
-  readonly isFailedToDecode: boolean;
-  readonly isMaxWeightInvalid: boolean;
-  readonly isNotHoldingFees: boolean;
-  readonly isTooExpensive: boolean;
-  readonly isTrap: boolean;
-  readonly asTrap: u64;
-  readonly isExpectationFalse: boolean;
-  readonly isPalletNotFound: boolean;
-  readonly isNameMismatch: boolean;
-  readonly isVersionIncompatible: boolean;
-  readonly isHoldingWouldOverflow: boolean;
-  readonly isExportError: boolean;
-  readonly isReanchorFailed: boolean;
-  readonly isNoDeal: boolean;
-  readonly isFeesNotMet: boolean;
-  readonly isLockError: boolean;
-  readonly isNoPermission: boolean;
-  readonly isUnanchored: boolean;
-  readonly isNotDepositable: boolean;
-  readonly isUnhandledXcmVersion: boolean;
-  readonly isWeightLimitReached: boolean;
-  readonly asWeightLimitReached: SpWeightsWeightV2Weight;
-  readonly isBarrier: boolean;
-  readonly isWeightNotComputable: boolean;
-  readonly isExceedsStackLimit: boolean;
-  readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit';
-}
-
-/** @name StagingXcmV3TraitsOutcome */
-export interface StagingXcmV3TraitsOutcome extends Enum {
-  readonly isComplete: boolean;
-  readonly asComplete: SpWeightsWeightV2Weight;
-  readonly isIncomplete: boolean;
-  readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>;
-  readonly isError: boolean;
-  readonly asError: StagingXcmV3TraitsError;
-  readonly type: 'Complete' | 'Incomplete' | 'Error';
-}
-
-/** @name StagingXcmV3WeightLimit */
-export interface StagingXcmV3WeightLimit extends Enum {
-  readonly isUnlimited: boolean;
-  readonly isLimited: boolean;
-  readonly asLimited: SpWeightsWeightV2Weight;
-  readonly type: 'Unlimited' | 'Limited';
-}
-
-/** @name StagingXcmV3Xcm */
-export interface StagingXcmV3Xcm extends Vec<StagingXcmV3Instruction> {}
-
-/** @name StagingXcmVersionedAssetId */
-export interface StagingXcmVersionedAssetId extends Enum {
-  readonly isV3: boolean;
-  readonly asV3: StagingXcmV3MultiassetAssetId;
-  readonly type: 'V3';
-}
-
-/** @name StagingXcmVersionedMultiAsset */
-export interface StagingXcmVersionedMultiAsset extends Enum {
-  readonly isV2: boolean;
-  readonly asV2: StagingXcmV2MultiAsset;
-  readonly isV3: boolean;
-  readonly asV3: StagingXcmV3MultiAsset;
-  readonly type: 'V2' | 'V3';
-}
-
-/** @name StagingXcmVersionedMultiAssets */
-export interface StagingXcmVersionedMultiAssets extends Enum {
-  readonly isV2: boolean;
-  readonly asV2: StagingXcmV2MultiassetMultiAssets;
-  readonly isV3: boolean;
-  readonly asV3: StagingXcmV3MultiassetMultiAssets;
-  readonly type: 'V2' | 'V3';
-}
-
-/** @name StagingXcmVersionedMultiLocation */
-export interface StagingXcmVersionedMultiLocation extends Enum {
-  readonly isV2: boolean;
-  readonly asV2: StagingXcmV2MultiLocation;
-  readonly isV3: boolean;
-  readonly asV3: StagingXcmV3MultiLocation;
-  readonly type: 'V2' | 'V3';
-}
-
-/** @name StagingXcmVersionedResponse */
-export interface StagingXcmVersionedResponse extends Enum {
-  readonly isV2: boolean;
-  readonly asV2: StagingXcmV2Response;
-  readonly isV3: boolean;
-  readonly asV3: StagingXcmV3Response;
-  readonly type: 'V2' | 'V3';
-}
-
-/** @name StagingXcmVersionedXcm */
-export interface StagingXcmVersionedXcm extends Enum {
-  readonly isV2: boolean;
-  readonly asV2: StagingXcmV2Xcm;
-  readonly isV3: boolean;
-  readonly asV3: StagingXcmV3Xcm;
-  readonly type: 'V2' | 'V3';
-}
-
-/** @name UpDataStructsAccessMode */
-export interface UpDataStructsAccessMode extends Enum {
-  readonly isNormal: boolean;
-  readonly isAllowList: boolean;
-  readonly type: 'Normal' | 'AllowList';
-}
-
-/** @name UpDataStructsCollection */
-export interface UpDataStructsCollection extends Struct {
-  readonly owner: AccountId32;
-  readonly mode: UpDataStructsCollectionMode;
-  readonly name: Vec<u16>;
-  readonly description: Vec<u16>;
-  readonly tokenPrefix: Bytes;
-  readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
-  readonly limits: UpDataStructsCollectionLimits;
-  readonly permissions: UpDataStructsCollectionPermissions;
-  readonly flags: U8aFixed;
-}
-
-/** @name UpDataStructsCollectionLimits */
-export interface UpDataStructsCollectionLimits extends Struct {
-  readonly accountTokenOwnershipLimit: Option<u32>;
-  readonly sponsoredDataSize: Option<u32>;
-  readonly sponsoredDataRateLimit: Option<UpDataStructsSponsoringRateLimit>;
-  readonly tokenLimit: Option<u32>;
-  readonly sponsorTransferTimeout: Option<u32>;
-  readonly sponsorApproveTimeout: Option<u32>;
-  readonly ownerCanTransfer: Option<bool>;
-  readonly ownerCanDestroy: Option<bool>;
-  readonly transfersEnabled: Option<bool>;
-}
-
-/** @name UpDataStructsCollectionMode */
-export interface UpDataStructsCollectionMode extends Enum {
-  readonly isNft: boolean;
-  readonly isFungible: boolean;
-  readonly asFungible: u8;
-  readonly isReFungible: boolean;
-  readonly type: 'Nft' | 'Fungible' | 'ReFungible';
-}
-
-/** @name UpDataStructsCollectionPermissions */
-export interface UpDataStructsCollectionPermissions extends Struct {
-  readonly access: Option<UpDataStructsAccessMode>;
-  readonly mintMode: Option<bool>;
-  readonly nesting: Option<UpDataStructsNestingPermissions>;
-}
-
-/** @name UpDataStructsCollectionStats */
-export interface UpDataStructsCollectionStats extends Struct {
-  readonly created: u32;
-  readonly destroyed: u32;
-  readonly alive: u32;
-}
-
-/** @name UpDataStructsCreateCollectionData */
-export interface UpDataStructsCreateCollectionData extends Struct {
-  readonly mode: UpDataStructsCollectionMode;
-  readonly access: Option<UpDataStructsAccessMode>;
-  readonly name: Vec<u16>;
-  readonly description: Vec<u16>;
-  readonly tokenPrefix: Bytes;
-  readonly limits: Option<UpDataStructsCollectionLimits>;
-  readonly permissions: Option<UpDataStructsCollectionPermissions>;
-  readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
-  readonly properties: Vec<UpDataStructsProperty>;
-  readonly adminList: Vec<PalletEvmAccountBasicCrossAccountIdRepr>;
-  readonly pendingSponsor: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
-  readonly flags: U8aFixed;
-}
-
-/** @name UpDataStructsCreateFungibleData */
-export interface UpDataStructsCreateFungibleData extends Struct {
-  readonly value: u128;
-}
-
-/** @name UpDataStructsCreateItemData */
-export interface UpDataStructsCreateItemData extends Enum {
-  readonly isNft: boolean;
-  readonly asNft: UpDataStructsCreateNftData;
-  readonly isFungible: boolean;
-  readonly asFungible: UpDataStructsCreateFungibleData;
-  readonly isReFungible: boolean;
-  readonly asReFungible: UpDataStructsCreateReFungibleData;
-  readonly type: 'Nft' | 'Fungible' | 'ReFungible';
-}
-
-/** @name UpDataStructsCreateItemExData */
-export interface UpDataStructsCreateItemExData extends Enum {
-  readonly isNft: boolean;
-  readonly asNft: Vec<UpDataStructsCreateNftExData>;
-  readonly isFungible: boolean;
-  readonly asFungible: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr,u128>;
-  readonly isRefungibleMultipleItems: boolean;
-  readonly asRefungibleMultipleItems: Vec<UpDataStructsCreateRefungibleExSingleOwner>;
-  readonly isRefungibleMultipleOwners: boolean;
-  readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners;
-  readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
-}
-
-/** @name UpDataStructsCreateNftData */
-export interface UpDataStructsCreateNftData extends Struct {
-  readonly properties: Vec<UpDataStructsProperty>;
-}
-
-/** @name UpDataStructsCreateNftExData */
-export interface UpDataStructsCreateNftExData extends Struct {
-  readonly properties: Vec<UpDataStructsProperty>;
-  readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-}
-
-/** @name UpDataStructsCreateReFungibleData */
-export interface UpDataStructsCreateReFungibleData extends Struct {
-  readonly pieces: u128;
-  readonly properties: Vec<UpDataStructsProperty>;
-}
-
-/** @name UpDataStructsCreateRefungibleExMultipleOwners */
-export interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct {
-  readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
-  readonly properties: Vec<UpDataStructsProperty>;
-}
-
-/** @name UpDataStructsCreateRefungibleExSingleOwner */
-export interface UpDataStructsCreateRefungibleExSingleOwner extends Struct {
-  readonly user: PalletEvmAccountBasicCrossAccountIdRepr;
-  readonly pieces: u128;
-  readonly properties: Vec<UpDataStructsProperty>;
-}
-
-/** @name UpDataStructsNestingPermissions */
-export interface UpDataStructsNestingPermissions extends Struct {
-  readonly tokenOwner: bool;
-  readonly collectionAdmin: bool;
-  readonly restricted: Option<UpDataStructsOwnerRestrictedSet>;
-}
-
-/** @name UpDataStructsOwnerRestrictedSet */
-export interface UpDataStructsOwnerRestrictedSet extends BTreeSet<u32> {}
-
-/** @name UpDataStructsProperties */
-export interface UpDataStructsProperties extends Struct {
-  readonly map: UpDataStructsPropertiesMapBoundedVec;
-  readonly consumedSpace: u32;
-  readonly reserved: u32;
-}
-
-/** @name UpDataStructsPropertiesMapBoundedVec */
-export interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap<Bytes, Bytes> {}
-
-/** @name UpDataStructsPropertiesMapPropertyPermission */
-export interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap<Bytes, UpDataStructsPropertyPermission> {}
-
-/** @name UpDataStructsProperty */
-export interface UpDataStructsProperty extends Struct {
-  readonly key: Bytes;
-  readonly value: Bytes;
-}
-
-/** @name UpDataStructsPropertyKeyPermission */
-export interface UpDataStructsPropertyKeyPermission extends Struct {
-  readonly key: Bytes;
-  readonly permission: UpDataStructsPropertyPermission;
-}
-
-/** @name UpDataStructsPropertyPermission */
-export interface UpDataStructsPropertyPermission extends Struct {
-  readonly mutable: bool;
-  readonly collectionAdmin: bool;
-  readonly tokenOwner: bool;
-}
-
-/** @name UpDataStructsPropertyScope */
-export interface UpDataStructsPropertyScope extends Enum {
-  readonly isNone: boolean;
-  readonly isRmrk: boolean;
-  readonly type: 'None' | 'Rmrk';
-}
-
-/** @name UpDataStructsRpcCollection */
-export interface UpDataStructsRpcCollection extends Struct {
-  readonly owner: AccountId32;
-  readonly mode: UpDataStructsCollectionMode;
-  readonly name: Vec<u16>;
-  readonly description: Vec<u16>;
-  readonly tokenPrefix: Bytes;
-  readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
-  readonly limits: UpDataStructsCollectionLimits;
-  readonly permissions: UpDataStructsCollectionPermissions;
-  readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
-  readonly properties: Vec<UpDataStructsProperty>;
-  readonly readOnly: bool;
-  readonly flags: UpDataStructsRpcCollectionFlags;
-}
-
-/** @name UpDataStructsRpcCollectionFlags */
-export interface UpDataStructsRpcCollectionFlags extends Struct {
-  readonly foreign: bool;
-  readonly erc721metadata: bool;
-}
-
-/** @name UpDataStructsSponsoringRateLimit */
-export interface UpDataStructsSponsoringRateLimit extends Enum {
-  readonly isSponsoringDisabled: boolean;
-  readonly isBlocks: boolean;
-  readonly asBlocks: u32;
-  readonly type: 'SponsoringDisabled' | 'Blocks';
-}
-
-/** @name UpDataStructsSponsorshipStateAccountId32 */
-export interface UpDataStructsSponsorshipStateAccountId32 extends Enum {
-  readonly isDisabled: boolean;
-  readonly isUnconfirmed: boolean;
-  readonly asUnconfirmed: AccountId32;
-  readonly isConfirmed: boolean;
-  readonly asConfirmed: AccountId32;
-  readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
-}
-
-/** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr */
-export interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum {
-  readonly isDisabled: boolean;
-  readonly isUnconfirmed: boolean;
-  readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
-  readonly isConfirmed: boolean;
-  readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
-  readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
-}
-
-/** @name UpDataStructsTokenChild */
-export interface UpDataStructsTokenChild extends Struct {
-  readonly token: u32;
-  readonly collection: u32;
-}
-
-/** @name UpDataStructsTokenData */
-export interface UpDataStructsTokenData extends Struct {
-  readonly properties: Vec<UpDataStructsProperty>;
-  readonly owner: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
-  readonly pieces: u128;
-}
-
-/** @name UpPovEstimateRpcPovInfo */
-export interface UpPovEstimateRpcPovInfo extends Struct {
-  readonly proofSize: u64;
-  readonly compactProofSize: u64;
-  readonly compressedProofSize: u64;
-  readonly results: Vec<Result<Result<Null, SpRuntimeDispatchError>, SpRuntimeTransactionValidityTransactionValidityError>>;
-  readonly keyValues: Vec<UpPovEstimateRpcTrieKeyValue>;
-}
-
-/** @name UpPovEstimateRpcTrieKeyValue */
-export interface UpPovEstimateRpcTrieKeyValue extends Struct {
-  readonly key: Bytes;
-  readonly value: Bytes;
-}
-
-export type PHANTOM_DEFAULT = 'default';
deletedjs-packages/types/src/definitions.tsdiffbeforeafterboth
--- a/js-packages/types/src/definitions.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-export {default as unique} from './unique/definitions.js';
-export {default as appPromotion} from './appPromotion/definitions.js';
-export {default as povinfo} from './povinfo/definitions.js';
-export {default as default} from './default/definitions.js';
deletedjs-packages/types/src/index.tsdiffbeforeafterboth
--- a/js-packages/types/src/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export * from './types.js';
deletedjs-packages/types/src/lookup.tsdiffbeforeafterboth
--- a/js-packages/types/src/lookup.ts
+++ /dev/null
@@ -1,5165 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-/* eslint-disable sort-keys */
-
-export default {
-  /**
-   * Lookup3: frame_system::AccountInfo<Nonce, pallet_balances::types::AccountData<Balance>>
-   **/
-  FrameSystemAccountInfo: {
-    nonce: 'u32',
-    consumers: 'u32',
-    providers: 'u32',
-    sufficients: 'u32',
-    data: 'PalletBalancesAccountData'
-  },
-  /**
-   * Lookup5: pallet_balances::types::AccountData<Balance>
-   **/
-  PalletBalancesAccountData: {
-    free: 'u128',
-    reserved: 'u128',
-    frozen: 'u128',
-    flags: 'u128'
-  },
-  /**
-   * Lookup8: frame_support::dispatch::PerDispatchClass<sp_weights::weight_v2::Weight>
-   **/
-  FrameSupportDispatchPerDispatchClassWeight: {
-    normal: 'SpWeightsWeightV2Weight',
-    operational: 'SpWeightsWeightV2Weight',
-    mandatory: 'SpWeightsWeightV2Weight'
-  },
-  /**
-   * Lookup9: sp_weights::weight_v2::Weight
-   **/
-  SpWeightsWeightV2Weight: {
-    refTime: 'Compact<u64>',
-    proofSize: 'Compact<u64>'
-  },
-  /**
-   * Lookup14: sp_runtime::generic::digest::Digest
-   **/
-  SpRuntimeDigest: {
-    logs: 'Vec<SpRuntimeDigestDigestItem>'
-  },
-  /**
-   * Lookup16: sp_runtime::generic::digest::DigestItem
-   **/
-  SpRuntimeDigestDigestItem: {
-    _enum: {
-      Other: 'Bytes',
-      __Unused1: 'Null',
-      __Unused2: 'Null',
-      __Unused3: 'Null',
-      Consensus: '([u8;4],Bytes)',
-      Seal: '([u8;4],Bytes)',
-      PreRuntime: '([u8;4],Bytes)',
-      __Unused7: 'Null',
-      RuntimeEnvironmentUpdated: 'Null'
-    }
-  },
-  /**
-   * Lookup19: frame_system::EventRecord<opal_runtime::RuntimeEvent, primitive_types::H256>
-   **/
-  FrameSystemEventRecord: {
-    phase: 'FrameSystemPhase',
-    event: 'Event',
-    topics: 'Vec<H256>'
-  },
-  /**
-   * Lookup21: frame_system::pallet::Event<T>
-   **/
-  FrameSystemEvent: {
-    _enum: {
-      ExtrinsicSuccess: {
-        dispatchInfo: 'FrameSupportDispatchDispatchInfo',
-      },
-      ExtrinsicFailed: {
-        dispatchError: 'SpRuntimeDispatchError',
-        dispatchInfo: 'FrameSupportDispatchDispatchInfo',
-      },
-      CodeUpdated: 'Null',
-      NewAccount: {
-        account: 'AccountId32',
-      },
-      KilledAccount: {
-        account: 'AccountId32',
-      },
-      Remarked: {
-        _alias: {
-          hash_: 'hash',
-        },
-        sender: 'AccountId32',
-        hash_: 'H256'
-      }
-    }
-  },
-  /**
-   * Lookup22: frame_support::dispatch::DispatchInfo
-   **/
-  FrameSupportDispatchDispatchInfo: {
-    weight: 'SpWeightsWeightV2Weight',
-    class: 'FrameSupportDispatchDispatchClass',
-    paysFee: 'FrameSupportDispatchPays'
-  },
-  /**
-   * Lookup23: frame_support::dispatch::DispatchClass
-   **/
-  FrameSupportDispatchDispatchClass: {
-    _enum: ['Normal', 'Operational', 'Mandatory']
-  },
-  /**
-   * Lookup24: frame_support::dispatch::Pays
-   **/
-  FrameSupportDispatchPays: {
-    _enum: ['Yes', 'No']
-  },
-  /**
-   * Lookup25: sp_runtime::DispatchError
-   **/
-  SpRuntimeDispatchError: {
-    _enum: {
-      Other: 'Null',
-      CannotLookup: 'Null',
-      BadOrigin: 'Null',
-      Module: 'SpRuntimeModuleError',
-      ConsumerRemaining: 'Null',
-      NoProviders: 'Null',
-      TooManyConsumers: 'Null',
-      Token: 'SpRuntimeTokenError',
-      Arithmetic: 'SpArithmeticArithmeticError',
-      Transactional: 'SpRuntimeTransactionalError',
-      Exhausted: 'Null',
-      Corruption: 'Null',
-      Unavailable: 'Null',
-      RootNotAllowed: 'Null'
-    }
-  },
-  /**
-   * Lookup26: sp_runtime::ModuleError
-   **/
-  SpRuntimeModuleError: {
-    index: 'u8',
-    error: '[u8;4]'
-  },
-  /**
-   * Lookup27: sp_runtime::TokenError
-   **/
-  SpRuntimeTokenError: {
-    _enum: ['FundsUnavailable', 'OnlyProvider', 'BelowMinimum', 'CannotCreate', 'UnknownAsset', 'Frozen', 'Unsupported', 'CannotCreateHold', 'NotExpendable', 'Blocked']
-  },
-  /**
-   * Lookup28: sp_arithmetic::ArithmeticError
-   **/
-  SpArithmeticArithmeticError: {
-    _enum: ['Underflow', 'Overflow', 'DivisionByZero']
-  },
-  /**
-   * Lookup29: sp_runtime::TransactionalError
-   **/
-  SpRuntimeTransactionalError: {
-    _enum: ['LimitReached', 'NoLayer']
-  },
-  /**
-   * Lookup30: pallet_state_trie_migration::pallet::Event<T>
-   **/
-  PalletStateTrieMigrationEvent: {
-    _enum: {
-      Migrated: {
-        top: 'u32',
-        child: 'u32',
-        compute: 'PalletStateTrieMigrationMigrationCompute',
-      },
-      Slashed: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      AutoMigrationFinished: 'Null',
-      Halted: {
-        error: 'PalletStateTrieMigrationError'
-      }
-    }
-  },
-  /**
-   * Lookup31: pallet_state_trie_migration::pallet::MigrationCompute
-   **/
-  PalletStateTrieMigrationMigrationCompute: {
-    _enum: ['Signed', 'Auto']
-  },
-  /**
-   * Lookup32: pallet_state_trie_migration::pallet::Error<T>
-   **/
-  PalletStateTrieMigrationError: {
-    _enum: ['MaxSignedLimits', 'KeyTooLong', 'NotEnoughFunds', 'BadWitness', 'SignedMigrationNotAllowed', 'BadChildRoot']
-  },
-  /**
-   * Lookup33: cumulus_pallet_parachain_system::pallet::Event<T>
-   **/
-  CumulusPalletParachainSystemEvent: {
-    _enum: {
-      ValidationFunctionStored: 'Null',
-      ValidationFunctionApplied: {
-        relayChainBlockNum: 'u32',
-      },
-      ValidationFunctionDiscarded: 'Null',
-      UpgradeAuthorized: {
-        codeHash: 'H256',
-      },
-      DownwardMessagesReceived: {
-        count: 'u32',
-      },
-      DownwardMessagesProcessed: {
-        weightUsed: 'SpWeightsWeightV2Weight',
-        dmqHead: 'H256',
-      },
-      UpwardMessageSent: {
-        messageHash: 'Option<[u8;32]>'
-      }
-    }
-  },
-  /**
-   * Lookup35: pallet_collator_selection::pallet::Event<T>
-   **/
-  PalletCollatorSelectionEvent: {
-    _enum: {
-      InvulnerableAdded: {
-        invulnerable: 'AccountId32',
-      },
-      InvulnerableRemoved: {
-        invulnerable: 'AccountId32',
-      },
-      LicenseObtained: {
-        accountId: 'AccountId32',
-        deposit: 'u128',
-      },
-      LicenseReleased: {
-        accountId: 'AccountId32',
-        depositReturned: 'u128',
-      },
-      CandidateAdded: {
-        accountId: 'AccountId32',
-      },
-      CandidateRemoved: {
-        accountId: 'AccountId32'
-      }
-    }
-  },
-  /**
-   * Lookup36: pallet_session::pallet::Event
-   **/
-  PalletSessionEvent: {
-    _enum: {
-      NewSession: {
-        sessionIndex: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup37: pallet_balances::pallet::Event<T, I>
-   **/
-  PalletBalancesEvent: {
-    _enum: {
-      Endowed: {
-        account: 'AccountId32',
-        freeBalance: 'u128',
-      },
-      DustLost: {
-        account: 'AccountId32',
-        amount: 'u128',
-      },
-      Transfer: {
-        from: 'AccountId32',
-        to: 'AccountId32',
-        amount: 'u128',
-      },
-      BalanceSet: {
-        who: 'AccountId32',
-        free: 'u128',
-      },
-      Reserved: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Unreserved: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      ReserveRepatriated: {
-        from: 'AccountId32',
-        to: 'AccountId32',
-        amount: 'u128',
-        destinationStatus: 'FrameSupportTokensMiscBalanceStatus',
-      },
-      Deposit: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Withdraw: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Slashed: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Minted: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Burned: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Suspended: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Restored: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Upgraded: {
-        who: 'AccountId32',
-      },
-      Issued: {
-        amount: 'u128',
-      },
-      Rescinded: {
-        amount: 'u128',
-      },
-      Locked: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Unlocked: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Frozen: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Thawed: {
-        who: 'AccountId32',
-        amount: 'u128'
-      }
-    }
-  },
-  /**
-   * Lookup38: frame_support::traits::tokens::misc::BalanceStatus
-   **/
-  FrameSupportTokensMiscBalanceStatus: {
-    _enum: ['Free', 'Reserved']
-  },
-  /**
-   * Lookup39: pallet_transaction_payment::pallet::Event<T>
-   **/
-  PalletTransactionPaymentEvent: {
-    _enum: {
-      TransactionFeePaid: {
-        who: 'AccountId32',
-        actualFee: 'u128',
-        tip: 'u128'
-      }
-    }
-  },
-  /**
-   * Lookup40: pallet_treasury::pallet::Event<T, I>
-   **/
-  PalletTreasuryEvent: {
-    _enum: {
-      Proposed: {
-        proposalIndex: 'u32',
-      },
-      Spending: {
-        budgetRemaining: 'u128',
-      },
-      Awarded: {
-        proposalIndex: 'u32',
-        award: 'u128',
-        account: 'AccountId32',
-      },
-      Rejected: {
-        proposalIndex: 'u32',
-        slashed: 'u128',
-      },
-      Burnt: {
-        burntFunds: 'u128',
-      },
-      Rollover: {
-        rolloverBalance: 'u128',
-      },
-      Deposit: {
-        value: 'u128',
-      },
-      SpendApproved: {
-        proposalIndex: 'u32',
-        amount: 'u128',
-        beneficiary: 'AccountId32',
-      },
-      UpdatedInactive: {
-        reactivated: 'u128',
-        deactivated: 'u128'
-      }
-    }
-  },
-  /**
-   * Lookup41: pallet_sudo::pallet::Event<T>
-   **/
-  PalletSudoEvent: {
-    _enum: {
-      Sudid: {
-        sudoResult: 'Result<Null, SpRuntimeDispatchError>',
-      },
-      KeyChanged: {
-        oldSudoer: 'Option<AccountId32>',
-      },
-      SudoAsDone: {
-        sudoResult: 'Result<Null, SpRuntimeDispatchError>'
-      }
-    }
-  },
-  /**
-   * Lookup45: orml_vesting::module::Event<T>
-   **/
-  OrmlVestingModuleEvent: {
-    _enum: {
-      VestingScheduleAdded: {
-        from: 'AccountId32',
-        to: 'AccountId32',
-        vestingSchedule: 'OrmlVestingVestingSchedule',
-      },
-      Claimed: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      VestingSchedulesUpdated: {
-        who: 'AccountId32'
-      }
-    }
-  },
-  /**
-   * Lookup46: orml_vesting::VestingSchedule<BlockNumber, Balance>
-   **/
-  OrmlVestingVestingSchedule: {
-    start: 'u32',
-    period: 'u32',
-    periodCount: 'u32',
-    perPeriod: 'Compact<u128>'
-  },
-  /**
-   * Lookup48: orml_xtokens::module::Event<T>
-   **/
-  OrmlXtokensModuleEvent: {
-    _enum: {
-      TransferredMultiAssets: {
-        sender: 'AccountId32',
-        assets: 'StagingXcmV3MultiassetMultiAssets',
-        fee: 'StagingXcmV3MultiAsset',
-        dest: 'StagingXcmV3MultiLocation'
-      }
-    }
-  },
-  /**
-   * Lookup49: staging_xcm::v3::multiasset::MultiAssets
-   **/
-  StagingXcmV3MultiassetMultiAssets: 'Vec<StagingXcmV3MultiAsset>',
-  /**
-   * Lookup51: staging_xcm::v3::multiasset::MultiAsset
-   **/
-  StagingXcmV3MultiAsset: {
-    id: 'StagingXcmV3MultiassetAssetId',
-    fun: 'StagingXcmV3MultiassetFungibility'
-  },
-  /**
-   * Lookup52: staging_xcm::v3::multiasset::AssetId
-   **/
-  StagingXcmV3MultiassetAssetId: {
-    _enum: {
-      Concrete: 'StagingXcmV3MultiLocation',
-      Abstract: '[u8;32]'
-    }
-  },
-  /**
-   * Lookup53: staging_xcm::v3::multilocation::MultiLocation
-   **/
-  StagingXcmV3MultiLocation: {
-    parents: 'u8',
-    interior: 'StagingXcmV3Junctions'
-  },
-  /**
-   * Lookup54: staging_xcm::v3::junctions::Junctions
-   **/
-  StagingXcmV3Junctions: {
-    _enum: {
-      Here: 'Null',
-      X1: 'StagingXcmV3Junction',
-      X2: '(StagingXcmV3Junction,StagingXcmV3Junction)',
-      X3: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
-      X4: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
-      X5: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
-      X6: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
-      X7: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)',
-      X8: '(StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction,StagingXcmV3Junction)'
-    }
-  },
-  /**
-   * Lookup55: staging_xcm::v3::junction::Junction
-   **/
-  StagingXcmV3Junction: {
-    _enum: {
-      Parachain: 'Compact<u32>',
-      AccountId32: {
-        network: 'Option<StagingXcmV3JunctionNetworkId>',
-        id: '[u8;32]',
-      },
-      AccountIndex64: {
-        network: 'Option<StagingXcmV3JunctionNetworkId>',
-        index: 'Compact<u64>',
-      },
-      AccountKey20: {
-        network: 'Option<StagingXcmV3JunctionNetworkId>',
-        key: '[u8;20]',
-      },
-      PalletInstance: 'u8',
-      GeneralIndex: 'Compact<u128>',
-      GeneralKey: {
-        length: 'u8',
-        data: '[u8;32]',
-      },
-      OnlyChild: 'Null',
-      Plurality: {
-        id: 'StagingXcmV3JunctionBodyId',
-        part: 'StagingXcmV3JunctionBodyPart',
-      },
-      GlobalConsensus: 'StagingXcmV3JunctionNetworkId'
-    }
-  },
-  /**
-   * Lookup58: staging_xcm::v3::junction::NetworkId
-   **/
-  StagingXcmV3JunctionNetworkId: {
-    _enum: {
-      ByGenesis: '[u8;32]',
-      ByFork: {
-        blockNumber: 'u64',
-        blockHash: '[u8;32]',
-      },
-      Polkadot: 'Null',
-      Kusama: 'Null',
-      Westend: 'Null',
-      Rococo: 'Null',
-      Wococo: 'Null',
-      Ethereum: {
-        chainId: 'Compact<u64>',
-      },
-      BitcoinCore: 'Null',
-      BitcoinCash: 'Null'
-    }
-  },
-  /**
-   * Lookup60: staging_xcm::v3::junction::BodyId
-   **/
-  StagingXcmV3JunctionBodyId: {
-    _enum: {
-      Unit: 'Null',
-      Moniker: '[u8;4]',
-      Index: 'Compact<u32>',
-      Executive: 'Null',
-      Technical: 'Null',
-      Legislative: 'Null',
-      Judicial: 'Null',
-      Defense: 'Null',
-      Administration: 'Null',
-      Treasury: 'Null'
-    }
-  },
-  /**
-   * Lookup61: staging_xcm::v3::junction::BodyPart
-   **/
-  StagingXcmV3JunctionBodyPart: {
-    _enum: {
-      Voice: 'Null',
-      Members: {
-        count: 'Compact<u32>',
-      },
-      Fraction: {
-        nom: 'Compact<u32>',
-        denom: 'Compact<u32>',
-      },
-      AtLeastProportion: {
-        nom: 'Compact<u32>',
-        denom: 'Compact<u32>',
-      },
-      MoreThanProportion: {
-        nom: 'Compact<u32>',
-        denom: 'Compact<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup62: staging_xcm::v3::multiasset::Fungibility
-   **/
-  StagingXcmV3MultiassetFungibility: {
-    _enum: {
-      Fungible: 'Compact<u128>',
-      NonFungible: 'StagingXcmV3MultiassetAssetInstance'
-    }
-  },
-  /**
-   * Lookup63: staging_xcm::v3::multiasset::AssetInstance
-   **/
-  StagingXcmV3MultiassetAssetInstance: {
-    _enum: {
-      Undefined: 'Null',
-      Index: 'Compact<u128>',
-      Array4: '[u8;4]',
-      Array8: '[u8;8]',
-      Array16: '[u8;16]',
-      Array32: '[u8;32]'
-    }
-  },
-  /**
-   * Lookup66: orml_tokens::module::Event<T>
-   **/
-  OrmlTokensModuleEvent: {
-    _enum: {
-      Endowed: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      DustLost: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Transfer: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        from: 'AccountId32',
-        to: 'AccountId32',
-        amount: 'u128',
-      },
-      Reserved: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Unreserved: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      ReserveRepatriated: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        from: 'AccountId32',
-        to: 'AccountId32',
-        amount: 'u128',
-        status: 'FrameSupportTokensMiscBalanceStatus',
-      },
-      BalanceSet: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        free: 'u128',
-        reserved: 'u128',
-      },
-      TotalIssuanceSet: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'u128',
-      },
-      Withdrawn: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Slashed: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        freeAmount: 'u128',
-        reservedAmount: 'u128',
-      },
-      Deposited: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      LockSet: {
-        lockId: '[u8;8]',
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      LockRemoved: {
-        lockId: '[u8;8]',
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-      },
-      Locked: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Unlocked: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      Issued: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'u128',
-      },
-      Rescinded: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'u128'
-      }
-    }
-  },
-  /**
-   * Lookup67: pallet_foreign_assets::AssetId
-   **/
-  PalletForeignAssetsAssetId: {
-    _enum: {
-      ForeignAssetId: 'u32',
-      NativeAssetId: 'PalletForeignAssetsNativeCurrency'
-    }
-  },
-  /**
-   * Lookup68: pallet_foreign_assets::NativeCurrency
-   **/
-  PalletForeignAssetsNativeCurrency: {
-    _enum: ['Here', 'Parent']
-  },
-  /**
-   * Lookup69: pallet_identity::pallet::Event<T>
-   **/
-  PalletIdentityEvent: {
-    _enum: {
-      IdentitySet: {
-        who: 'AccountId32',
-      },
-      IdentityCleared: {
-        who: 'AccountId32',
-        deposit: 'u128',
-      },
-      IdentityKilled: {
-        who: 'AccountId32',
-        deposit: 'u128',
-      },
-      IdentitiesInserted: {
-        amount: 'u32',
-      },
-      IdentitiesRemoved: {
-        amount: 'u32',
-      },
-      JudgementRequested: {
-        who: 'AccountId32',
-        registrarIndex: 'u32',
-      },
-      JudgementUnrequested: {
-        who: 'AccountId32',
-        registrarIndex: 'u32',
-      },
-      JudgementGiven: {
-        target: 'AccountId32',
-        registrarIndex: 'u32',
-      },
-      RegistrarAdded: {
-        registrarIndex: 'u32',
-      },
-      SubIdentityAdded: {
-        sub: 'AccountId32',
-        main: 'AccountId32',
-        deposit: 'u128',
-      },
-      SubIdentityRemoved: {
-        sub: 'AccountId32',
-        main: 'AccountId32',
-        deposit: 'u128',
-      },
-      SubIdentityRevoked: {
-        sub: 'AccountId32',
-        main: 'AccountId32',
-        deposit: 'u128',
-      },
-      SubIdentitiesInserted: {
-        amount: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup70: pallet_preimage::pallet::Event<T>
-   **/
-  PalletPreimageEvent: {
-    _enum: {
-      Noted: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-      },
-      Requested: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-      },
-      Cleared: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256'
-      }
-    }
-  },
-  /**
-   * Lookup71: pallet_democracy::pallet::Event<T>
-   **/
-  PalletDemocracyEvent: {
-    _enum: {
-      Proposed: {
-        proposalIndex: 'u32',
-        deposit: 'u128',
-      },
-      Tabled: {
-        proposalIndex: 'u32',
-        deposit: 'u128',
-      },
-      ExternalTabled: 'Null',
-      Started: {
-        refIndex: 'u32',
-        threshold: 'PalletDemocracyVoteThreshold',
-      },
-      Passed: {
-        refIndex: 'u32',
-      },
-      NotPassed: {
-        refIndex: 'u32',
-      },
-      Cancelled: {
-        refIndex: 'u32',
-      },
-      Delegated: {
-        who: 'AccountId32',
-        target: 'AccountId32',
-      },
-      Undelegated: {
-        account: 'AccountId32',
-      },
-      Vetoed: {
-        who: 'AccountId32',
-        proposalHash: 'H256',
-        until: 'u32',
-      },
-      Blacklisted: {
-        proposalHash: 'H256',
-      },
-      Voted: {
-        voter: 'AccountId32',
-        refIndex: 'u32',
-        vote: 'PalletDemocracyVoteAccountVote',
-      },
-      Seconded: {
-        seconder: 'AccountId32',
-        propIndex: 'u32',
-      },
-      ProposalCanceled: {
-        propIndex: 'u32',
-      },
-      MetadataSet: {
-        _alias: {
-          hash_: 'hash',
-        },
-        owner: 'PalletDemocracyMetadataOwner',
-        hash_: 'H256',
-      },
-      MetadataCleared: {
-        _alias: {
-          hash_: 'hash',
-        },
-        owner: 'PalletDemocracyMetadataOwner',
-        hash_: 'H256',
-      },
-      MetadataTransferred: {
-        _alias: {
-          hash_: 'hash',
-        },
-        prevOwner: 'PalletDemocracyMetadataOwner',
-        owner: 'PalletDemocracyMetadataOwner',
-        hash_: 'H256'
-      }
-    }
-  },
-  /**
-   * Lookup72: pallet_democracy::vote_threshold::VoteThreshold
-   **/
-  PalletDemocracyVoteThreshold: {
-    _enum: ['SuperMajorityApprove', 'SuperMajorityAgainst', 'SimpleMajority']
-  },
-  /**
-   * Lookup73: pallet_democracy::vote::AccountVote<Balance>
-   **/
-  PalletDemocracyVoteAccountVote: {
-    _enum: {
-      Standard: {
-        vote: 'Vote',
-        balance: 'u128',
-      },
-      Split: {
-        aye: 'u128',
-        nay: 'u128'
-      }
-    }
-  },
-  /**
-   * Lookup75: pallet_democracy::types::MetadataOwner
-   **/
-  PalletDemocracyMetadataOwner: {
-    _enum: {
-      External: 'Null',
-      Proposal: 'u32',
-      Referendum: 'u32'
-    }
-  },
-  /**
-   * Lookup76: pallet_collective::pallet::Event<T, I>
-   **/
-  PalletCollectiveEvent: {
-    _enum: {
-      Proposed: {
-        account: 'AccountId32',
-        proposalIndex: 'u32',
-        proposalHash: 'H256',
-        threshold: 'u32',
-      },
-      Voted: {
-        account: 'AccountId32',
-        proposalHash: 'H256',
-        voted: 'bool',
-        yes: 'u32',
-        no: 'u32',
-      },
-      Approved: {
-        proposalHash: 'H256',
-      },
-      Disapproved: {
-        proposalHash: 'H256',
-      },
-      Executed: {
-        proposalHash: 'H256',
-        result: 'Result<Null, SpRuntimeDispatchError>',
-      },
-      MemberExecuted: {
-        proposalHash: 'H256',
-        result: 'Result<Null, SpRuntimeDispatchError>',
-      },
-      Closed: {
-        proposalHash: 'H256',
-        yes: 'u32',
-        no: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup79: pallet_membership::pallet::Event<T, I>
-   **/
-  PalletMembershipEvent: {
-    _enum: ['MemberAdded', 'MemberRemoved', 'MembersSwapped', 'MembersReset', 'KeyChanged', 'Dummy']
-  },
-  /**
-   * Lookup81: pallet_ranked_collective::pallet::Event<T, I>
-   **/
-  PalletRankedCollectiveEvent: {
-    _enum: {
-      MemberAdded: {
-        who: 'AccountId32',
-      },
-      RankChanged: {
-        who: 'AccountId32',
-        rank: 'u16',
-      },
-      MemberRemoved: {
-        who: 'AccountId32',
-        rank: 'u16',
-      },
-      Voted: {
-        who: 'AccountId32',
-        poll: 'u32',
-        vote: 'PalletRankedCollectiveVoteRecord',
-        tally: 'PalletRankedCollectiveTally'
-      }
-    }
-  },
-  /**
-   * Lookup83: pallet_ranked_collective::VoteRecord
-   **/
-  PalletRankedCollectiveVoteRecord: {
-    _enum: {
-      Aye: 'u32',
-      Nay: 'u32'
-    }
-  },
-  /**
-   * Lookup84: pallet_ranked_collective::Tally<T, I, M>
-   **/
-  PalletRankedCollectiveTally: {
-    bareAyes: 'u32',
-    ayes: 'u32',
-    nays: 'u32'
-  },
-  /**
-   * Lookup85: pallet_referenda::pallet::Event<T, I>
-   **/
-  PalletReferendaEvent: {
-    _enum: {
-      Submitted: {
-        index: 'u32',
-        track: 'u16',
-        proposal: 'FrameSupportPreimagesBounded',
-      },
-      DecisionDepositPlaced: {
-        index: 'u32',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      DecisionDepositRefunded: {
-        index: 'u32',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      DepositSlashed: {
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      DecisionStarted: {
-        index: 'u32',
-        track: 'u16',
-        proposal: 'FrameSupportPreimagesBounded',
-        tally: 'PalletRankedCollectiveTally',
-      },
-      ConfirmStarted: {
-        index: 'u32',
-      },
-      ConfirmAborted: {
-        index: 'u32',
-      },
-      Confirmed: {
-        index: 'u32',
-        tally: 'PalletRankedCollectiveTally',
-      },
-      Approved: {
-        index: 'u32',
-      },
-      Rejected: {
-        index: 'u32',
-        tally: 'PalletRankedCollectiveTally',
-      },
-      TimedOut: {
-        index: 'u32',
-        tally: 'PalletRankedCollectiveTally',
-      },
-      Cancelled: {
-        index: 'u32',
-        tally: 'PalletRankedCollectiveTally',
-      },
-      Killed: {
-        index: 'u32',
-        tally: 'PalletRankedCollectiveTally',
-      },
-      SubmissionDepositRefunded: {
-        index: 'u32',
-        who: 'AccountId32',
-        amount: 'u128',
-      },
-      MetadataSet: {
-        _alias: {
-          hash_: 'hash',
-        },
-        index: 'u32',
-        hash_: 'H256',
-      },
-      MetadataCleared: {
-        _alias: {
-          hash_: 'hash',
-        },
-        index: 'u32',
-        hash_: 'H256'
-      }
-    }
-  },
-  /**
-   * Lookup86: frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>
-   **/
-  FrameSupportPreimagesBounded: {
-    _enum: {
-      Legacy: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-      },
-      Inline: 'Bytes',
-      Lookup: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-        len: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup88: frame_system::pallet::Call<T>
-   **/
-  FrameSystemCall: {
-    _enum: {
-      remark: {
-        remark: 'Bytes',
-      },
-      set_heap_pages: {
-        pages: 'u64',
-      },
-      set_code: {
-        code: 'Bytes',
-      },
-      set_code_without_checks: {
-        code: 'Bytes',
-      },
-      set_storage: {
-        items: 'Vec<(Bytes,Bytes)>',
-      },
-      kill_storage: {
-        _alias: {
-          keys_: 'keys',
-        },
-        keys_: 'Vec<Bytes>',
-      },
-      kill_prefix: {
-        prefix: 'Bytes',
-        subkeys: 'u32',
-      },
-      remark_with_event: {
-        remark: 'Bytes'
-      }
-    }
-  },
-  /**
-   * Lookup92: pallet_state_trie_migration::pallet::Call<T>
-   **/
-  PalletStateTrieMigrationCall: {
-    _enum: {
-      control_auto_migration: {
-        maybeConfig: 'Option<PalletStateTrieMigrationMigrationLimits>',
-      },
-      continue_migrate: {
-        limits: 'PalletStateTrieMigrationMigrationLimits',
-        realSizeUpper: 'u32',
-        witnessTask: 'PalletStateTrieMigrationMigrationTask',
-      },
-      migrate_custom_top: {
-        _alias: {
-          keys_: 'keys',
-        },
-        keys_: 'Vec<Bytes>',
-        witnessSize: 'u32',
-      },
-      migrate_custom_child: {
-        root: 'Bytes',
-        childKeys: 'Vec<Bytes>',
-        totalSize: 'u32',
-      },
-      set_signed_max_limits: {
-        limits: 'PalletStateTrieMigrationMigrationLimits',
-      },
-      force_set_progress: {
-        progressTop: 'PalletStateTrieMigrationProgress',
-        progressChild: 'PalletStateTrieMigrationProgress'
-      }
-    }
-  },
-  /**
-   * Lookup94: pallet_state_trie_migration::pallet::MigrationLimits
-   **/
-  PalletStateTrieMigrationMigrationLimits: {
-    _alias: {
-      size_: 'size'
-    },
-    size_: 'u32',
-    item: 'u32'
-  },
-  /**
-   * Lookup95: pallet_state_trie_migration::pallet::MigrationTask<T>
-   **/
-  PalletStateTrieMigrationMigrationTask: {
-    _alias: {
-      size_: 'size'
-    },
-    progressTop: 'PalletStateTrieMigrationProgress',
-    progressChild: 'PalletStateTrieMigrationProgress',
-    size_: 'u32',
-    topItems: 'u32',
-    childItems: 'u32'
-  },
-  /**
-   * Lookup96: pallet_state_trie_migration::pallet::Progress<MaxKeyLen>
-   **/
-  PalletStateTrieMigrationProgress: {
-    _enum: {
-      ToStart: 'Null',
-      LastKey: 'Bytes',
-      Complete: 'Null'
-    }
-  },
-  /**
-   * Lookup98: cumulus_pallet_parachain_system::pallet::Call<T>
-   **/
-  CumulusPalletParachainSystemCall: {
-    _enum: {
-      set_validation_data: {
-        data: 'CumulusPrimitivesParachainInherentParachainInherentData',
-      },
-      sudo_send_upward_message: {
-        message: 'Bytes',
-      },
-      authorize_upgrade: {
-        codeHash: 'H256',
-        checkVersion: 'bool',
-      },
-      enact_authorized_upgrade: {
-        code: 'Bytes'
-      }
-    }
-  },
-  /**
-   * Lookup99: cumulus_primitives_parachain_inherent::ParachainInherentData
-   **/
-  CumulusPrimitivesParachainInherentParachainInherentData: {
-    validationData: 'PolkadotPrimitivesV5PersistedValidationData',
-    relayChainState: 'SpTrieStorageProof',
-    downwardMessages: 'Vec<PolkadotCorePrimitivesInboundDownwardMessage>',
-    horizontalMessages: 'BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>'
-  },
-  /**
-   * Lookup100: polkadot_primitives::v5::PersistedValidationData<primitive_types::H256, N>
-   **/
-  PolkadotPrimitivesV5PersistedValidationData: {
-    parentHead: 'Bytes',
-    relayParentNumber: 'u32',
-    relayParentStorageRoot: 'H256',
-    maxPovSize: 'u32'
-  },
-  /**
-   * Lookup102: sp_trie::storage_proof::StorageProof
-   **/
-  SpTrieStorageProof: {
-    trieNodes: 'BTreeSet<Bytes>'
-  },
-  /**
-   * Lookup105: polkadot_core_primitives::InboundDownwardMessage<BlockNumber>
-   **/
-  PolkadotCorePrimitivesInboundDownwardMessage: {
-    sentAt: 'u32',
-    msg: 'Bytes'
-  },
-  /**
-   * Lookup109: polkadot_core_primitives::InboundHrmpMessage<BlockNumber>
-   **/
-  PolkadotCorePrimitivesInboundHrmpMessage: {
-    sentAt: 'u32',
-    data: 'Bytes'
-  },
-  /**
-   * Lookup112: parachain_info::pallet::Call<T>
-   **/
-  ParachainInfoCall: 'Null',
-  /**
-   * Lookup113: pallet_collator_selection::pallet::Call<T>
-   **/
-  PalletCollatorSelectionCall: {
-    _enum: {
-      add_invulnerable: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'AccountId32',
-      },
-      remove_invulnerable: {
-        who: 'AccountId32',
-      },
-      get_license: 'Null',
-      onboard: 'Null',
-      offboard: 'Null',
-      release_license: 'Null',
-      force_release_license: {
-        who: 'AccountId32'
-      }
-    }
-  },
-  /**
-   * Lookup114: pallet_session::pallet::Call<T>
-   **/
-  PalletSessionCall: {
-    _enum: {
-      set_keys: {
-        _alias: {
-          keys_: 'keys',
-        },
-        keys_: 'OpalRuntimeRuntimeCommonSessionKeys',
-        proof: 'Bytes',
-      },
-      purge_keys: 'Null'
-    }
-  },
-  /**
-   * Lookup115: opal_runtime::runtime_common::SessionKeys
-   **/
-  OpalRuntimeRuntimeCommonSessionKeys: {
-    aura: 'SpConsensusAuraSr25519AppSr25519Public'
-  },
-  /**
-   * Lookup116: sp_consensus_aura::sr25519::app_sr25519::Public
-   **/
-  SpConsensusAuraSr25519AppSr25519Public: 'SpCoreSr25519Public',
-  /**
-   * Lookup117: sp_core::sr25519::Public
-   **/
-  SpCoreSr25519Public: '[u8;32]',
-  /**
-   * Lookup118: pallet_balances::pallet::Call<T, I>
-   **/
-  PalletBalancesCall: {
-    _enum: {
-      transfer_allow_death: {
-        dest: 'MultiAddress',
-        value: 'Compact<u128>',
-      },
-      set_balance_deprecated: {
-        who: 'MultiAddress',
-        newFree: 'Compact<u128>',
-        oldReserved: 'Compact<u128>',
-      },
-      force_transfer: {
-        source: 'MultiAddress',
-        dest: 'MultiAddress',
-        value: 'Compact<u128>',
-      },
-      transfer_keep_alive: {
-        dest: 'MultiAddress',
-        value: 'Compact<u128>',
-      },
-      transfer_all: {
-        dest: 'MultiAddress',
-        keepAlive: 'bool',
-      },
-      force_unreserve: {
-        who: 'MultiAddress',
-        amount: 'u128',
-      },
-      upgrade_accounts: {
-        who: 'Vec<AccountId32>',
-      },
-      transfer: {
-        dest: 'MultiAddress',
-        value: 'Compact<u128>',
-      },
-      force_set_balance: {
-        who: 'MultiAddress',
-        newFree: 'Compact<u128>'
-      }
-    }
-  },
-  /**
-   * Lookup122: pallet_timestamp::pallet::Call<T>
-   **/
-  PalletTimestampCall: {
-    _enum: {
-      set: {
-        now: 'Compact<u64>'
-      }
-    }
-  },
-  /**
-   * Lookup123: pallet_treasury::pallet::Call<T, I>
-   **/
-  PalletTreasuryCall: {
-    _enum: {
-      propose_spend: {
-        value: 'Compact<u128>',
-        beneficiary: 'MultiAddress',
-      },
-      reject_proposal: {
-        proposalId: 'Compact<u32>',
-      },
-      approve_proposal: {
-        proposalId: 'Compact<u32>',
-      },
-      spend: {
-        amount: 'Compact<u128>',
-        beneficiary: 'MultiAddress',
-      },
-      remove_approval: {
-        proposalId: 'Compact<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup124: pallet_sudo::pallet::Call<T>
-   **/
-  PalletSudoCall: {
-    _enum: {
-      sudo: {
-        call: 'Call',
-      },
-      sudo_unchecked_weight: {
-        call: 'Call',
-        weight: 'SpWeightsWeightV2Weight',
-      },
-      set_key: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'MultiAddress',
-      },
-      sudo_as: {
-        who: 'MultiAddress',
-        call: 'Call'
-      }
-    }
-  },
-  /**
-   * Lookup125: orml_vesting::module::Call<T>
-   **/
-  OrmlVestingModuleCall: {
-    _enum: {
-      claim: 'Null',
-      vested_transfer: {
-        dest: 'MultiAddress',
-        schedule: 'OrmlVestingVestingSchedule',
-      },
-      update_vesting_schedules: {
-        who: 'MultiAddress',
-        vestingSchedules: 'Vec<OrmlVestingVestingSchedule>',
-      },
-      claim_for: {
-        dest: 'MultiAddress'
-      }
-    }
-  },
-  /**
-   * Lookup127: orml_xtokens::module::Call<T>
-   **/
-  OrmlXtokensModuleCall: {
-    _enum: {
-      transfer: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'u128',
-        dest: 'StagingXcmVersionedMultiLocation',
-        destWeightLimit: 'StagingXcmV3WeightLimit',
-      },
-      transfer_multiasset: {
-        asset: 'StagingXcmVersionedMultiAsset',
-        dest: 'StagingXcmVersionedMultiLocation',
-        destWeightLimit: 'StagingXcmV3WeightLimit',
-      },
-      transfer_with_fee: {
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'u128',
-        fee: 'u128',
-        dest: 'StagingXcmVersionedMultiLocation',
-        destWeightLimit: 'StagingXcmV3WeightLimit',
-      },
-      transfer_multiasset_with_fee: {
-        asset: 'StagingXcmVersionedMultiAsset',
-        fee: 'StagingXcmVersionedMultiAsset',
-        dest: 'StagingXcmVersionedMultiLocation',
-        destWeightLimit: 'StagingXcmV3WeightLimit',
-      },
-      transfer_multicurrencies: {
-        currencies: 'Vec<(PalletForeignAssetsAssetId,u128)>',
-        feeItem: 'u32',
-        dest: 'StagingXcmVersionedMultiLocation',
-        destWeightLimit: 'StagingXcmV3WeightLimit',
-      },
-      transfer_multiassets: {
-        assets: 'StagingXcmVersionedMultiAssets',
-        feeItem: 'u32',
-        dest: 'StagingXcmVersionedMultiLocation',
-        destWeightLimit: 'StagingXcmV3WeightLimit'
-      }
-    }
-  },
-  /**
-   * Lookup128: staging_xcm::VersionedMultiLocation
-   **/
-  StagingXcmVersionedMultiLocation: {
-    _enum: {
-      __Unused0: 'Null',
-      V2: 'StagingXcmV2MultiLocation',
-      __Unused2: 'Null',
-      V3: 'StagingXcmV3MultiLocation'
-    }
-  },
-  /**
-   * Lookup129: staging_xcm::v2::multilocation::MultiLocation
-   **/
-  StagingXcmV2MultiLocation: {
-    parents: 'u8',
-    interior: 'StagingXcmV2MultilocationJunctions'
-  },
-  /**
-   * Lookup130: staging_xcm::v2::multilocation::Junctions
-   **/
-  StagingXcmV2MultilocationJunctions: {
-    _enum: {
-      Here: 'Null',
-      X1: 'StagingXcmV2Junction',
-      X2: '(StagingXcmV2Junction,StagingXcmV2Junction)',
-      X3: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
-      X4: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
-      X5: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
-      X6: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
-      X7: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)',
-      X8: '(StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction,StagingXcmV2Junction)'
-    }
-  },
-  /**
-   * Lookup131: staging_xcm::v2::junction::Junction
-   **/
-  StagingXcmV2Junction: {
-    _enum: {
-      Parachain: 'Compact<u32>',
-      AccountId32: {
-        network: 'StagingXcmV2NetworkId',
-        id: '[u8;32]',
-      },
-      AccountIndex64: {
-        network: 'StagingXcmV2NetworkId',
-        index: 'Compact<u64>',
-      },
-      AccountKey20: {
-        network: 'StagingXcmV2NetworkId',
-        key: '[u8;20]',
-      },
-      PalletInstance: 'u8',
-      GeneralIndex: 'Compact<u128>',
-      GeneralKey: 'Bytes',
-      OnlyChild: 'Null',
-      Plurality: {
-        id: 'StagingXcmV2BodyId',
-        part: 'StagingXcmV2BodyPart'
-      }
-    }
-  },
-  /**
-   * Lookup132: staging_xcm::v2::NetworkId
-   **/
-  StagingXcmV2NetworkId: {
-    _enum: {
-      Any: 'Null',
-      Named: 'Bytes',
-      Polkadot: 'Null',
-      Kusama: 'Null'
-    }
-  },
-  /**
-   * Lookup134: staging_xcm::v2::BodyId
-   **/
-  StagingXcmV2BodyId: {
-    _enum: {
-      Unit: 'Null',
-      Named: 'Bytes',
-      Index: 'Compact<u32>',
-      Executive: 'Null',
-      Technical: 'Null',
-      Legislative: 'Null',
-      Judicial: 'Null',
-      Defense: 'Null',
-      Administration: 'Null',
-      Treasury: 'Null'
-    }
-  },
-  /**
-   * Lookup135: staging_xcm::v2::BodyPart
-   **/
-  StagingXcmV2BodyPart: {
-    _enum: {
-      Voice: 'Null',
-      Members: {
-        count: 'Compact<u32>',
-      },
-      Fraction: {
-        nom: 'Compact<u32>',
-        denom: 'Compact<u32>',
-      },
-      AtLeastProportion: {
-        nom: 'Compact<u32>',
-        denom: 'Compact<u32>',
-      },
-      MoreThanProportion: {
-        nom: 'Compact<u32>',
-        denom: 'Compact<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup136: staging_xcm::v3::WeightLimit
-   **/
-  StagingXcmV3WeightLimit: {
-    _enum: {
-      Unlimited: 'Null',
-      Limited: 'SpWeightsWeightV2Weight'
-    }
-  },
-  /**
-   * Lookup137: staging_xcm::VersionedMultiAsset
-   **/
-  StagingXcmVersionedMultiAsset: {
-    _enum: {
-      __Unused0: 'Null',
-      V2: 'StagingXcmV2MultiAsset',
-      __Unused2: 'Null',
-      V3: 'StagingXcmV3MultiAsset'
-    }
-  },
-  /**
-   * Lookup138: staging_xcm::v2::multiasset::MultiAsset
-   **/
-  StagingXcmV2MultiAsset: {
-    id: 'StagingXcmV2MultiassetAssetId',
-    fun: 'StagingXcmV2MultiassetFungibility'
-  },
-  /**
-   * Lookup139: staging_xcm::v2::multiasset::AssetId
-   **/
-  StagingXcmV2MultiassetAssetId: {
-    _enum: {
-      Concrete: 'StagingXcmV2MultiLocation',
-      Abstract: 'Bytes'
-    }
-  },
-  /**
-   * Lookup140: staging_xcm::v2::multiasset::Fungibility
-   **/
-  StagingXcmV2MultiassetFungibility: {
-    _enum: {
-      Fungible: 'Compact<u128>',
-      NonFungible: 'StagingXcmV2MultiassetAssetInstance'
-    }
-  },
-  /**
-   * Lookup141: staging_xcm::v2::multiasset::AssetInstance
-   **/
-  StagingXcmV2MultiassetAssetInstance: {
-    _enum: {
-      Undefined: 'Null',
-      Index: 'Compact<u128>',
-      Array4: '[u8;4]',
-      Array8: '[u8;8]',
-      Array16: '[u8;16]',
-      Array32: '[u8;32]',
-      Blob: 'Bytes'
-    }
-  },
-  /**
-   * Lookup144: staging_xcm::VersionedMultiAssets
-   **/
-  StagingXcmVersionedMultiAssets: {
-    _enum: {
-      __Unused0: 'Null',
-      V2: 'StagingXcmV2MultiassetMultiAssets',
-      __Unused2: 'Null',
-      V3: 'StagingXcmV3MultiassetMultiAssets'
-    }
-  },
-  /**
-   * Lookup145: staging_xcm::v2::multiasset::MultiAssets
-   **/
-  StagingXcmV2MultiassetMultiAssets: 'Vec<StagingXcmV2MultiAsset>',
-  /**
-   * Lookup147: orml_tokens::module::Call<T>
-   **/
-  OrmlTokensModuleCall: {
-    _enum: {
-      transfer: {
-        dest: 'MultiAddress',
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'Compact<u128>',
-      },
-      transfer_all: {
-        dest: 'MultiAddress',
-        currencyId: 'PalletForeignAssetsAssetId',
-        keepAlive: 'bool',
-      },
-      transfer_keep_alive: {
-        dest: 'MultiAddress',
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'Compact<u128>',
-      },
-      force_transfer: {
-        source: 'MultiAddress',
-        dest: 'MultiAddress',
-        currencyId: 'PalletForeignAssetsAssetId',
-        amount: 'Compact<u128>',
-      },
-      set_balance: {
-        who: 'MultiAddress',
-        currencyId: 'PalletForeignAssetsAssetId',
-        newFree: 'Compact<u128>',
-        newReserved: 'Compact<u128>'
-      }
-    }
-  },
-  /**
-   * Lookup148: pallet_identity::pallet::Call<T>
-   **/
-  PalletIdentityCall: {
-    _enum: {
-      add_registrar: {
-        account: 'MultiAddress',
-      },
-      set_identity: {
-        info: 'PalletIdentityIdentityInfo',
-      },
-      set_subs: {
-        subs: 'Vec<(AccountId32,Data)>',
-      },
-      clear_identity: 'Null',
-      request_judgement: {
-        regIndex: 'Compact<u32>',
-        maxFee: 'Compact<u128>',
-      },
-      cancel_request: {
-        regIndex: 'u32',
-      },
-      set_fee: {
-        index: 'Compact<u32>',
-        fee: 'Compact<u128>',
-      },
-      set_account_id: {
-        _alias: {
-          new_: 'new',
-        },
-        index: 'Compact<u32>',
-        new_: 'MultiAddress',
-      },
-      set_fields: {
-        index: 'Compact<u32>',
-        fields: 'PalletIdentityBitFlags',
-      },
-      provide_judgement: {
-        regIndex: 'Compact<u32>',
-        target: 'MultiAddress',
-        judgement: 'PalletIdentityJudgement',
-        identity: 'H256',
-      },
-      kill_identity: {
-        target: 'MultiAddress',
-      },
-      add_sub: {
-        sub: 'MultiAddress',
-        data: 'Data',
-      },
-      rename_sub: {
-        sub: 'MultiAddress',
-        data: 'Data',
-      },
-      remove_sub: {
-        sub: 'MultiAddress',
-      },
-      quit_sub: 'Null',
-      force_insert_identities: {
-        identities: 'Vec<(AccountId32,PalletIdentityRegistration)>',
-      },
-      force_remove_identities: {
-        identities: 'Vec<AccountId32>',
-      },
-      force_set_subs: {
-        subs: 'Vec<(AccountId32,(u128,Vec<(AccountId32,Data)>))>'
-      }
-    }
-  },
-  /**
-   * Lookup149: pallet_identity::types::IdentityInfo<FieldLimit>
-   **/
-  PalletIdentityIdentityInfo: {
-    additional: 'Vec<(Data,Data)>',
-    display: 'Data',
-    legal: 'Data',
-    web: 'Data',
-    riot: 'Data',
-    email: 'Data',
-    pgpFingerprint: 'Option<[u8;20]>',
-    image: 'Data',
-    twitter: 'Data'
-  },
-  /**
-   * Lookup185: pallet_identity::types::BitFlags<pallet_identity::types::IdentityField>
-   **/
-  PalletIdentityBitFlags: {
-    _bitLength: 64,
-    Display: 1,
-    Legal: 2,
-    Web: 4,
-    Riot: 8,
-    Email: 16,
-    PgpFingerprint: 32,
-    Image: 64,
-    Twitter: 128
-  },
-  /**
-   * Lookup186: pallet_identity::types::IdentityField
-   **/
-  PalletIdentityIdentityField: {
-    _enum: ['__Unused0', 'Display', 'Legal', '__Unused3', 'Web', '__Unused5', '__Unused6', '__Unused7', 'Riot', '__Unused9', '__Unused10', '__Unused11', '__Unused12', '__Unused13', '__Unused14', '__Unused15', 'Email', '__Unused17', '__Unused18', '__Unused19', '__Unused20', '__Unused21', '__Unused22', '__Unused23', '__Unused24', '__Unused25', '__Unused26', '__Unused27', '__Unused28', '__Unused29', '__Unused30', '__Unused31', 'PgpFingerprint', '__Unused33', '__Unused34', '__Unused35', '__Unused36', '__Unused37', '__Unused38', '__Unused39', '__Unused40', '__Unused41', '__Unused42', '__Unused43', '__Unused44', '__Unused45', '__Unused46', '__Unused47', '__Unused48', '__Unused49', '__Unused50', '__Unused51', '__Unused52', '__Unused53', '__Unused54', '__Unused55', '__Unused56', '__Unused57', '__Unused58', '__Unused59', '__Unused60', '__Unused61', '__Unused62', '__Unused63', 'Image', '__Unused65', '__Unused66', '__Unused67', '__Unused68', '__Unused69', '__Unused70', '__Unused71', '__Unused72', '__Unused73', '__Unused74', '__Unused75', '__Unused76', '__Unused77', '__Unused78', '__Unused79', '__Unused80', '__Unused81', '__Unused82', '__Unused83', '__Unused84', '__Unused85', '__Unused86', '__Unused87', '__Unused88', '__Unused89', '__Unused90', '__Unused91', '__Unused92', '__Unused93', '__Unused94', '__Unused95', '__Unused96', '__Unused97', '__Unused98', '__Unused99', '__Unused100', '__Unused101', '__Unused102', '__Unused103', '__Unused104', '__Unused105', '__Unused106', '__Unused107', '__Unused108', '__Unused109', '__Unused110', '__Unused111', '__Unused112', '__Unused113', '__Unused114', '__Unused115', '__Unused116', '__Unused117', '__Unused118', '__Unused119', '__Unused120', '__Unused121', '__Unused122', '__Unused123', '__Unused124', '__Unused125', '__Unused126', '__Unused127', 'Twitter']
-  },
-  /**
-   * Lookup187: pallet_identity::types::Judgement<Balance>
-   **/
-  PalletIdentityJudgement: {
-    _enum: {
-      Unknown: 'Null',
-      FeePaid: 'u128',
-      Reasonable: 'Null',
-      KnownGood: 'Null',
-      OutOfDate: 'Null',
-      LowQuality: 'Null',
-      Erroneous: 'Null'
-    }
-  },
-  /**
-   * Lookup190: pallet_identity::types::Registration<Balance, MaxJudgements, MaxAdditionalFields>
-   **/
-  PalletIdentityRegistration: {
-    judgements: 'Vec<(u32,PalletIdentityJudgement)>',
-    deposit: 'u128',
-    info: 'PalletIdentityIdentityInfo'
-  },
-  /**
-   * Lookup198: pallet_preimage::pallet::Call<T>
-   **/
-  PalletPreimageCall: {
-    _enum: {
-      note_preimage: {
-        bytes: 'Bytes',
-      },
-      unnote_preimage: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-      },
-      request_preimage: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-      },
-      unrequest_preimage: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256'
-      }
-    }
-  },
-  /**
-   * Lookup199: pallet_democracy::pallet::Call<T>
-   **/
-  PalletDemocracyCall: {
-    _enum: {
-      propose: {
-        proposal: 'FrameSupportPreimagesBounded',
-        value: 'Compact<u128>',
-      },
-      second: {
-        proposal: 'Compact<u32>',
-      },
-      vote: {
-        refIndex: 'Compact<u32>',
-        vote: 'PalletDemocracyVoteAccountVote',
-      },
-      emergency_cancel: {
-        refIndex: 'u32',
-      },
-      external_propose: {
-        proposal: 'FrameSupportPreimagesBounded',
-      },
-      external_propose_majority: {
-        proposal: 'FrameSupportPreimagesBounded',
-      },
-      external_propose_default: {
-        proposal: 'FrameSupportPreimagesBounded',
-      },
-      fast_track: {
-        proposalHash: 'H256',
-        votingPeriod: 'u32',
-        delay: 'u32',
-      },
-      veto_external: {
-        proposalHash: 'H256',
-      },
-      cancel_referendum: {
-        refIndex: 'Compact<u32>',
-      },
-      delegate: {
-        to: 'MultiAddress',
-        conviction: 'PalletDemocracyConviction',
-        balance: 'u128',
-      },
-      undelegate: 'Null',
-      clear_public_proposals: 'Null',
-      unlock: {
-        target: 'MultiAddress',
-      },
-      remove_vote: {
-        index: 'u32',
-      },
-      remove_other_vote: {
-        target: 'MultiAddress',
-        index: 'u32',
-      },
-      blacklist: {
-        proposalHash: 'H256',
-        maybeRefIndex: 'Option<u32>',
-      },
-      cancel_proposal: {
-        propIndex: 'Compact<u32>',
-      },
-      set_metadata: {
-        owner: 'PalletDemocracyMetadataOwner',
-        maybeHash: 'Option<H256>'
-      }
-    }
-  },
-  /**
-   * Lookup200: pallet_democracy::conviction::Conviction
-   **/
-  PalletDemocracyConviction: {
-    _enum: ['None', 'Locked1x', 'Locked2x', 'Locked3x', 'Locked4x', 'Locked5x', 'Locked6x']
-  },
-  /**
-   * Lookup203: pallet_collective::pallet::Call<T, I>
-   **/
-  PalletCollectiveCall: {
-    _enum: {
-      set_members: {
-        newMembers: 'Vec<AccountId32>',
-        prime: 'Option<AccountId32>',
-        oldCount: 'u32',
-      },
-      execute: {
-        proposal: 'Call',
-        lengthBound: 'Compact<u32>',
-      },
-      propose: {
-        threshold: 'Compact<u32>',
-        proposal: 'Call',
-        lengthBound: 'Compact<u32>',
-      },
-      vote: {
-        proposal: 'H256',
-        index: 'Compact<u32>',
-        approve: 'bool',
-      },
-      __Unused4: 'Null',
-      disapprove_proposal: {
-        proposalHash: 'H256',
-      },
-      close: {
-        proposalHash: 'H256',
-        index: 'Compact<u32>',
-        proposalWeightBound: 'SpWeightsWeightV2Weight',
-        lengthBound: 'Compact<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup205: pallet_membership::pallet::Call<T, I>
-   **/
-  PalletMembershipCall: {
-    _enum: {
-      add_member: {
-        who: 'MultiAddress',
-      },
-      remove_member: {
-        who: 'MultiAddress',
-      },
-      swap_member: {
-        remove: 'MultiAddress',
-        add: 'MultiAddress',
-      },
-      reset_members: {
-        members: 'Vec<AccountId32>',
-      },
-      change_key: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'MultiAddress',
-      },
-      set_prime: {
-        who: 'MultiAddress',
-      },
-      clear_prime: 'Null'
-    }
-  },
-  /**
-   * Lookup207: pallet_ranked_collective::pallet::Call<T, I>
-   **/
-  PalletRankedCollectiveCall: {
-    _enum: {
-      add_member: {
-        who: 'MultiAddress',
-      },
-      promote_member: {
-        who: 'MultiAddress',
-      },
-      demote_member: {
-        who: 'MultiAddress',
-      },
-      remove_member: {
-        who: 'MultiAddress',
-        minRank: 'u16',
-      },
-      vote: {
-        poll: 'u32',
-        aye: 'bool',
-      },
-      cleanup_poll: {
-        pollIndex: 'u32',
-        max: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup208: pallet_referenda::pallet::Call<T, I>
-   **/
-  PalletReferendaCall: {
-    _enum: {
-      submit: {
-        proposalOrigin: 'OpalRuntimeOriginCaller',
-        proposal: 'FrameSupportPreimagesBounded',
-        enactmentMoment: 'FrameSupportScheduleDispatchTime',
-      },
-      place_decision_deposit: {
-        index: 'u32',
-      },
-      refund_decision_deposit: {
-        index: 'u32',
-      },
-      cancel: {
-        index: 'u32',
-      },
-      kill: {
-        index: 'u32',
-      },
-      nudge_referendum: {
-        index: 'u32',
-      },
-      one_fewer_deciding: {
-        track: 'u16',
-      },
-      refund_submission_deposit: {
-        index: 'u32',
-      },
-      set_metadata: {
-        index: 'u32',
-        maybeHash: 'Option<H256>'
-      }
-    }
-  },
-  /**
-   * Lookup209: opal_runtime::OriginCaller
-   **/
-  OpalRuntimeOriginCaller: {
-    _enum: {
-      system: 'FrameSupportDispatchRawOrigin',
-      __Unused1: 'Null',
-      __Unused2: 'Null',
-      __Unused3: 'Null',
-      __Unused4: 'Null',
-      __Unused5: 'Null',
-      __Unused6: 'Null',
-      Void: 'SpCoreVoid',
-      __Unused8: 'Null',
-      __Unused9: 'Null',
-      __Unused10: 'Null',
-      __Unused11: 'Null',
-      __Unused12: 'Null',
-      __Unused13: 'Null',
-      __Unused14: 'Null',
-      __Unused15: 'Null',
-      __Unused16: 'Null',
-      __Unused17: 'Null',
-      __Unused18: 'Null',
-      __Unused19: 'Null',
-      __Unused20: 'Null',
-      __Unused21: 'Null',
-      __Unused22: 'Null',
-      __Unused23: 'Null',
-      __Unused24: 'Null',
-      __Unused25: 'Null',
-      __Unused26: 'Null',
-      __Unused27: 'Null',
-      __Unused28: 'Null',
-      __Unused29: 'Null',
-      __Unused30: 'Null',
-      __Unused31: 'Null',
-      __Unused32: 'Null',
-      __Unused33: 'Null',
-      __Unused34: 'Null',
-      __Unused35: 'Null',
-      __Unused36: 'Null',
-      __Unused37: 'Null',
-      __Unused38: 'Null',
-      __Unused39: 'Null',
-      __Unused40: 'Null',
-      __Unused41: 'Null',
-      __Unused42: 'Null',
-      Council: 'PalletCollectiveRawOrigin',
-      TechnicalCommittee: 'PalletCollectiveRawOrigin',
-      __Unused45: 'Null',
-      __Unused46: 'Null',
-      __Unused47: 'Null',
-      __Unused48: 'Null',
-      __Unused49: 'Null',
-      __Unused50: 'Null',
-      PolkadotXcm: 'PalletXcmOrigin',
-      CumulusXcm: 'CumulusPalletXcmOrigin',
-      __Unused53: 'Null',
-      __Unused54: 'Null',
-      __Unused55: 'Null',
-      __Unused56: 'Null',
-      __Unused57: 'Null',
-      __Unused58: 'Null',
-      __Unused59: 'Null',
-      __Unused60: 'Null',
-      __Unused61: 'Null',
-      __Unused62: 'Null',
-      __Unused63: 'Null',
-      __Unused64: 'Null',
-      __Unused65: 'Null',
-      __Unused66: 'Null',
-      __Unused67: 'Null',
-      __Unused68: 'Null',
-      __Unused69: 'Null',
-      __Unused70: 'Null',
-      __Unused71: 'Null',
-      __Unused72: 'Null',
-      __Unused73: 'Null',
-      __Unused74: 'Null',
-      __Unused75: 'Null',
-      __Unused76: 'Null',
-      __Unused77: 'Null',
-      __Unused78: 'Null',
-      __Unused79: 'Null',
-      __Unused80: 'Null',
-      __Unused81: 'Null',
-      __Unused82: 'Null',
-      __Unused83: 'Null',
-      __Unused84: 'Null',
-      __Unused85: 'Null',
-      __Unused86: 'Null',
-      __Unused87: 'Null',
-      __Unused88: 'Null',
-      __Unused89: 'Null',
-      __Unused90: 'Null',
-      __Unused91: 'Null',
-      __Unused92: 'Null',
-      __Unused93: 'Null',
-      __Unused94: 'Null',
-      __Unused95: 'Null',
-      __Unused96: 'Null',
-      __Unused97: 'Null',
-      __Unused98: 'Null',
-      Origins: 'PalletGovOriginsOrigin',
-      __Unused100: 'Null',
-      Ethereum: 'PalletEthereumRawOrigin'
-    }
-  },
-  /**
-   * Lookup210: frame_support::dispatch::RawOrigin<sp_core::crypto::AccountId32>
-   **/
-  FrameSupportDispatchRawOrigin: {
-    _enum: {
-      Root: 'Null',
-      Signed: 'AccountId32',
-      None: 'Null'
-    }
-  },
-  /**
-   * Lookup211: pallet_collective::RawOrigin<sp_core::crypto::AccountId32, I>
-   **/
-  PalletCollectiveRawOrigin: {
-    _enum: {
-      Members: '(u32,u32)',
-      Member: 'AccountId32',
-      _Phantom: 'Null'
-    }
-  },
-  /**
-   * Lookup213: pallet_gov_origins::pallet::Origin
-   **/
-  PalletGovOriginsOrigin: {
-    _enum: ['FellowshipProposition']
-  },
-  /**
-   * Lookup214: pallet_xcm::pallet::Origin
-   **/
-  PalletXcmOrigin: {
-    _enum: {
-      Xcm: 'StagingXcmV3MultiLocation',
-      Response: 'StagingXcmV3MultiLocation'
-    }
-  },
-  /**
-   * Lookup215: cumulus_pallet_xcm::pallet::Origin
-   **/
-  CumulusPalletXcmOrigin: {
-    _enum: {
-      Relay: 'Null',
-      SiblingParachain: 'u32'
-    }
-  },
-  /**
-   * Lookup216: pallet_ethereum::RawOrigin
-   **/
-  PalletEthereumRawOrigin: {
-    _enum: {
-      EthereumTransaction: 'H160'
-    }
-  },
-  /**
-   * Lookup218: sp_core::Void
-   **/
-  SpCoreVoid: 'Null',
-  /**
-   * Lookup219: frame_support::traits::schedule::DispatchTime<BlockNumber>
-   **/
-  FrameSupportScheduleDispatchTime: {
-    _enum: {
-      At: 'u32',
-      After: 'u32'
-    }
-  },
-  /**
-   * Lookup220: pallet_scheduler::pallet::Call<T>
-   **/
-  PalletSchedulerCall: {
-    _enum: {
-      schedule: {
-        when: 'u32',
-        maybePeriodic: 'Option<(u32,u32)>',
-        priority: 'u8',
-        call: 'Call',
-      },
-      cancel: {
-        when: 'u32',
-        index: 'u32',
-      },
-      schedule_named: {
-        id: '[u8;32]',
-        when: 'u32',
-        maybePeriodic: 'Option<(u32,u32)>',
-        priority: 'u8',
-        call: 'Call',
-      },
-      cancel_named: {
-        id: '[u8;32]',
-      },
-      schedule_after: {
-        after: 'u32',
-        maybePeriodic: 'Option<(u32,u32)>',
-        priority: 'u8',
-        call: 'Call',
-      },
-      schedule_named_after: {
-        id: '[u8;32]',
-        after: 'u32',
-        maybePeriodic: 'Option<(u32,u32)>',
-        priority: 'u8',
-        call: 'Call'
-      }
-    }
-  },
-  /**
-   * Lookup223: cumulus_pallet_xcmp_queue::pallet::Call<T>
-   **/
-  CumulusPalletXcmpQueueCall: {
-    _enum: {
-      service_overweight: {
-        index: 'u64',
-        weightLimit: 'SpWeightsWeightV2Weight',
-      },
-      suspend_xcm_execution: 'Null',
-      resume_xcm_execution: 'Null',
-      update_suspend_threshold: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'u32',
-      },
-      update_drop_threshold: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'u32',
-      },
-      update_resume_threshold: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'u32',
-      },
-      update_threshold_weight: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'SpWeightsWeightV2Weight',
-      },
-      update_weight_restrict_decay: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'SpWeightsWeightV2Weight',
-      },
-      update_xcmp_max_individual_weight: {
-        _alias: {
-          new_: 'new',
-        },
-        new_: 'SpWeightsWeightV2Weight'
-      }
-    }
-  },
-  /**
-   * Lookup224: pallet_xcm::pallet::Call<T>
-   **/
-  PalletXcmCall: {
-    _enum: {
-      send: {
-        dest: 'StagingXcmVersionedMultiLocation',
-        message: 'StagingXcmVersionedXcm',
-      },
-      teleport_assets: {
-        dest: 'StagingXcmVersionedMultiLocation',
-        beneficiary: 'StagingXcmVersionedMultiLocation',
-        assets: 'StagingXcmVersionedMultiAssets',
-        feeAssetItem: 'u32',
-      },
-      reserve_transfer_assets: {
-        dest: 'StagingXcmVersionedMultiLocation',
-        beneficiary: 'StagingXcmVersionedMultiLocation',
-        assets: 'StagingXcmVersionedMultiAssets',
-        feeAssetItem: 'u32',
-      },
-      execute: {
-        message: 'StagingXcmVersionedXcm',
-        maxWeight: 'SpWeightsWeightV2Weight',
-      },
-      force_xcm_version: {
-        location: 'StagingXcmV3MultiLocation',
-        version: 'u32',
-      },
-      force_default_xcm_version: {
-        maybeXcmVersion: 'Option<u32>',
-      },
-      force_subscribe_version_notify: {
-        location: 'StagingXcmVersionedMultiLocation',
-      },
-      force_unsubscribe_version_notify: {
-        location: 'StagingXcmVersionedMultiLocation',
-      },
-      limited_reserve_transfer_assets: {
-        dest: 'StagingXcmVersionedMultiLocation',
-        beneficiary: 'StagingXcmVersionedMultiLocation',
-        assets: 'StagingXcmVersionedMultiAssets',
-        feeAssetItem: 'u32',
-        weightLimit: 'StagingXcmV3WeightLimit',
-      },
-      limited_teleport_assets: {
-        dest: 'StagingXcmVersionedMultiLocation',
-        beneficiary: 'StagingXcmVersionedMultiLocation',
-        assets: 'StagingXcmVersionedMultiAssets',
-        feeAssetItem: 'u32',
-        weightLimit: 'StagingXcmV3WeightLimit',
-      },
-      force_suspension: {
-        suspended: 'bool'
-      }
-    }
-  },
-  /**
-   * Lookup225: staging_xcm::VersionedXcm<RuntimeCall>
-   **/
-  StagingXcmVersionedXcm: {
-    _enum: {
-      __Unused0: 'Null',
-      __Unused1: 'Null',
-      V2: 'StagingXcmV2Xcm',
-      V3: 'StagingXcmV3Xcm'
-    }
-  },
-  /**
-   * Lookup226: staging_xcm::v2::Xcm<RuntimeCall>
-   **/
-  StagingXcmV2Xcm: 'Vec<StagingXcmV2Instruction>',
-  /**
-   * Lookup228: staging_xcm::v2::Instruction<RuntimeCall>
-   **/
-  StagingXcmV2Instruction: {
-    _enum: {
-      WithdrawAsset: 'StagingXcmV2MultiassetMultiAssets',
-      ReserveAssetDeposited: 'StagingXcmV2MultiassetMultiAssets',
-      ReceiveTeleportedAsset: 'StagingXcmV2MultiassetMultiAssets',
-      QueryResponse: {
-        queryId: 'Compact<u64>',
-        response: 'StagingXcmV2Response',
-        maxWeight: 'Compact<u64>',
-      },
-      TransferAsset: {
-        assets: 'StagingXcmV2MultiassetMultiAssets',
-        beneficiary: 'StagingXcmV2MultiLocation',
-      },
-      TransferReserveAsset: {
-        assets: 'StagingXcmV2MultiassetMultiAssets',
-        dest: 'StagingXcmV2MultiLocation',
-        xcm: 'StagingXcmV2Xcm',
-      },
-      Transact: {
-        originType: 'StagingXcmV2OriginKind',
-        requireWeightAtMost: 'Compact<u64>',
-        call: 'StagingXcmDoubleEncoded',
-      },
-      HrmpNewChannelOpenRequest: {
-        sender: 'Compact<u32>',
-        maxMessageSize: 'Compact<u32>',
-        maxCapacity: 'Compact<u32>',
-      },
-      HrmpChannelAccepted: {
-        recipient: 'Compact<u32>',
-      },
-      HrmpChannelClosing: {
-        initiator: 'Compact<u32>',
-        sender: 'Compact<u32>',
-        recipient: 'Compact<u32>',
-      },
-      ClearOrigin: 'Null',
-      DescendOrigin: 'StagingXcmV2MultilocationJunctions',
-      ReportError: {
-        queryId: 'Compact<u64>',
-        dest: 'StagingXcmV2MultiLocation',
-        maxResponseWeight: 'Compact<u64>',
-      },
-      DepositAsset: {
-        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
-        maxAssets: 'Compact<u32>',
-        beneficiary: 'StagingXcmV2MultiLocation',
-      },
-      DepositReserveAsset: {
-        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
-        maxAssets: 'Compact<u32>',
-        dest: 'StagingXcmV2MultiLocation',
-        xcm: 'StagingXcmV2Xcm',
-      },
-      ExchangeAsset: {
-        give: 'StagingXcmV2MultiassetMultiAssetFilter',
-        receive: 'StagingXcmV2MultiassetMultiAssets',
-      },
-      InitiateReserveWithdraw: {
-        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
-        reserve: 'StagingXcmV2MultiLocation',
-        xcm: 'StagingXcmV2Xcm',
-      },
-      InitiateTeleport: {
-        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
-        dest: 'StagingXcmV2MultiLocation',
-        xcm: 'StagingXcmV2Xcm',
-      },
-      QueryHolding: {
-        queryId: 'Compact<u64>',
-        dest: 'StagingXcmV2MultiLocation',
-        assets: 'StagingXcmV2MultiassetMultiAssetFilter',
-        maxResponseWeight: 'Compact<u64>',
-      },
-      BuyExecution: {
-        fees: 'StagingXcmV2MultiAsset',
-        weightLimit: 'StagingXcmV2WeightLimit',
-      },
-      RefundSurplus: 'Null',
-      SetErrorHandler: 'StagingXcmV2Xcm',
-      SetAppendix: 'StagingXcmV2Xcm',
-      ClearError: 'Null',
-      ClaimAsset: {
-        assets: 'StagingXcmV2MultiassetMultiAssets',
-        ticket: 'StagingXcmV2MultiLocation',
-      },
-      Trap: 'Compact<u64>',
-      SubscribeVersion: {
-        queryId: 'Compact<u64>',
-        maxResponseWeight: 'Compact<u64>',
-      },
-      UnsubscribeVersion: 'Null'
-    }
-  },
-  /**
-   * Lookup229: staging_xcm::v2::Response
-   **/
-  StagingXcmV2Response: {
-    _enum: {
-      Null: 'Null',
-      Assets: 'StagingXcmV2MultiassetMultiAssets',
-      ExecutionResult: 'Option<(u32,StagingXcmV2TraitsError)>',
-      Version: 'u32'
-    }
-  },
-  /**
-   * Lookup232: staging_xcm::v2::traits::Error
-   **/
-  StagingXcmV2TraitsError: {
-    _enum: {
-      Overflow: 'Null',
-      Unimplemented: 'Null',
-      UntrustedReserveLocation: 'Null',
-      UntrustedTeleportLocation: 'Null',
-      MultiLocationFull: 'Null',
-      MultiLocationNotInvertible: 'Null',
-      BadOrigin: 'Null',
-      InvalidLocation: 'Null',
-      AssetNotFound: 'Null',
-      FailedToTransactAsset: 'Null',
-      NotWithdrawable: 'Null',
-      LocationCannotHold: 'Null',
-      ExceedsMaxMessageSize: 'Null',
-      DestinationUnsupported: 'Null',
-      Transport: 'Null',
-      Unroutable: 'Null',
-      UnknownClaim: 'Null',
-      FailedToDecode: 'Null',
-      MaxWeightInvalid: 'Null',
-      NotHoldingFees: 'Null',
-      TooExpensive: 'Null',
-      Trap: 'u64',
-      UnhandledXcmVersion: 'Null',
-      WeightLimitReached: 'u64',
-      Barrier: 'Null',
-      WeightNotComputable: 'Null'
-    }
-  },
-  /**
-   * Lookup233: staging_xcm::v2::OriginKind
-   **/
-  StagingXcmV2OriginKind: {
-    _enum: ['Native', 'SovereignAccount', 'Superuser', 'Xcm']
-  },
-  /**
-   * Lookup234: staging_xcm::double_encoded::DoubleEncoded<T>
-   **/
-  StagingXcmDoubleEncoded: {
-    encoded: 'Bytes'
-  },
-  /**
-   * Lookup235: staging_xcm::v2::multiasset::MultiAssetFilter
-   **/
-  StagingXcmV2MultiassetMultiAssetFilter: {
-    _enum: {
-      Definite: 'StagingXcmV2MultiassetMultiAssets',
-      Wild: 'StagingXcmV2MultiassetWildMultiAsset'
-    }
-  },
-  /**
-   * Lookup236: staging_xcm::v2::multiasset::WildMultiAsset
-   **/
-  StagingXcmV2MultiassetWildMultiAsset: {
-    _enum: {
-      All: 'Null',
-      AllOf: {
-        id: 'StagingXcmV2MultiassetAssetId',
-        fun: 'StagingXcmV2MultiassetWildFungibility'
-      }
-    }
-  },
-  /**
-   * Lookup237: staging_xcm::v2::multiasset::WildFungibility
-   **/
-  StagingXcmV2MultiassetWildFungibility: {
-    _enum: ['Fungible', 'NonFungible']
-  },
-  /**
-   * Lookup238: staging_xcm::v2::WeightLimit
-   **/
-  StagingXcmV2WeightLimit: {
-    _enum: {
-      Unlimited: 'Null',
-      Limited: 'Compact<u64>'
-    }
-  },
-  /**
-   * Lookup239: staging_xcm::v3::Xcm<Call>
-   **/
-  StagingXcmV3Xcm: 'Vec<StagingXcmV3Instruction>',
-  /**
-   * Lookup241: staging_xcm::v3::Instruction<Call>
-   **/
-  StagingXcmV3Instruction: {
-    _enum: {
-      WithdrawAsset: 'StagingXcmV3MultiassetMultiAssets',
-      ReserveAssetDeposited: 'StagingXcmV3MultiassetMultiAssets',
-      ReceiveTeleportedAsset: 'StagingXcmV3MultiassetMultiAssets',
-      QueryResponse: {
-        queryId: 'Compact<u64>',
-        response: 'StagingXcmV3Response',
-        maxWeight: 'SpWeightsWeightV2Weight',
-        querier: 'Option<StagingXcmV3MultiLocation>',
-      },
-      TransferAsset: {
-        assets: 'StagingXcmV3MultiassetMultiAssets',
-        beneficiary: 'StagingXcmV3MultiLocation',
-      },
-      TransferReserveAsset: {
-        assets: 'StagingXcmV3MultiassetMultiAssets',
-        dest: 'StagingXcmV3MultiLocation',
-        xcm: 'StagingXcmV3Xcm',
-      },
-      Transact: {
-        originKind: 'StagingXcmV2OriginKind',
-        requireWeightAtMost: 'SpWeightsWeightV2Weight',
-        call: 'StagingXcmDoubleEncoded',
-      },
-      HrmpNewChannelOpenRequest: {
-        sender: 'Compact<u32>',
-        maxMessageSize: 'Compact<u32>',
-        maxCapacity: 'Compact<u32>',
-      },
-      HrmpChannelAccepted: {
-        recipient: 'Compact<u32>',
-      },
-      HrmpChannelClosing: {
-        initiator: 'Compact<u32>',
-        sender: 'Compact<u32>',
-        recipient: 'Compact<u32>',
-      },
-      ClearOrigin: 'Null',
-      DescendOrigin: 'StagingXcmV3Junctions',
-      ReportError: 'StagingXcmV3QueryResponseInfo',
-      DepositAsset: {
-        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
-        beneficiary: 'StagingXcmV3MultiLocation',
-      },
-      DepositReserveAsset: {
-        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
-        dest: 'StagingXcmV3MultiLocation',
-        xcm: 'StagingXcmV3Xcm',
-      },
-      ExchangeAsset: {
-        give: 'StagingXcmV3MultiassetMultiAssetFilter',
-        want: 'StagingXcmV3MultiassetMultiAssets',
-        maximal: 'bool',
-      },
-      InitiateReserveWithdraw: {
-        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
-        reserve: 'StagingXcmV3MultiLocation',
-        xcm: 'StagingXcmV3Xcm',
-      },
-      InitiateTeleport: {
-        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
-        dest: 'StagingXcmV3MultiLocation',
-        xcm: 'StagingXcmV3Xcm',
-      },
-      ReportHolding: {
-        responseInfo: 'StagingXcmV3QueryResponseInfo',
-        assets: 'StagingXcmV3MultiassetMultiAssetFilter',
-      },
-      BuyExecution: {
-        fees: 'StagingXcmV3MultiAsset',
-        weightLimit: 'StagingXcmV3WeightLimit',
-      },
-      RefundSurplus: 'Null',
-      SetErrorHandler: 'StagingXcmV3Xcm',
-      SetAppendix: 'StagingXcmV3Xcm',
-      ClearError: 'Null',
-      ClaimAsset: {
-        assets: 'StagingXcmV3MultiassetMultiAssets',
-        ticket: 'StagingXcmV3MultiLocation',
-      },
-      Trap: 'Compact<u64>',
-      SubscribeVersion: {
-        queryId: 'Compact<u64>',
-        maxResponseWeight: 'SpWeightsWeightV2Weight',
-      },
-      UnsubscribeVersion: 'Null',
-      BurnAsset: 'StagingXcmV3MultiassetMultiAssets',
-      ExpectAsset: 'StagingXcmV3MultiassetMultiAssets',
-      ExpectOrigin: 'Option<StagingXcmV3MultiLocation>',
-      ExpectError: 'Option<(u32,StagingXcmV3TraitsError)>',
-      ExpectTransactStatus: 'StagingXcmV3MaybeErrorCode',
-      QueryPallet: {
-        moduleName: 'Bytes',
-        responseInfo: 'StagingXcmV3QueryResponseInfo',
-      },
-      ExpectPallet: {
-        index: 'Compact<u32>',
-        name: 'Bytes',
-        moduleName: 'Bytes',
-        crateMajor: 'Compact<u32>',
-        minCrateMinor: 'Compact<u32>',
-      },
-      ReportTransactStatus: 'StagingXcmV3QueryResponseInfo',
-      ClearTransactStatus: 'Null',
-      UniversalOrigin: 'StagingXcmV3Junction',
-      ExportMessage: {
-        network: 'StagingXcmV3JunctionNetworkId',
-        destination: 'StagingXcmV3Junctions',
-        xcm: 'StagingXcmV3Xcm',
-      },
-      LockAsset: {
-        asset: 'StagingXcmV3MultiAsset',
-        unlocker: 'StagingXcmV3MultiLocation',
-      },
-      UnlockAsset: {
-        asset: 'StagingXcmV3MultiAsset',
-        target: 'StagingXcmV3MultiLocation',
-      },
-      NoteUnlockable: {
-        asset: 'StagingXcmV3MultiAsset',
-        owner: 'StagingXcmV3MultiLocation',
-      },
-      RequestUnlock: {
-        asset: 'StagingXcmV3MultiAsset',
-        locker: 'StagingXcmV3MultiLocation',
-      },
-      SetFeesMode: {
-        jitWithdraw: 'bool',
-      },
-      SetTopic: '[u8;32]',
-      ClearTopic: 'Null',
-      AliasOrigin: 'StagingXcmV3MultiLocation',
-      UnpaidExecution: {
-        weightLimit: 'StagingXcmV3WeightLimit',
-        checkOrigin: 'Option<StagingXcmV3MultiLocation>'
-      }
-    }
-  },
-  /**
-   * Lookup242: staging_xcm::v3::Response
-   **/
-  StagingXcmV3Response: {
-    _enum: {
-      Null: 'Null',
-      Assets: 'StagingXcmV3MultiassetMultiAssets',
-      ExecutionResult: 'Option<(u32,StagingXcmV3TraitsError)>',
-      Version: 'u32',
-      PalletsInfo: 'Vec<StagingXcmV3PalletInfo>',
-      DispatchResult: 'StagingXcmV3MaybeErrorCode'
-    }
-  },
-  /**
-   * Lookup245: staging_xcm::v3::traits::Error
-   **/
-  StagingXcmV3TraitsError: {
-    _enum: {
-      Overflow: 'Null',
-      Unimplemented: 'Null',
-      UntrustedReserveLocation: 'Null',
-      UntrustedTeleportLocation: 'Null',
-      LocationFull: 'Null',
-      LocationNotInvertible: 'Null',
-      BadOrigin: 'Null',
-      InvalidLocation: 'Null',
-      AssetNotFound: 'Null',
-      FailedToTransactAsset: 'Null',
-      NotWithdrawable: 'Null',
-      LocationCannotHold: 'Null',
-      ExceedsMaxMessageSize: 'Null',
-      DestinationUnsupported: 'Null',
-      Transport: 'Null',
-      Unroutable: 'Null',
-      UnknownClaim: 'Null',
-      FailedToDecode: 'Null',
-      MaxWeightInvalid: 'Null',
-      NotHoldingFees: 'Null',
-      TooExpensive: 'Null',
-      Trap: 'u64',
-      ExpectationFalse: 'Null',
-      PalletNotFound: 'Null',
-      NameMismatch: 'Null',
-      VersionIncompatible: 'Null',
-      HoldingWouldOverflow: 'Null',
-      ExportError: 'Null',
-      ReanchorFailed: 'Null',
-      NoDeal: 'Null',
-      FeesNotMet: 'Null',
-      LockError: 'Null',
-      NoPermission: 'Null',
-      Unanchored: 'Null',
-      NotDepositable: 'Null',
-      UnhandledXcmVersion: 'Null',
-      WeightLimitReached: 'SpWeightsWeightV2Weight',
-      Barrier: 'Null',
-      WeightNotComputable: 'Null',
-      ExceedsStackLimit: 'Null'
-    }
-  },
-  /**
-   * Lookup247: staging_xcm::v3::PalletInfo
-   **/
-  StagingXcmV3PalletInfo: {
-    index: 'Compact<u32>',
-    name: 'Bytes',
-    moduleName: 'Bytes',
-    major: 'Compact<u32>',
-    minor: 'Compact<u32>',
-    patch: 'Compact<u32>'
-  },
-  /**
-   * Lookup250: staging_xcm::v3::MaybeErrorCode
-   **/
-  StagingXcmV3MaybeErrorCode: {
-    _enum: {
-      Success: 'Null',
-      Error: 'Bytes',
-      TruncatedError: 'Bytes'
-    }
-  },
-  /**
-   * Lookup253: staging_xcm::v3::QueryResponseInfo
-   **/
-  StagingXcmV3QueryResponseInfo: {
-    destination: 'StagingXcmV3MultiLocation',
-    queryId: 'Compact<u64>',
-    maxWeight: 'SpWeightsWeightV2Weight'
-  },
-  /**
-   * Lookup254: staging_xcm::v3::multiasset::MultiAssetFilter
-   **/
-  StagingXcmV3MultiassetMultiAssetFilter: {
-    _enum: {
-      Definite: 'StagingXcmV3MultiassetMultiAssets',
-      Wild: 'StagingXcmV3MultiassetWildMultiAsset'
-    }
-  },
-  /**
-   * Lookup255: staging_xcm::v3::multiasset::WildMultiAsset
-   **/
-  StagingXcmV3MultiassetWildMultiAsset: {
-    _enum: {
-      All: 'Null',
-      AllOf: {
-        id: 'StagingXcmV3MultiassetAssetId',
-        fun: 'StagingXcmV3MultiassetWildFungibility',
-      },
-      AllCounted: 'Compact<u32>',
-      AllOfCounted: {
-        id: 'StagingXcmV3MultiassetAssetId',
-        fun: 'StagingXcmV3MultiassetWildFungibility',
-        count: 'Compact<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup256: staging_xcm::v3::multiasset::WildFungibility
-   **/
-  StagingXcmV3MultiassetWildFungibility: {
-    _enum: ['Fungible', 'NonFungible']
-  },
-  /**
-   * Lookup265: cumulus_pallet_xcm::pallet::Call<T>
-   **/
-  CumulusPalletXcmCall: 'Null',
-  /**
-   * Lookup266: cumulus_pallet_dmp_queue::pallet::Call<T>
-   **/
-  CumulusPalletDmpQueueCall: {
-    _enum: {
-      service_overweight: {
-        index: 'u64',
-        weightLimit: 'SpWeightsWeightV2Weight'
-      }
-    }
-  },
-  /**
-   * Lookup267: pallet_inflation::pallet::Call<T>
-   **/
-  PalletInflationCall: {
-    _enum: {
-      start_inflation: {
-        inflationStartRelayBlock: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup268: pallet_unique::pallet::Call<T>
-   **/
-  PalletUniqueCall: {
-    _enum: {
-      create_collection: {
-        collectionName: 'Vec<u16>',
-        collectionDescription: 'Vec<u16>',
-        tokenPrefix: 'Bytes',
-        mode: 'UpDataStructsCollectionMode',
-      },
-      create_collection_ex: {
-        data: 'UpDataStructsCreateCollectionData',
-      },
-      destroy_collection: {
-        collectionId: 'u32',
-      },
-      add_to_allow_list: {
-        collectionId: 'u32',
-        address: 'PalletEvmAccountBasicCrossAccountIdRepr',
-      },
-      remove_from_allow_list: {
-        collectionId: 'u32',
-        address: 'PalletEvmAccountBasicCrossAccountIdRepr',
-      },
-      change_collection_owner: {
-        collectionId: 'u32',
-        newOwner: 'AccountId32',
-      },
-      add_collection_admin: {
-        collectionId: 'u32',
-        newAdminId: 'PalletEvmAccountBasicCrossAccountIdRepr',
-      },
-      remove_collection_admin: {
-        collectionId: 'u32',
-        accountId: 'PalletEvmAccountBasicCrossAccountIdRepr',
-      },
-      set_collection_sponsor: {
-        collectionId: 'u32',
-        newSponsor: 'AccountId32',
-      },
-      confirm_sponsorship: {
-        collectionId: 'u32',
-      },
-      remove_collection_sponsor: {
-        collectionId: 'u32',
-      },
-      create_item: {
-        collectionId: 'u32',
-        owner: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        data: 'UpDataStructsCreateItemData',
-      },
-      create_multiple_items: {
-        collectionId: 'u32',
-        owner: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        itemsData: 'Vec<UpDataStructsCreateItemData>',
-      },
-      set_collection_properties: {
-        collectionId: 'u32',
-        properties: 'Vec<UpDataStructsProperty>',
-      },
-      delete_collection_properties: {
-        collectionId: 'u32',
-        propertyKeys: 'Vec<Bytes>',
-      },
-      set_token_properties: {
-        collectionId: 'u32',
-        tokenId: 'u32',
-        properties: 'Vec<UpDataStructsProperty>',
-      },
-      delete_token_properties: {
-        collectionId: 'u32',
-        tokenId: 'u32',
-        propertyKeys: 'Vec<Bytes>',
-      },
-      set_token_property_permissions: {
-        collectionId: 'u32',
-        propertyPermissions: 'Vec<UpDataStructsPropertyKeyPermission>',
-      },
-      create_multiple_items_ex: {
-        collectionId: 'u32',
-        data: 'UpDataStructsCreateItemExData',
-      },
-      set_transfers_enabled_flag: {
-        collectionId: 'u32',
-        value: 'bool',
-      },
-      burn_item: {
-        collectionId: 'u32',
-        itemId: 'u32',
-        value: 'u128',
-      },
-      burn_from: {
-        collectionId: 'u32',
-        from: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        itemId: 'u32',
-        value: 'u128',
-      },
-      transfer: {
-        recipient: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        collectionId: 'u32',
-        itemId: 'u32',
-        value: 'u128',
-      },
-      approve: {
-        spender: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        collectionId: 'u32',
-        itemId: 'u32',
-        amount: 'u128',
-      },
-      approve_from: {
-        from: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        to: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        collectionId: 'u32',
-        itemId: 'u32',
-        amount: 'u128',
-      },
-      transfer_from: {
-        from: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        recipient: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        collectionId: 'u32',
-        itemId: 'u32',
-        value: 'u128',
-      },
-      set_collection_limits: {
-        collectionId: 'u32',
-        newLimit: 'UpDataStructsCollectionLimits',
-      },
-      set_collection_permissions: {
-        collectionId: 'u32',
-        newPermission: 'UpDataStructsCollectionPermissions',
-      },
-      repartition: {
-        collectionId: 'u32',
-        tokenId: 'u32',
-        amount: 'u128',
-      },
-      set_allowance_for_all: {
-        collectionId: 'u32',
-        operator: 'PalletEvmAccountBasicCrossAccountIdRepr',
-        approve: 'bool',
-      },
-      force_repair_collection: {
-        collectionId: 'u32',
-      },
-      force_repair_item: {
-        collectionId: 'u32',
-        itemId: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup273: up_data_structs::CollectionMode
-   **/
-  UpDataStructsCollectionMode: {
-    _enum: {
-      NFT: 'Null',
-      Fungible: 'u8',
-      ReFungible: 'Null'
-    }
-  },
-  /**
-   * Lookup274: up_data_structs::CreateCollectionData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  UpDataStructsCreateCollectionData: {
-    mode: 'UpDataStructsCollectionMode',
-    access: 'Option<UpDataStructsAccessMode>',
-    name: 'Vec<u16>',
-    description: 'Vec<u16>',
-    tokenPrefix: 'Bytes',
-    limits: 'Option<UpDataStructsCollectionLimits>',
-    permissions: 'Option<UpDataStructsCollectionPermissions>',
-    tokenPropertyPermissions: 'Vec<UpDataStructsPropertyKeyPermission>',
-    properties: 'Vec<UpDataStructsProperty>',
-    adminList: 'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
-    pendingSponsor: 'Option<PalletEvmAccountBasicCrossAccountIdRepr>',
-    flags: '[u8;1]'
-  },
-  /**
-   * Lookup275: pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>
-   **/
-  PalletEvmAccountBasicCrossAccountIdRepr: {
-    _enum: {
-      Substrate: 'AccountId32',
-      Ethereum: 'H160'
-    }
-  },
-  /**
-   * Lookup277: up_data_structs::AccessMode
-   **/
-  UpDataStructsAccessMode: {
-    _enum: ['Normal', 'AllowList']
-  },
-  /**
-   * Lookup279: up_data_structs::CollectionLimits
-   **/
-  UpDataStructsCollectionLimits: {
-    accountTokenOwnershipLimit: 'Option<u32>',
-    sponsoredDataSize: 'Option<u32>',
-    sponsoredDataRateLimit: 'Option<UpDataStructsSponsoringRateLimit>',
-    tokenLimit: 'Option<u32>',
-    sponsorTransferTimeout: 'Option<u32>',
-    sponsorApproveTimeout: 'Option<u32>',
-    ownerCanTransfer: 'Option<bool>',
-    ownerCanDestroy: 'Option<bool>',
-    transfersEnabled: 'Option<bool>'
-  },
-  /**
-   * Lookup281: up_data_structs::SponsoringRateLimit
-   **/
-  UpDataStructsSponsoringRateLimit: {
-    _enum: {
-      SponsoringDisabled: 'Null',
-      Blocks: 'u32'
-    }
-  },
-  /**
-   * Lookup284: up_data_structs::CollectionPermissions
-   **/
-  UpDataStructsCollectionPermissions: {
-    access: 'Option<UpDataStructsAccessMode>',
-    mintMode: 'Option<bool>',
-    nesting: 'Option<UpDataStructsNestingPermissions>'
-  },
-  /**
-   * Lookup286: up_data_structs::NestingPermissions
-   **/
-  UpDataStructsNestingPermissions: {
-    tokenOwner: 'bool',
-    collectionAdmin: 'bool',
-    restricted: 'Option<UpDataStructsOwnerRestrictedSet>'
-  },
-  /**
-   * Lookup288: up_data_structs::OwnerRestrictedSet
-   **/
-  UpDataStructsOwnerRestrictedSet: 'BTreeSet<u32>',
-  /**
-   * Lookup294: up_data_structs::PropertyKeyPermission
-   **/
-  UpDataStructsPropertyKeyPermission: {
-    key: 'Bytes',
-    permission: 'UpDataStructsPropertyPermission'
-  },
-  /**
-   * Lookup296: up_data_structs::PropertyPermission
-   **/
-  UpDataStructsPropertyPermission: {
-    mutable: 'bool',
-    collectionAdmin: 'bool',
-    tokenOwner: 'bool'
-  },
-  /**
-   * Lookup299: up_data_structs::Property
-   **/
-  UpDataStructsProperty: {
-    key: 'Bytes',
-    value: 'Bytes'
-  },
-  /**
-   * Lookup304: up_data_structs::CreateItemData
-   **/
-  UpDataStructsCreateItemData: {
-    _enum: {
-      NFT: 'UpDataStructsCreateNftData',
-      Fungible: 'UpDataStructsCreateFungibleData',
-      ReFungible: 'UpDataStructsCreateReFungibleData'
-    }
-  },
-  /**
-   * Lookup305: up_data_structs::CreateNftData
-   **/
-  UpDataStructsCreateNftData: {
-    properties: 'Vec<UpDataStructsProperty>'
-  },
-  /**
-   * Lookup306: up_data_structs::CreateFungibleData
-   **/
-  UpDataStructsCreateFungibleData: {
-    value: 'u128'
-  },
-  /**
-   * Lookup307: up_data_structs::CreateReFungibleData
-   **/
-  UpDataStructsCreateReFungibleData: {
-    pieces: 'u128',
-    properties: 'Vec<UpDataStructsProperty>'
-  },
-  /**
-   * Lookup311: up_data_structs::CreateItemExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  UpDataStructsCreateItemExData: {
-    _enum: {
-      NFT: 'Vec<UpDataStructsCreateNftExData>',
-      Fungible: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>',
-      RefungibleMultipleItems: 'Vec<UpDataStructsCreateRefungibleExSingleOwner>',
-      RefungibleMultipleOwners: 'UpDataStructsCreateRefungibleExMultipleOwners'
-    }
-  },
-  /**
-   * Lookup313: up_data_structs::CreateNftExData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  UpDataStructsCreateNftExData: {
-    properties: 'Vec<UpDataStructsProperty>',
-    owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
-  },
-  /**
-   * Lookup320: up_data_structs::CreateRefungibleExSingleOwner<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  UpDataStructsCreateRefungibleExSingleOwner: {
-    user: 'PalletEvmAccountBasicCrossAccountIdRepr',
-    pieces: 'u128',
-    properties: 'Vec<UpDataStructsProperty>'
-  },
-  /**
-   * Lookup322: up_data_structs::CreateRefungibleExMultipleOwners<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  UpDataStructsCreateRefungibleExMultipleOwners: {
-    users: 'BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>',
-    properties: 'Vec<UpDataStructsProperty>'
-  },
-  /**
-   * Lookup323: pallet_configuration::pallet::Call<T>
-   **/
-  PalletConfigurationCall: {
-    _enum: {
-      set_weight_to_fee_coefficient_override: {
-        coeff: 'Option<u64>',
-      },
-      set_min_gas_price_override: {
-        coeff: 'Option<u64>',
-      },
-      __Unused2: 'Null',
-      set_app_promotion_configuration_override: {
-        configuration: 'PalletConfigurationAppPromotionConfiguration',
-      },
-      set_collator_selection_desired_collators: {
-        max: 'Option<u32>',
-      },
-      set_collator_selection_license_bond: {
-        amount: 'Option<u128>',
-      },
-      set_collator_selection_kick_threshold: {
-        threshold: 'Option<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup325: pallet_configuration::AppPromotionConfiguration<BlockNumber>
-   **/
-  PalletConfigurationAppPromotionConfiguration: {
-    recalculationInterval: 'Option<u32>',
-    pendingInterval: 'Option<u32>',
-    intervalIncome: 'Option<Perbill>',
-    maxStakersPerCalculation: 'Option<u8>'
-  },
-  /**
-   * Lookup330: pallet_structure::pallet::Call<T>
-   **/
-  PalletStructureCall: 'Null',
-  /**
-   * Lookup331: pallet_app_promotion::pallet::Call<T>
-   **/
-  PalletAppPromotionCall: {
-    _enum: {
-      set_admin_address: {
-        admin: 'PalletEvmAccountBasicCrossAccountIdRepr',
-      },
-      stake: {
-        amount: 'u128',
-      },
-      unstake_all: 'Null',
-      sponsor_collection: {
-        collectionId: 'u32',
-      },
-      stop_sponsoring_collection: {
-        collectionId: 'u32',
-      },
-      sponsor_contract: {
-        contractId: 'H160',
-      },
-      stop_sponsoring_contract: {
-        contractId: 'H160',
-      },
-      payout_stakers: {
-        stakersNumber: 'Option<u8>',
-      },
-      unstake_partial: {
-        amount: 'u128',
-      },
-      force_unstake: {
-        pendingBlocks: 'Vec<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup333: pallet_foreign_assets::module::Call<T>
-   **/
-  PalletForeignAssetsModuleCall: {
-    _enum: {
-      register_foreign_asset: {
-        owner: 'AccountId32',
-        location: 'StagingXcmVersionedMultiLocation',
-        metadata: 'PalletForeignAssetsModuleAssetMetadata',
-      },
-      update_foreign_asset: {
-        foreignAssetId: 'u32',
-        location: 'StagingXcmVersionedMultiLocation',
-        metadata: 'PalletForeignAssetsModuleAssetMetadata'
-      }
-    }
-  },
-  /**
-   * Lookup334: pallet_foreign_assets::module::AssetMetadata<Balance>
-   **/
-  PalletForeignAssetsModuleAssetMetadata: {
-    name: 'Bytes',
-    symbol: 'Bytes',
-    decimals: 'u8',
-    minimalBalance: 'u128'
-  },
-  /**
-   * Lookup337: pallet_evm::pallet::Call<T>
-   **/
-  PalletEvmCall: {
-    _enum: {
-      withdraw: {
-        address: 'H160',
-        value: 'u128',
-      },
-      call: {
-        source: 'H160',
-        target: 'H160',
-        input: 'Bytes',
-        value: 'U256',
-        gasLimit: 'u64',
-        maxFeePerGas: 'U256',
-        maxPriorityFeePerGas: 'Option<U256>',
-        nonce: 'Option<U256>',
-        accessList: 'Vec<(H160,Vec<H256>)>',
-      },
-      create: {
-        source: 'H160',
-        init: 'Bytes',
-        value: 'U256',
-        gasLimit: 'u64',
-        maxFeePerGas: 'U256',
-        maxPriorityFeePerGas: 'Option<U256>',
-        nonce: 'Option<U256>',
-        accessList: 'Vec<(H160,Vec<H256>)>',
-      },
-      create2: {
-        source: 'H160',
-        init: 'Bytes',
-        salt: 'H256',
-        value: 'U256',
-        gasLimit: 'u64',
-        maxFeePerGas: 'U256',
-        maxPriorityFeePerGas: 'Option<U256>',
-        nonce: 'Option<U256>',
-        accessList: 'Vec<(H160,Vec<H256>)>'
-      }
-    }
-  },
-  /**
-   * Lookup344: pallet_ethereum::pallet::Call<T>
-   **/
-  PalletEthereumCall: {
-    _enum: {
-      transact: {
-        transaction: 'EthereumTransactionTransactionV2'
-      }
-    }
-  },
-  /**
-   * Lookup345: ethereum::transaction::TransactionV2
-   **/
-  EthereumTransactionTransactionV2: {
-    _enum: {
-      Legacy: 'EthereumTransactionLegacyTransaction',
-      EIP2930: 'EthereumTransactionEip2930Transaction',
-      EIP1559: 'EthereumTransactionEip1559Transaction'
-    }
-  },
-  /**
-   * Lookup346: ethereum::transaction::LegacyTransaction
-   **/
-  EthereumTransactionLegacyTransaction: {
-    nonce: 'U256',
-    gasPrice: 'U256',
-    gasLimit: 'U256',
-    action: 'EthereumTransactionTransactionAction',
-    value: 'U256',
-    input: 'Bytes',
-    signature: 'EthereumTransactionTransactionSignature'
-  },
-  /**
-   * Lookup347: ethereum::transaction::TransactionAction
-   **/
-  EthereumTransactionTransactionAction: {
-    _enum: {
-      Call: 'H160',
-      Create: 'Null'
-    }
-  },
-  /**
-   * Lookup348: ethereum::transaction::TransactionSignature
-   **/
-  EthereumTransactionTransactionSignature: {
-    v: 'u64',
-    r: 'H256',
-    s: 'H256'
-  },
-  /**
-   * Lookup350: ethereum::transaction::EIP2930Transaction
-   **/
-  EthereumTransactionEip2930Transaction: {
-    chainId: 'u64',
-    nonce: 'U256',
-    gasPrice: 'U256',
-    gasLimit: 'U256',
-    action: 'EthereumTransactionTransactionAction',
-    value: 'U256',
-    input: 'Bytes',
-    accessList: 'Vec<EthereumTransactionAccessListItem>',
-    oddYParity: 'bool',
-    r: 'H256',
-    s: 'H256'
-  },
-  /**
-   * Lookup352: ethereum::transaction::AccessListItem
-   **/
-  EthereumTransactionAccessListItem: {
-    address: 'H160',
-    storageKeys: 'Vec<H256>'
-  },
-  /**
-   * Lookup353: ethereum::transaction::EIP1559Transaction
-   **/
-  EthereumTransactionEip1559Transaction: {
-    chainId: 'u64',
-    nonce: 'U256',
-    maxPriorityFeePerGas: 'U256',
-    maxFeePerGas: 'U256',
-    gasLimit: 'U256',
-    action: 'EthereumTransactionTransactionAction',
-    value: 'U256',
-    input: 'Bytes',
-    accessList: 'Vec<EthereumTransactionAccessListItem>',
-    oddYParity: 'bool',
-    r: 'H256',
-    s: 'H256'
-  },
-  /**
-   * Lookup354: pallet_evm_contract_helpers::pallet::Call<T>
-   **/
-  PalletEvmContractHelpersCall: {
-    _enum: {
-      migrate_from_self_sponsoring: {
-        addresses: 'Vec<H160>'
-      }
-    }
-  },
-  /**
-   * Lookup356: pallet_evm_migration::pallet::Call<T>
-   **/
-  PalletEvmMigrationCall: {
-    _enum: {
-      begin: {
-        address: 'H160',
-      },
-      set_data: {
-        address: 'H160',
-        data: 'Vec<(H256,H256)>',
-      },
-      finish: {
-        address: 'H160',
-        code: 'Bytes',
-      },
-      insert_eth_logs: {
-        logs: 'Vec<EthereumLog>',
-      },
-      insert_events: {
-        events: 'Vec<Bytes>',
-      },
-      remove_rmrk_data: 'Null'
-    }
-  },
-  /**
-   * Lookup360: ethereum::log::Log
-   **/
-  EthereumLog: {
-    address: 'H160',
-    topics: 'Vec<H256>',
-    data: 'Bytes'
-  },
-  /**
-   * Lookup361: pallet_maintenance::pallet::Call<T>
-   **/
-  PalletMaintenanceCall: {
-    _enum: ['enable', 'disable']
-  },
-  /**
-   * Lookup362: pallet_utility::pallet::Call<T>
-   **/
-  PalletUtilityCall: {
-    _enum: {
-      batch: {
-        calls: 'Vec<Call>',
-      },
-      as_derivative: {
-        index: 'u16',
-        call: 'Call',
-      },
-      batch_all: {
-        calls: 'Vec<Call>',
-      },
-      dispatch_as: {
-        asOrigin: 'OpalRuntimeOriginCaller',
-        call: 'Call',
-      },
-      force_batch: {
-        calls: 'Vec<Call>',
-      },
-      with_weight: {
-        call: 'Call',
-        weight: 'SpWeightsWeightV2Weight'
-      }
-    }
-  },
-  /**
-   * Lookup364: pallet_test_utils::pallet::Call<T>
-   **/
-  PalletTestUtilsCall: {
-    _enum: {
-      enable: 'Null',
-      set_test_value: {
-        value: 'u32',
-      },
-      set_test_value_and_rollback: {
-        value: 'u32',
-      },
-      inc_test_value: 'Null',
-      just_take_fee: 'Null',
-      batch_all: {
-        calls: 'Vec<Call>'
-      }
-    }
-  },
-  /**
-   * Lookup366: pallet_scheduler::pallet::Event<T>
-   **/
-  PalletSchedulerEvent: {
-    _enum: {
-      Scheduled: {
-        when: 'u32',
-        index: 'u32',
-      },
-      Canceled: {
-        when: 'u32',
-        index: 'u32',
-      },
-      Dispatched: {
-        task: '(u32,u32)',
-        id: 'Option<[u8;32]>',
-        result: 'Result<Null, SpRuntimeDispatchError>',
-      },
-      CallUnavailable: {
-        task: '(u32,u32)',
-        id: 'Option<[u8;32]>',
-      },
-      PeriodicFailed: {
-        task: '(u32,u32)',
-        id: 'Option<[u8;32]>',
-      },
-      PermanentlyOverweight: {
-        task: '(u32,u32)',
-        id: 'Option<[u8;32]>'
-      }
-    }
-  },
-  /**
-   * Lookup367: cumulus_pallet_xcmp_queue::pallet::Event<T>
-   **/
-  CumulusPalletXcmpQueueEvent: {
-    _enum: {
-      Success: {
-        messageHash: '[u8;32]',
-        messageId: '[u8;32]',
-        weight: 'SpWeightsWeightV2Weight',
-      },
-      Fail: {
-        messageHash: '[u8;32]',
-        messageId: '[u8;32]',
-        error: 'StagingXcmV3TraitsError',
-        weight: 'SpWeightsWeightV2Weight',
-      },
-      BadVersion: {
-        messageHash: '[u8;32]',
-      },
-      BadFormat: {
-        messageHash: '[u8;32]',
-      },
-      XcmpMessageSent: {
-        messageHash: '[u8;32]',
-      },
-      OverweightEnqueued: {
-        sender: 'u32',
-        sentAt: 'u32',
-        index: 'u64',
-        required: 'SpWeightsWeightV2Weight',
-      },
-      OverweightServiced: {
-        index: 'u64',
-        used: 'SpWeightsWeightV2Weight'
-      }
-    }
-  },
-  /**
-   * Lookup368: pallet_xcm::pallet::Event<T>
-   **/
-  PalletXcmEvent: {
-    _enum: {
-      Attempted: {
-        outcome: 'StagingXcmV3TraitsOutcome',
-      },
-      Sent: {
-        origin: 'StagingXcmV3MultiLocation',
-        destination: 'StagingXcmV3MultiLocation',
-        message: 'StagingXcmV3Xcm',
-        messageId: '[u8;32]',
-      },
-      UnexpectedResponse: {
-        origin: 'StagingXcmV3MultiLocation',
-        queryId: 'u64',
-      },
-      ResponseReady: {
-        queryId: 'u64',
-        response: 'StagingXcmV3Response',
-      },
-      Notified: {
-        queryId: 'u64',
-        palletIndex: 'u8',
-        callIndex: 'u8',
-      },
-      NotifyOverweight: {
-        queryId: 'u64',
-        palletIndex: 'u8',
-        callIndex: 'u8',
-        actualWeight: 'SpWeightsWeightV2Weight',
-        maxBudgetedWeight: 'SpWeightsWeightV2Weight',
-      },
-      NotifyDispatchError: {
-        queryId: 'u64',
-        palletIndex: 'u8',
-        callIndex: 'u8',
-      },
-      NotifyDecodeFailed: {
-        queryId: 'u64',
-        palletIndex: 'u8',
-        callIndex: 'u8',
-      },
-      InvalidResponder: {
-        origin: 'StagingXcmV3MultiLocation',
-        queryId: 'u64',
-        expectedLocation: 'Option<StagingXcmV3MultiLocation>',
-      },
-      InvalidResponderVersion: {
-        origin: 'StagingXcmV3MultiLocation',
-        queryId: 'u64',
-      },
-      ResponseTaken: {
-        queryId: 'u64',
-      },
-      AssetsTrapped: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-        origin: 'StagingXcmV3MultiLocation',
-        assets: 'StagingXcmVersionedMultiAssets',
-      },
-      VersionChangeNotified: {
-        destination: 'StagingXcmV3MultiLocation',
-        result: 'u32',
-        cost: 'StagingXcmV3MultiassetMultiAssets',
-        messageId: '[u8;32]',
-      },
-      SupportedVersionChanged: {
-        location: 'StagingXcmV3MultiLocation',
-        version: 'u32',
-      },
-      NotifyTargetSendFail: {
-        location: 'StagingXcmV3MultiLocation',
-        queryId: 'u64',
-        error: 'StagingXcmV3TraitsError',
-      },
-      NotifyTargetMigrationFail: {
-        location: 'StagingXcmVersionedMultiLocation',
-        queryId: 'u64',
-      },
-      InvalidQuerierVersion: {
-        origin: 'StagingXcmV3MultiLocation',
-        queryId: 'u64',
-      },
-      InvalidQuerier: {
-        origin: 'StagingXcmV3MultiLocation',
-        queryId: 'u64',
-        expectedQuerier: 'StagingXcmV3MultiLocation',
-        maybeActualQuerier: 'Option<StagingXcmV3MultiLocation>',
-      },
-      VersionNotifyStarted: {
-        destination: 'StagingXcmV3MultiLocation',
-        cost: 'StagingXcmV3MultiassetMultiAssets',
-        messageId: '[u8;32]',
-      },
-      VersionNotifyRequested: {
-        destination: 'StagingXcmV3MultiLocation',
-        cost: 'StagingXcmV3MultiassetMultiAssets',
-        messageId: '[u8;32]',
-      },
-      VersionNotifyUnrequested: {
-        destination: 'StagingXcmV3MultiLocation',
-        cost: 'StagingXcmV3MultiassetMultiAssets',
-        messageId: '[u8;32]',
-      },
-      FeesPaid: {
-        paying: 'StagingXcmV3MultiLocation',
-        fees: 'StagingXcmV3MultiassetMultiAssets',
-      },
-      AssetsClaimed: {
-        _alias: {
-          hash_: 'hash',
-        },
-        hash_: 'H256',
-        origin: 'StagingXcmV3MultiLocation',
-        assets: 'StagingXcmVersionedMultiAssets'
-      }
-    }
-  },
-  /**
-   * Lookup369: staging_xcm::v3::traits::Outcome
-   **/
-  StagingXcmV3TraitsOutcome: {
-    _enum: {
-      Complete: 'SpWeightsWeightV2Weight',
-      Incomplete: '(SpWeightsWeightV2Weight,StagingXcmV3TraitsError)',
-      Error: 'StagingXcmV3TraitsError'
-    }
-  },
-  /**
-   * Lookup370: cumulus_pallet_xcm::pallet::Event<T>
-   **/
-  CumulusPalletXcmEvent: {
-    _enum: {
-      InvalidFormat: '[u8;32]',
-      UnsupportedVersion: '[u8;32]',
-      ExecutedDownward: '([u8;32],StagingXcmV3TraitsOutcome)'
-    }
-  },
-  /**
-   * Lookup371: cumulus_pallet_dmp_queue::pallet::Event<T>
-   **/
-  CumulusPalletDmpQueueEvent: {
-    _enum: {
-      InvalidFormat: {
-        messageHash: '[u8;32]',
-      },
-      UnsupportedVersion: {
-        messageHash: '[u8;32]',
-      },
-      ExecutedDownward: {
-        messageHash: '[u8;32]',
-        messageId: '[u8;32]',
-        outcome: 'StagingXcmV3TraitsOutcome',
-      },
-      WeightExhausted: {
-        messageHash: '[u8;32]',
-        messageId: '[u8;32]',
-        remainingWeight: 'SpWeightsWeightV2Weight',
-        requiredWeight: 'SpWeightsWeightV2Weight',
-      },
-      OverweightEnqueued: {
-        messageHash: '[u8;32]',
-        messageId: '[u8;32]',
-        overweightIndex: 'u64',
-        requiredWeight: 'SpWeightsWeightV2Weight',
-      },
-      OverweightServiced: {
-        overweightIndex: 'u64',
-        weightUsed: 'SpWeightsWeightV2Weight',
-      },
-      MaxMessagesExhausted: {
-        messageHash: '[u8;32]'
-      }
-    }
-  },
-  /**
-   * Lookup372: pallet_configuration::pallet::Event<T>
-   **/
-  PalletConfigurationEvent: {
-    _enum: {
-      NewDesiredCollators: {
-        desiredCollators: 'Option<u32>',
-      },
-      NewCollatorLicenseBond: {
-        bondCost: 'Option<u128>',
-      },
-      NewCollatorKickThreshold: {
-        lengthInBlocks: 'Option<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup373: pallet_common::pallet::Event<T>
-   **/
-  PalletCommonEvent: {
-    _enum: {
-      CollectionCreated: '(u32,u8,AccountId32)',
-      CollectionDestroyed: 'u32',
-      ItemCreated: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
-      ItemDestroyed: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
-      Transfer: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
-      Approved: '(u32,u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,u128)',
-      ApprovedForAll: '(u32,PalletEvmAccountBasicCrossAccountIdRepr,PalletEvmAccountBasicCrossAccountIdRepr,bool)',
-      CollectionPropertySet: '(u32,Bytes)',
-      CollectionPropertyDeleted: '(u32,Bytes)',
-      TokenPropertySet: '(u32,u32,Bytes)',
-      TokenPropertyDeleted: '(u32,u32,Bytes)',
-      PropertyPermissionSet: '(u32,Bytes)',
-      AllowListAddressAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
-      AllowListAddressRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
-      CollectionAdminAdded: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
-      CollectionAdminRemoved: '(u32,PalletEvmAccountBasicCrossAccountIdRepr)',
-      CollectionLimitSet: 'u32',
-      CollectionOwnerChanged: '(u32,AccountId32)',
-      CollectionPermissionSet: 'u32',
-      CollectionSponsorSet: '(u32,AccountId32)',
-      SponsorshipConfirmed: '(u32,AccountId32)',
-      CollectionSponsorRemoved: 'u32'
-    }
-  },
-  /**
-   * Lookup374: pallet_structure::pallet::Event<T>
-   **/
-  PalletStructureEvent: {
-    _enum: {
-      Executed: 'Result<Null, SpRuntimeDispatchError>'
-    }
-  },
-  /**
-   * Lookup375: pallet_app_promotion::pallet::Event<T>
-   **/
-  PalletAppPromotionEvent: {
-    _enum: {
-      StakingRecalculation: '(AccountId32,u128,u128)',
-      Stake: '(AccountId32,u128)',
-      Unstake: '(AccountId32,u128)',
-      SetAdmin: 'AccountId32'
-    }
-  },
-  /**
-   * Lookup376: pallet_foreign_assets::module::Event<T>
-   **/
-  PalletForeignAssetsModuleEvent: {
-    _enum: {
-      ForeignAssetRegistered: {
-        assetId: 'u32',
-        assetAddress: 'StagingXcmV3MultiLocation',
-        metadata: 'PalletForeignAssetsModuleAssetMetadata',
-      },
-      ForeignAssetUpdated: {
-        assetId: 'u32',
-        assetAddress: 'StagingXcmV3MultiLocation',
-        metadata: 'PalletForeignAssetsModuleAssetMetadata',
-      },
-      AssetRegistered: {
-        assetId: 'PalletForeignAssetsAssetId',
-        metadata: 'PalletForeignAssetsModuleAssetMetadata',
-      },
-      AssetUpdated: {
-        assetId: 'PalletForeignAssetsAssetId',
-        metadata: 'PalletForeignAssetsModuleAssetMetadata'
-      }
-    }
-  },
-  /**
-   * Lookup377: pallet_evm::pallet::Event<T>
-   **/
-  PalletEvmEvent: {
-    _enum: {
-      Log: {
-        log: 'EthereumLog',
-      },
-      Created: {
-        address: 'H160',
-      },
-      CreatedFailed: {
-        address: 'H160',
-      },
-      Executed: {
-        address: 'H160',
-      },
-      ExecutedFailed: {
-        address: 'H160'
-      }
-    }
-  },
-  /**
-   * Lookup378: pallet_ethereum::pallet::Event
-   **/
-  PalletEthereumEvent: {
-    _enum: {
-      Executed: {
-        from: 'H160',
-        to: 'H160',
-        transactionHash: 'H256',
-        exitReason: 'EvmCoreErrorExitReason',
-        extraData: 'Bytes'
-      }
-    }
-  },
-  /**
-   * Lookup379: evm_core::error::ExitReason
-   **/
-  EvmCoreErrorExitReason: {
-    _enum: {
-      Succeed: 'EvmCoreErrorExitSucceed',
-      Error: 'EvmCoreErrorExitError',
-      Revert: 'EvmCoreErrorExitRevert',
-      Fatal: 'EvmCoreErrorExitFatal'
-    }
-  },
-  /**
-   * Lookup380: evm_core::error::ExitSucceed
-   **/
-  EvmCoreErrorExitSucceed: {
-    _enum: ['Stopped', 'Returned', 'Suicided']
-  },
-  /**
-   * Lookup381: evm_core::error::ExitError
-   **/
-  EvmCoreErrorExitError: {
-    _enum: {
-      StackUnderflow: 'Null',
-      StackOverflow: 'Null',
-      InvalidJump: 'Null',
-      InvalidRange: 'Null',
-      DesignatedInvalid: 'Null',
-      CallTooDeep: 'Null',
-      CreateCollision: 'Null',
-      CreateContractLimit: 'Null',
-      OutOfOffset: 'Null',
-      OutOfGas: 'Null',
-      OutOfFund: 'Null',
-      PCUnderflow: 'Null',
-      CreateEmpty: 'Null',
-      Other: 'Text',
-      MaxNonce: 'Null',
-      InvalidCode: 'u8'
-    }
-  },
-  /**
-   * Lookup385: evm_core::error::ExitRevert
-   **/
-  EvmCoreErrorExitRevert: {
-    _enum: ['Reverted']
-  },
-  /**
-   * Lookup386: evm_core::error::ExitFatal
-   **/
-  EvmCoreErrorExitFatal: {
-    _enum: {
-      NotSupported: 'Null',
-      UnhandledInterrupt: 'Null',
-      CallErrorAsFatal: 'EvmCoreErrorExitError',
-      Other: 'Text'
-    }
-  },
-  /**
-   * Lookup387: pallet_evm_contract_helpers::pallet::Event<T>
-   **/
-  PalletEvmContractHelpersEvent: {
-    _enum: {
-      ContractSponsorSet: '(H160,AccountId32)',
-      ContractSponsorshipConfirmed: '(H160,AccountId32)',
-      ContractSponsorRemoved: 'H160'
-    }
-  },
-  /**
-   * Lookup388: pallet_evm_migration::pallet::Event<T>
-   **/
-  PalletEvmMigrationEvent: {
-    _enum: ['TestEvent']
-  },
-  /**
-   * Lookup389: pallet_maintenance::pallet::Event<T>
-   **/
-  PalletMaintenanceEvent: {
-    _enum: ['MaintenanceEnabled', 'MaintenanceDisabled']
-  },
-  /**
-   * Lookup390: pallet_utility::pallet::Event
-   **/
-  PalletUtilityEvent: {
-    _enum: {
-      BatchInterrupted: {
-        index: 'u32',
-        error: 'SpRuntimeDispatchError',
-      },
-      BatchCompleted: 'Null',
-      BatchCompletedWithErrors: 'Null',
-      ItemCompleted: 'Null',
-      ItemFailed: {
-        error: 'SpRuntimeDispatchError',
-      },
-      DispatchedAs: {
-        result: 'Result<Null, SpRuntimeDispatchError>'
-      }
-    }
-  },
-  /**
-   * Lookup391: pallet_test_utils::pallet::Event<T>
-   **/
-  PalletTestUtilsEvent: {
-    _enum: ['ValueIsSet', 'ShouldRollback', 'BatchCompleted']
-  },
-  /**
-   * Lookup392: frame_system::Phase
-   **/
-  FrameSystemPhase: {
-    _enum: {
-      ApplyExtrinsic: 'u32',
-      Finalization: 'Null',
-      Initialization: 'Null'
-    }
-  },
-  /**
-   * Lookup394: frame_system::LastRuntimeUpgradeInfo
-   **/
-  FrameSystemLastRuntimeUpgradeInfo: {
-    specVersion: 'Compact<u32>',
-    specName: 'Text'
-  },
-  /**
-   * Lookup395: frame_system::limits::BlockWeights
-   **/
-  FrameSystemLimitsBlockWeights: {
-    baseBlock: 'SpWeightsWeightV2Weight',
-    maxBlock: 'SpWeightsWeightV2Weight',
-    perClass: 'FrameSupportDispatchPerDispatchClassWeightsPerClass'
-  },
-  /**
-   * Lookup396: frame_support::dispatch::PerDispatchClass<frame_system::limits::WeightsPerClass>
-   **/
-  FrameSupportDispatchPerDispatchClassWeightsPerClass: {
-    normal: 'FrameSystemLimitsWeightsPerClass',
-    operational: 'FrameSystemLimitsWeightsPerClass',
-    mandatory: 'FrameSystemLimitsWeightsPerClass'
-  },
-  /**
-   * Lookup397: frame_system::limits::WeightsPerClass
-   **/
-  FrameSystemLimitsWeightsPerClass: {
-    baseExtrinsic: 'SpWeightsWeightV2Weight',
-    maxExtrinsic: 'Option<SpWeightsWeightV2Weight>',
-    maxTotal: 'Option<SpWeightsWeightV2Weight>',
-    reserved: 'Option<SpWeightsWeightV2Weight>'
-  },
-  /**
-   * Lookup399: frame_system::limits::BlockLength
-   **/
-  FrameSystemLimitsBlockLength: {
-    max: 'FrameSupportDispatchPerDispatchClassU32'
-  },
-  /**
-   * Lookup400: frame_support::dispatch::PerDispatchClass<T>
-   **/
-  FrameSupportDispatchPerDispatchClassU32: {
-    normal: 'u32',
-    operational: 'u32',
-    mandatory: 'u32'
-  },
-  /**
-   * Lookup401: sp_weights::RuntimeDbWeight
-   **/
-  SpWeightsRuntimeDbWeight: {
-    read: 'u64',
-    write: 'u64'
-  },
-  /**
-   * Lookup402: sp_version::RuntimeVersion
-   **/
-  SpVersionRuntimeVersion: {
-    specName: 'Text',
-    implName: 'Text',
-    authoringVersion: 'u32',
-    specVersion: 'u32',
-    implVersion: 'u32',
-    apis: 'Vec<([u8;8],u32)>',
-    transactionVersion: 'u32',
-    stateVersion: 'u8'
-  },
-  /**
-   * Lookup406: frame_system::pallet::Error<T>
-   **/
-  FrameSystemError: {
-    _enum: ['InvalidSpecName', 'SpecVersionNeedsToIncrease', 'FailedToExtractRuntimeVersion', 'NonDefaultComposite', 'NonZeroRefCount', 'CallFiltered']
-  },
-  /**
-   * Lookup408: cumulus_pallet_parachain_system::unincluded_segment::Ancestor<primitive_types::H256>
-   **/
-  CumulusPalletParachainSystemUnincludedSegmentAncestor: {
-    usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth',
-    paraHeadHash: 'Option<H256>',
-    consumedGoAheadSignal: 'Option<PolkadotPrimitivesV5UpgradeGoAhead>'
-  },
-  /**
-   * Lookup409: cumulus_pallet_parachain_system::unincluded_segment::UsedBandwidth
-   **/
-  CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: {
-    umpMsgCount: 'u32',
-    umpTotalBytes: 'u32',
-    hrmpOutgoing: 'BTreeMap<u32, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate>'
-  },
-  /**
-   * Lookup411: cumulus_pallet_parachain_system::unincluded_segment::HrmpChannelUpdate
-   **/
-  CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: {
-    msgCount: 'u32',
-    totalBytes: 'u32'
-  },
-  /**
-   * Lookup415: polkadot_primitives::v5::UpgradeGoAhead
-   **/
-  PolkadotPrimitivesV5UpgradeGoAhead: {
-    _enum: ['Abort', 'GoAhead']
-  },
-  /**
-   * Lookup416: cumulus_pallet_parachain_system::unincluded_segment::SegmentTracker<primitive_types::H256>
-   **/
-  CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: {
-    usedBandwidth: 'CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth',
-    hrmpWatermark: 'Option<u32>',
-    consumedGoAheadSignal: 'Option<PolkadotPrimitivesV5UpgradeGoAhead>'
-  },
-  /**
-   * Lookup418: polkadot_primitives::v5::UpgradeRestriction
-   **/
-  PolkadotPrimitivesV5UpgradeRestriction: {
-    _enum: ['Present']
-  },
-  /**
-   * Lookup419: cumulus_pallet_parachain_system::relay_state_snapshot::MessagingStateSnapshot
-   **/
-  CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: {
-    dmqMqcHead: 'H256',
-    relayDispatchQueueRemainingCapacity: 'CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity',
-    ingressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>',
-    egressChannels: 'Vec<(u32,PolkadotPrimitivesV5AbridgedHrmpChannel)>'
-  },
-  /**
-   * Lookup420: cumulus_pallet_parachain_system::relay_state_snapshot::RelayDispatchQueueRemainingCapacity
-   **/
-  CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: {
-    remainingCount: 'u32',
-    remainingSize: 'u32'
-  },
-  /**
-   * Lookup423: polkadot_primitives::v5::AbridgedHrmpChannel
-   **/
-  PolkadotPrimitivesV5AbridgedHrmpChannel: {
-    maxCapacity: 'u32',
-    maxTotalSize: 'u32',
-    maxMessageSize: 'u32',
-    msgCount: 'u32',
-    totalSize: 'u32',
-    mqcHead: 'Option<H256>'
-  },
-  /**
-   * Lookup424: polkadot_primitives::v5::AbridgedHostConfiguration
-   **/
-  PolkadotPrimitivesV5AbridgedHostConfiguration: {
-    maxCodeSize: 'u32',
-    maxHeadDataSize: 'u32',
-    maxUpwardQueueCount: 'u32',
-    maxUpwardQueueSize: 'u32',
-    maxUpwardMessageSize: 'u32',
-    maxUpwardMessageNumPerCandidate: 'u32',
-    hrmpMaxMessageNumPerCandidate: 'u32',
-    validationUpgradeCooldown: 'u32',
-    validationUpgradeDelay: 'u32',
-    asyncBackingParams: 'PolkadotPrimitivesVstagingAsyncBackingParams'
-  },
-  /**
-   * Lookup425: polkadot_primitives::vstaging::AsyncBackingParams
-   **/
-  PolkadotPrimitivesVstagingAsyncBackingParams: {
-    maxCandidateDepth: 'u32',
-    allowedAncestryLen: 'u32'
-  },
-  /**
-   * Lookup431: polkadot_core_primitives::OutboundHrmpMessage<polkadot_parachain_primitives::primitives::Id>
-   **/
-  PolkadotCorePrimitivesOutboundHrmpMessage: {
-    recipient: 'u32',
-    data: 'Bytes'
-  },
-  /**
-   * Lookup432: cumulus_pallet_parachain_system::CodeUpgradeAuthorization<T>
-   **/
-  CumulusPalletParachainSystemCodeUpgradeAuthorization: {
-    codeHash: 'H256',
-    checkVersion: 'bool'
-  },
-  /**
-   * Lookup433: cumulus_pallet_parachain_system::pallet::Error<T>
-   **/
-  CumulusPalletParachainSystemError: {
-    _enum: ['OverlappingUpgrades', 'ProhibitedByPolkadot', 'TooBig', 'ValidationDataNotAvailable', 'HostConfigurationNotAvailable', 'NotScheduled', 'NothingAuthorized', 'Unauthorized']
-  },
-  /**
-   * Lookup435: pallet_collator_selection::pallet::Error<T>
-   **/
-  PalletCollatorSelectionError: {
-    _enum: ['TooManyCandidates', 'Unknown', 'Permission', 'AlreadyHoldingLicense', 'NoLicense', 'AlreadyCandidate', 'NotCandidate', 'TooManyInvulnerables', 'TooFewInvulnerables', 'AlreadyInvulnerable', 'NotInvulnerable', 'NoAssociatedValidatorId', 'ValidatorNotRegistered']
-  },
-  /**
-   * Lookup439: sp_core::crypto::KeyTypeId
-   **/
-  SpCoreCryptoKeyTypeId: '[u8;4]',
-  /**
-   * Lookup440: pallet_session::pallet::Error<T>
-   **/
-  PalletSessionError: {
-    _enum: ['InvalidProof', 'NoAssociatedValidatorId', 'DuplicatedKey', 'NoKeys', 'NoAccount']
-  },
-  /**
-   * Lookup446: pallet_balances::types::BalanceLock<Balance>
-   **/
-  PalletBalancesBalanceLock: {
-    id: '[u8;8]',
-    amount: 'u128',
-    reasons: 'PalletBalancesReasons'
-  },
-  /**
-   * Lookup447: pallet_balances::types::Reasons
-   **/
-  PalletBalancesReasons: {
-    _enum: ['Fee', 'Misc', 'All']
-  },
-  /**
-   * Lookup450: pallet_balances::types::ReserveData<ReserveIdentifier, Balance>
-   **/
-  PalletBalancesReserveData: {
-    id: '[u8;16]',
-    amount: 'u128'
-  },
-  /**
-   * Lookup454: opal_runtime::RuntimeHoldReason
-   **/
-  OpalRuntimeRuntimeHoldReason: {
-    _enum: {
-      __Unused0: 'Null',
-      __Unused1: 'Null',
-      __Unused2: 'Null',
-      __Unused3: 'Null',
-      __Unused4: 'Null',
-      __Unused5: 'Null',
-      __Unused6: 'Null',
-      __Unused7: 'Null',
-      __Unused8: 'Null',
-      __Unused9: 'Null',
-      __Unused10: 'Null',
-      __Unused11: 'Null',
-      __Unused12: 'Null',
-      __Unused13: 'Null',
-      __Unused14: 'Null',
-      __Unused15: 'Null',
-      __Unused16: 'Null',
-      __Unused17: 'Null',
-      __Unused18: 'Null',
-      __Unused19: 'Null',
-      __Unused20: 'Null',
-      __Unused21: 'Null',
-      __Unused22: 'Null',
-      CollatorSelection: 'PalletCollatorSelectionHoldReason'
-    }
-  },
-  /**
-   * Lookup455: pallet_collator_selection::pallet::HoldReason
-   **/
-  PalletCollatorSelectionHoldReason: {
-    _enum: ['LicenseBond']
-  },
-  /**
-   * Lookup458: pallet_balances::types::IdAmount<Id, Balance>
-   **/
-  PalletBalancesIdAmount: {
-    id: '[u8;16]',
-    amount: 'u128'
-  },
-  /**
-   * Lookup460: pallet_balances::pallet::Error<T, I>
-   **/
-  PalletBalancesError: {
-    _enum: ['VestingBalance', 'LiquidityRestrictions', 'InsufficientBalance', 'ExistentialDeposit', 'Expendability', 'ExistingVestingSchedule', 'DeadAccount', 'TooManyReserves', 'TooManyHolds', 'TooManyFreezes']
-  },
-  /**
-   * Lookup462: pallet_transaction_payment::Releases
-   **/
-  PalletTransactionPaymentReleases: {
-    _enum: ['V1Ancient', 'V2']
-  },
-  /**
-   * Lookup463: pallet_treasury::Proposal<sp_core::crypto::AccountId32, Balance>
-   **/
-  PalletTreasuryProposal: {
-    proposer: 'AccountId32',
-    value: 'u128',
-    beneficiary: 'AccountId32',
-    bond: 'u128'
-  },
-  /**
-   * Lookup466: frame_support::PalletId
-   **/
-  FrameSupportPalletId: '[u8;8]',
-  /**
-   * Lookup467: pallet_treasury::pallet::Error<T, I>
-   **/
-  PalletTreasuryError: {
-    _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'InsufficientPermission', 'ProposalNotApproved']
-  },
-  /**
-   * Lookup468: pallet_sudo::pallet::Error<T>
-   **/
-  PalletSudoError: {
-    _enum: ['RequireSudo']
-  },
-  /**
-   * Lookup470: orml_vesting::module::Error<T>
-   **/
-  OrmlVestingModuleError: {
-    _enum: ['ZeroVestingPeriod', 'ZeroVestingPeriodCount', 'InsufficientBalanceToLock', 'TooManyVestingSchedules', 'AmountLow', 'MaxVestingSchedulesExceeded']
-  },
-  /**
-   * Lookup471: orml_xtokens::module::Error<T>
-   **/
-  OrmlXtokensModuleError: {
-    _enum: ['AssetHasNoReserve', 'NotCrossChainTransfer', 'InvalidDest', 'NotCrossChainTransferableCurrency', 'UnweighableMessage', 'XcmExecutionFailed', 'CannotReanchor', 'InvalidAncestry', 'InvalidAsset', 'DestinationNotInvertible', 'BadVersion', 'DistinctReserveForAssetAndFee', 'ZeroFee', 'ZeroAmount', 'TooManyAssetsBeingSent', 'AssetIndexNonExistent', 'FeeNotEnough', 'NotSupportedMultiLocation', 'MinXcmFeeNotDefined']
-  },
-  /**
-   * Lookup474: orml_tokens::BalanceLock<Balance>
-   **/
-  OrmlTokensBalanceLock: {
-    id: '[u8;8]',
-    amount: 'u128'
-  },
-  /**
-   * Lookup476: orml_tokens::AccountData<Balance>
-   **/
-  OrmlTokensAccountData: {
-    free: 'u128',
-    reserved: 'u128',
-    frozen: 'u128'
-  },
-  /**
-   * Lookup478: orml_tokens::ReserveData<ReserveIdentifier, Balance>
-   **/
-  OrmlTokensReserveData: {
-    id: 'Null',
-    amount: 'u128'
-  },
-  /**
-   * Lookup480: orml_tokens::module::Error<T>
-   **/
-  OrmlTokensModuleError: {
-    _enum: ['BalanceTooLow', 'AmountIntoBalanceFailed', 'LiquidityRestrictions', 'MaxLocksExceeded', 'KeepAlive', 'ExistentialDeposit', 'DeadAccount', 'TooManyReserves']
-  },
-  /**
-   * Lookup485: pallet_identity::types::RegistrarInfo<Balance, sp_core::crypto::AccountId32>
-   **/
-  PalletIdentityRegistrarInfo: {
-    account: 'AccountId32',
-    fee: 'u128',
-    fields: 'PalletIdentityBitFlags'
-  },
-  /**
-   * Lookup487: pallet_identity::pallet::Error<T>
-   **/
-  PalletIdentityError: {
-    _enum: ['TooManySubAccounts', 'NotFound', 'NotNamed', 'EmptyIndex', 'FeeChanged', 'NoIdentity', 'StickyJudgement', 'JudgementGiven', 'InvalidJudgement', 'InvalidIndex', 'InvalidTarget', 'TooManyFields', 'TooManyRegistrars', 'AlreadyClaimed', 'NotSub', 'NotOwned', 'JudgementForDifferentIdentity', 'JudgementPaymentFailed']
-  },
-  /**
-   * Lookup488: pallet_preimage::RequestStatus<sp_core::crypto::AccountId32, Balance>
-   **/
-  PalletPreimageRequestStatus: {
-    _enum: {
-      Unrequested: {
-        deposit: '(AccountId32,u128)',
-        len: 'u32',
-      },
-      Requested: {
-        deposit: 'Option<(AccountId32,u128)>',
-        count: 'u32',
-        len: 'Option<u32>'
-      }
-    }
-  },
-  /**
-   * Lookup493: pallet_preimage::pallet::Error<T>
-   **/
-  PalletPreimageError: {
-    _enum: ['TooBig', 'AlreadyNoted', 'NotAuthorized', 'NotNoted', 'Requested', 'NotRequested']
-  },
-  /**
-   * Lookup499: pallet_democracy::types::ReferendumInfo<BlockNumber, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance>
-   **/
-  PalletDemocracyReferendumInfo: {
-    _enum: {
-      Ongoing: 'PalletDemocracyReferendumStatus',
-      Finished: {
-        approved: 'bool',
-        end: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup500: pallet_democracy::types::ReferendumStatus<BlockNumber, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance>
-   **/
-  PalletDemocracyReferendumStatus: {
-    end: 'u32',
-    proposal: 'FrameSupportPreimagesBounded',
-    threshold: 'PalletDemocracyVoteThreshold',
-    delay: 'u32',
-    tally: 'PalletDemocracyTally'
-  },
-  /**
-   * Lookup501: pallet_democracy::types::Tally<Balance>
-   **/
-  PalletDemocracyTally: {
-    ayes: 'u128',
-    nays: 'u128',
-    turnout: 'u128'
-  },
-  /**
-   * Lookup502: pallet_democracy::vote::Voting<Balance, sp_core::crypto::AccountId32, BlockNumber, MaxVotes>
-   **/
-  PalletDemocracyVoteVoting: {
-    _enum: {
-      Direct: {
-        votes: 'Vec<(u32,PalletDemocracyVoteAccountVote)>',
-        delegations: 'PalletDemocracyDelegations',
-        prior: 'PalletDemocracyVotePriorLock',
-      },
-      Delegating: {
-        balance: 'u128',
-        target: 'AccountId32',
-        conviction: 'PalletDemocracyConviction',
-        delegations: 'PalletDemocracyDelegations',
-        prior: 'PalletDemocracyVotePriorLock'
-      }
-    }
-  },
-  /**
-   * Lookup506: pallet_democracy::types::Delegations<Balance>
-   **/
-  PalletDemocracyDelegations: {
-    votes: 'u128',
-    capital: 'u128'
-  },
-  /**
-   * Lookup507: pallet_democracy::vote::PriorLock<BlockNumber, Balance>
-   **/
-  PalletDemocracyVotePriorLock: '(u32,u128)',
-  /**
-   * Lookup510: pallet_democracy::pallet::Error<T>
-   **/
-  PalletDemocracyError: {
-    _enum: ['ValueLow', 'ProposalMissing', 'AlreadyCanceled', 'DuplicateProposal', 'ProposalBlacklisted', 'NotSimpleMajority', 'InvalidHash', 'NoProposal', 'AlreadyVetoed', 'ReferendumInvalid', 'NoneWaiting', 'NotVoter', 'NoPermission', 'AlreadyDelegating', 'InsufficientFunds', 'NotDelegating', 'VotesExist', 'InstantNotAllowed', 'Nonsense', 'WrongUpperBound', 'MaxVotesReached', 'TooMany', 'VotingPeriodLow', 'PreimageNotExist']
-  },
-  /**
-   * Lookup512: pallet_collective::Votes<sp_core::crypto::AccountId32, BlockNumber>
-   **/
-  PalletCollectiveVotes: {
-    index: 'u32',
-    threshold: 'u32',
-    ayes: 'Vec<AccountId32>',
-    nays: 'Vec<AccountId32>',
-    end: 'u32'
-  },
-  /**
-   * Lookup513: pallet_collective::pallet::Error<T, I>
-   **/
-  PalletCollectiveError: {
-    _enum: ['NotMember', 'DuplicateProposal', 'ProposalMissing', 'WrongIndex', 'DuplicateVote', 'AlreadyInitialized', 'TooEarly', 'TooManyProposals', 'WrongProposalWeight', 'WrongProposalLength', 'PrimeAccountNotMember']
-  },
-  /**
-   * Lookup517: pallet_membership::pallet::Error<T, I>
-   **/
-  PalletMembershipError: {
-    _enum: ['AlreadyMember', 'NotMember', 'TooManyMembers']
-  },
-  /**
-   * Lookup520: pallet_ranked_collective::MemberRecord
-   **/
-  PalletRankedCollectiveMemberRecord: {
-    rank: 'u16'
-  },
-  /**
-   * Lookup525: pallet_ranked_collective::pallet::Error<T, I>
-   **/
-  PalletRankedCollectiveError: {
-    _enum: ['AlreadyMember', 'NotMember', 'NotPolling', 'Ongoing', 'NoneRemaining', 'Corruption', 'RankTooLow', 'InvalidWitness', 'NoPermission']
-  },
-  /**
-   * Lookup526: pallet_referenda::types::ReferendumInfo<TrackId, opal_runtime::OriginCaller, Moment, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance, pallet_ranked_collective::Tally<T, I, M>, sp_core::crypto::AccountId32, ScheduleAddress>
-   **/
-  PalletReferendaReferendumInfo: {
-    _enum: {
-      Ongoing: 'PalletReferendaReferendumStatus',
-      Approved: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
-      Rejected: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
-      Cancelled: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
-      TimedOut: '(u32,Option<PalletReferendaDeposit>,Option<PalletReferendaDeposit>)',
-      Killed: 'u32'
-    }
-  },
-  /**
-   * Lookup527: pallet_referenda::types::ReferendumStatus<TrackId, opal_runtime::OriginCaller, Moment, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, Balance, pallet_ranked_collective::Tally<T, I, M>, sp_core::crypto::AccountId32, ScheduleAddress>
-   **/
-  PalletReferendaReferendumStatus: {
-    track: 'u16',
-    origin: 'OpalRuntimeOriginCaller',
-    proposal: 'FrameSupportPreimagesBounded',
-    enactment: 'FrameSupportScheduleDispatchTime',
-    submitted: 'u32',
-    submissionDeposit: 'PalletReferendaDeposit',
-    decisionDeposit: 'Option<PalletReferendaDeposit>',
-    deciding: 'Option<PalletReferendaDecidingStatus>',
-    tally: 'PalletRankedCollectiveTally',
-    inQueue: 'bool',
-    alarm: 'Option<(u32,(u32,u32))>'
-  },
-  /**
-   * Lookup528: pallet_referenda::types::Deposit<sp_core::crypto::AccountId32, Balance>
-   **/
-  PalletReferendaDeposit: {
-    who: 'AccountId32',
-    amount: 'u128'
-  },
-  /**
-   * Lookup531: pallet_referenda::types::DecidingStatus<BlockNumber>
-   **/
-  PalletReferendaDecidingStatus: {
-    since: 'u32',
-    confirming: 'Option<u32>'
-  },
-  /**
-   * Lookup537: pallet_referenda::types::TrackInfo<Balance, Moment>
-   **/
-  PalletReferendaTrackInfo: {
-    name: 'Text',
-    maxDeciding: 'u32',
-    decisionDeposit: 'u128',
-    preparePeriod: 'u32',
-    decisionPeriod: 'u32',
-    confirmPeriod: 'u32',
-    minEnactmentPeriod: 'u32',
-    minApproval: 'PalletReferendaCurve',
-    minSupport: 'PalletReferendaCurve'
-  },
-  /**
-   * Lookup538: pallet_referenda::types::Curve
-   **/
-  PalletReferendaCurve: {
-    _enum: {
-      LinearDecreasing: {
-        length: 'Perbill',
-        floor: 'Perbill',
-        ceil: 'Perbill',
-      },
-      SteppedDecreasing: {
-        begin: 'Perbill',
-        end: 'Perbill',
-        step: 'Perbill',
-        period: 'Perbill',
-      },
-      Reciprocal: {
-        factor: 'i64',
-        xOffset: 'i64',
-        yOffset: 'i64'
-      }
-    }
-  },
-  /**
-   * Lookup541: pallet_referenda::pallet::Error<T, I>
-   **/
-  PalletReferendaError: {
-    _enum: ['NotOngoing', 'HasDeposit', 'BadTrack', 'Full', 'QueueEmpty', 'BadReferendum', 'NothingToDo', 'NoTrack', 'Unfinished', 'NoPermission', 'NoDeposit', 'BadStatus', 'PreimageNotExist']
-  },
-  /**
-   * Lookup544: pallet_scheduler::Scheduled<Name, frame_support::traits::preimages::Bounded<opal_runtime::RuntimeCall>, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32>
-   **/
-  PalletSchedulerScheduled: {
-    maybeId: 'Option<[u8;32]>',
-    priority: 'u8',
-    call: 'FrameSupportPreimagesBounded',
-    maybePeriodic: 'Option<(u32,u32)>',
-    origin: 'OpalRuntimeOriginCaller'
-  },
-  /**
-   * Lookup546: pallet_scheduler::pallet::Error<T>
-   **/
-  PalletSchedulerError: {
-    _enum: ['FailedToSchedule', 'NotFound', 'TargetBlockNumberInPast', 'RescheduleNoChange', 'Named']
-  },
-  /**
-   * Lookup548: cumulus_pallet_xcmp_queue::InboundChannelDetails
-   **/
-  CumulusPalletXcmpQueueInboundChannelDetails: {
-    sender: 'u32',
-    state: 'CumulusPalletXcmpQueueInboundState',
-    messageMetadata: 'Vec<(u32,PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat)>'
-  },
-  /**
-   * Lookup549: cumulus_pallet_xcmp_queue::InboundState
-   **/
-  CumulusPalletXcmpQueueInboundState: {
-    _enum: ['Ok', 'Suspended']
-  },
-  /**
-   * Lookup552: polkadot_parachain_primitives::primitives::XcmpMessageFormat
-   **/
-  PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: {
-    _enum: ['ConcatenatedVersionedXcm', 'ConcatenatedEncodedBlob', 'Signals']
-  },
-  /**
-   * Lookup555: cumulus_pallet_xcmp_queue::OutboundChannelDetails
-   **/
-  CumulusPalletXcmpQueueOutboundChannelDetails: {
-    recipient: 'u32',
-    state: 'CumulusPalletXcmpQueueOutboundState',
-    signalsExist: 'bool',
-    firstIndex: 'u16',
-    lastIndex: 'u16'
-  },
-  /**
-   * Lookup556: cumulus_pallet_xcmp_queue::OutboundState
-   **/
-  CumulusPalletXcmpQueueOutboundState: {
-    _enum: ['Ok', 'Suspended']
-  },
-  /**
-   * Lookup558: cumulus_pallet_xcmp_queue::QueueConfigData
-   **/
-  CumulusPalletXcmpQueueQueueConfigData: {
-    suspendThreshold: 'u32',
-    dropThreshold: 'u32',
-    resumeThreshold: 'u32',
-    thresholdWeight: 'SpWeightsWeightV2Weight',
-    weightRestrictDecay: 'SpWeightsWeightV2Weight',
-    xcmpMaxIndividualWeight: 'SpWeightsWeightV2Weight'
-  },
-  /**
-   * Lookup560: cumulus_pallet_xcmp_queue::pallet::Error<T>
-   **/
-  CumulusPalletXcmpQueueError: {
-    _enum: ['FailedToSend', 'BadXcmOrigin', 'BadXcm', 'BadOverweightIndex', 'WeightOverLimit']
-  },
-  /**
-   * Lookup561: pallet_xcm::pallet::QueryStatus<BlockNumber>
-   **/
-  PalletXcmQueryStatus: {
-    _enum: {
-      Pending: {
-        responder: 'StagingXcmVersionedMultiLocation',
-        maybeMatchQuerier: 'Option<StagingXcmVersionedMultiLocation>',
-        maybeNotify: 'Option<(u8,u8)>',
-        timeout: 'u32',
-      },
-      VersionNotifier: {
-        origin: 'StagingXcmVersionedMultiLocation',
-        isActive: 'bool',
-      },
-      Ready: {
-        response: 'StagingXcmVersionedResponse',
-        at: 'u32'
-      }
-    }
-  },
-  /**
-   * Lookup565: staging_xcm::VersionedResponse
-   **/
-  StagingXcmVersionedResponse: {
-    _enum: {
-      __Unused0: 'Null',
-      __Unused1: 'Null',
-      V2: 'StagingXcmV2Response',
-      V3: 'StagingXcmV3Response'
-    }
-  },
-  /**
-   * Lookup571: pallet_xcm::pallet::VersionMigrationStage
-   **/
-  PalletXcmVersionMigrationStage: {
-    _enum: {
-      MigrateSupportedVersion: 'Null',
-      MigrateVersionNotifiers: 'Null',
-      NotifyCurrentTargets: 'Option<Bytes>',
-      MigrateAndNotifyOldTargets: 'Null'
-    }
-  },
-  /**
-   * Lookup574: staging_xcm::VersionedAssetId
-   **/
-  StagingXcmVersionedAssetId: {
-    _enum: {
-      __Unused0: 'Null',
-      __Unused1: 'Null',
-      __Unused2: 'Null',
-      V3: 'StagingXcmV3MultiassetAssetId'
-    }
-  },
-  /**
-   * Lookup575: pallet_xcm::pallet::RemoteLockedFungibleRecord<ConsumerIdentifier, MaxConsumers>
-   **/
-  PalletXcmRemoteLockedFungibleRecord: {
-    amount: 'u128',
-    owner: 'StagingXcmVersionedMultiLocation',
-    locker: 'StagingXcmVersionedMultiLocation',
-    consumers: 'Vec<(Null,u128)>'
-  },
-  /**
-   * Lookup582: pallet_xcm::pallet::Error<T>
-   **/
-  PalletXcmError: {
-    _enum: ['Unreachable', 'SendFailure', 'Filtered', 'UnweighableMessage', 'DestinationNotInvertible', 'Empty', 'CannotReanchor', 'TooManyAssets', 'InvalidOrigin', 'BadVersion', 'BadLocation', 'NoSubscription', 'AlreadySubscribed', 'InvalidAsset', 'LowBalance', 'TooManyLocks', 'AccountNotSovereign', 'FeesNotMet', 'LockNotFound', 'InUse']
-  },
-  /**
-   * Lookup583: cumulus_pallet_xcm::pallet::Error<T>
-   **/
-  CumulusPalletXcmError: 'Null',
-  /**
-   * Lookup584: cumulus_pallet_dmp_queue::ConfigData
-   **/
-  CumulusPalletDmpQueueConfigData: {
-    maxIndividual: 'SpWeightsWeightV2Weight'
-  },
-  /**
-   * Lookup585: cumulus_pallet_dmp_queue::PageIndexData
-   **/
-  CumulusPalletDmpQueuePageIndexData: {
-    beginUsed: 'u32',
-    endUsed: 'u32',
-    overweightCount: 'u64'
-  },
-  /**
-   * Lookup588: cumulus_pallet_dmp_queue::pallet::Error<T>
-   **/
-  CumulusPalletDmpQueueError: {
-    _enum: ['Unknown', 'OverLimit']
-  },
-  /**
-   * Lookup592: pallet_unique::pallet::Error<T>
-   **/
-  PalletUniqueError: {
-    _enum: ['CollectionDecimalPointLimitExceeded', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection']
-  },
-  /**
-   * Lookup593: pallet_configuration::pallet::Error<T>
-   **/
-  PalletConfigurationError: {
-    _enum: ['InconsistentConfiguration']
-  },
-  /**
-   * Lookup594: up_data_structs::Collection<sp_core::crypto::AccountId32>
-   **/
-  UpDataStructsCollection: {
-    owner: 'AccountId32',
-    mode: 'UpDataStructsCollectionMode',
-    name: 'Vec<u16>',
-    description: 'Vec<u16>',
-    tokenPrefix: 'Bytes',
-    sponsorship: 'UpDataStructsSponsorshipStateAccountId32',
-    limits: 'UpDataStructsCollectionLimits',
-    permissions: 'UpDataStructsCollectionPermissions',
-    flags: '[u8;1]'
-  },
-  /**
-   * Lookup595: up_data_structs::SponsorshipState<sp_core::crypto::AccountId32>
-   **/
-  UpDataStructsSponsorshipStateAccountId32: {
-    _enum: {
-      Disabled: 'Null',
-      Unconfirmed: 'AccountId32',
-      Confirmed: 'AccountId32'
-    }
-  },
-  /**
-   * Lookup596: up_data_structs::Properties
-   **/
-  UpDataStructsProperties: {
-    map: 'UpDataStructsPropertiesMapBoundedVec',
-    consumedSpace: 'u32',
-    reserved: 'u32'
-  },
-  /**
-   * Lookup597: up_data_structs::PropertiesMap<bounded_collections::bounded_vec::BoundedVec<T, S>>
-   **/
-  UpDataStructsPropertiesMapBoundedVec: 'BTreeMap<Bytes, Bytes>',
-  /**
-   * Lookup602: up_data_structs::PropertiesMap<up_data_structs::PropertyPermission>
-   **/
-  UpDataStructsPropertiesMapPropertyPermission: 'BTreeMap<Bytes, UpDataStructsPropertyPermission>',
-  /**
-   * Lookup609: up_data_structs::CollectionStats
-   **/
-  UpDataStructsCollectionStats: {
-    created: 'u32',
-    destroyed: 'u32',
-    alive: 'u32'
-  },
-  /**
-   * Lookup610: up_data_structs::TokenChild
-   **/
-  UpDataStructsTokenChild: {
-    token: 'u32',
-    collection: 'u32'
-  },
-  /**
-   * Lookup611: PhantomType::up_data_structs<T>
-   **/
-  PhantomTypeUpDataStructs: '[(UpDataStructsTokenData,UpDataStructsRpcCollection,UpPovEstimateRpcPovInfo);0]',
-  /**
-   * Lookup613: up_data_structs::TokenData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  UpDataStructsTokenData: {
-    properties: 'Vec<UpDataStructsProperty>',
-    owner: 'Option<PalletEvmAccountBasicCrossAccountIdRepr>',
-    pieces: 'u128'
-  },
-  /**
-   * Lookup614: up_data_structs::RpcCollection<sp_core::crypto::AccountId32>
-   **/
-  UpDataStructsRpcCollection: {
-    owner: 'AccountId32',
-    mode: 'UpDataStructsCollectionMode',
-    name: 'Vec<u16>',
-    description: 'Vec<u16>',
-    tokenPrefix: 'Bytes',
-    sponsorship: 'UpDataStructsSponsorshipStateAccountId32',
-    limits: 'UpDataStructsCollectionLimits',
-    permissions: 'UpDataStructsCollectionPermissions',
-    tokenPropertyPermissions: 'Vec<UpDataStructsPropertyKeyPermission>',
-    properties: 'Vec<UpDataStructsProperty>',
-    readOnly: 'bool',
-    flags: 'UpDataStructsRpcCollectionFlags'
-  },
-  /**
-   * Lookup615: up_data_structs::RpcCollectionFlags
-   **/
-  UpDataStructsRpcCollectionFlags: {
-    foreign: 'bool',
-    erc721metadata: 'bool'
-  },
-  /**
-   * Lookup616: up_pov_estimate_rpc::PovInfo
-   **/
-  UpPovEstimateRpcPovInfo: {
-    proofSize: 'u64',
-    compactProofSize: 'u64',
-    compressedProofSize: 'u64',
-    results: 'Vec<Result<Result<Null, SpRuntimeDispatchError>, SpRuntimeTransactionValidityTransactionValidityError>>',
-    keyValues: 'Vec<UpPovEstimateRpcTrieKeyValue>'
-  },
-  /**
-   * Lookup619: sp_runtime::transaction_validity::TransactionValidityError
-   **/
-  SpRuntimeTransactionValidityTransactionValidityError: {
-    _enum: {
-      Invalid: 'SpRuntimeTransactionValidityInvalidTransaction',
-      Unknown: 'SpRuntimeTransactionValidityUnknownTransaction'
-    }
-  },
-  /**
-   * Lookup620: sp_runtime::transaction_validity::InvalidTransaction
-   **/
-  SpRuntimeTransactionValidityInvalidTransaction: {
-    _enum: {
-      Call: 'Null',
-      Payment: 'Null',
-      Future: 'Null',
-      Stale: 'Null',
-      BadProof: 'Null',
-      AncientBirthBlock: 'Null',
-      ExhaustsResources: 'Null',
-      Custom: 'u8',
-      BadMandatory: 'Null',
-      MandatoryValidation: 'Null',
-      BadSigner: 'Null'
-    }
-  },
-  /**
-   * Lookup621: sp_runtime::transaction_validity::UnknownTransaction
-   **/
-  SpRuntimeTransactionValidityUnknownTransaction: {
-    _enum: {
-      CannotLookup: 'Null',
-      NoUnsignedValidator: 'Null',
-      Custom: 'u8'
-    }
-  },
-  /**
-   * Lookup623: up_pov_estimate_rpc::TrieKeyValue
-   **/
-  UpPovEstimateRpcTrieKeyValue: {
-    key: 'Bytes',
-    value: 'Bytes'
-  },
-  /**
-   * Lookup625: pallet_common::pallet::Error<T>
-   **/
-  PalletCommonError: {
-    _enum: ['CollectionNotFound', 'MustBeTokenOwner', 'NoPermission', 'CantDestroyNotEmptyCollection', 'PublicMintingNotAllowed', 'AddressNotInAllowlist', 'CollectionNameLimitExceeded', 'CollectionDescriptionLimitExceeded', 'CollectionTokenPrefixLimitExceeded', 'TotalCollectionsLimitExceeded', 'CollectionAdminCountExceeded', 'CollectionLimitBoundsExceeded', 'OwnerPermissionsCantBeReverted', 'TransferNotAllowed', 'AccountTokenLimitExceeded', 'CollectionTokenLimitExceeded', 'MetadataFlagFrozen', 'TokenNotFound', 'TokenValueTooLow', 'ApprovedValueTooLow', 'CantApproveMoreThanOwned', 'AddressIsNotEthMirror', 'AddressIsZero', 'UnsupportedOperation', 'NotSufficientFounds', 'UserIsNotAllowedToNest', 'SourceCollectionIsNotAllowedToNest', 'CollectionFieldSizeExceeded', 'NoSpaceForProperty', 'PropertyLimitReached', 'PropertyKeyIsTooLong', 'InvalidCharacterInPropertyKey', 'EmptyPropertyKey', 'CollectionIsExternal', 'CollectionIsInternal', 'ConfirmSponsorshipFail', 'UserIsNotCollectionAdmin', 'FungibleItemsHaveNoId']
-  },
-  /**
-   * Lookup627: pallet_fungible::pallet::Error<T>
-   **/
-  PalletFungibleError: {
-    _enum: ['NotFungibleDataUsedToMintFungibleCollectionToken', 'FungibleItemsDontHaveData', 'FungibleDisallowsNesting', 'SettingPropertiesNotAllowed', 'SettingAllowanceForAllNotAllowed', 'FungibleTokensAreAlwaysValid']
-  },
-  /**
-   * Lookup632: pallet_refungible::pallet::Error<T>
-   **/
-  PalletRefungibleError: {
-    _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed']
-  },
-  /**
-   * Lookup633: pallet_nonfungible::ItemData<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  PalletNonfungibleItemData: {
-    owner: 'PalletEvmAccountBasicCrossAccountIdRepr'
-  },
-  /**
-   * Lookup635: up_data_structs::PropertyScope
-   **/
-  UpDataStructsPropertyScope: {
-    _enum: ['None', 'Rmrk']
-  },
-  /**
-   * Lookup638: pallet_nonfungible::pallet::Error<T>
-   **/
-  PalletNonfungibleError: {
-    _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren']
-  },
-  /**
-   * Lookup639: pallet_structure::pallet::Error<T>
-   **/
-  PalletStructureError: {
-    _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound', 'CantNestTokenUnderCollection']
-  },
-  /**
-   * Lookup644: pallet_app_promotion::pallet::Error<T>
-   **/
-  PalletAppPromotionError: {
-    _enum: ['AdminNotSet', 'NoPermission', 'NotSufficientFunds', 'PendingForBlockOverflow', 'SponsorNotSet', 'InsufficientStakedBalance', 'InconsistencyState']
-  },
-  /**
-   * Lookup645: pallet_foreign_assets::module::Error<T>
-   **/
-  PalletForeignAssetsModuleError: {
-    _enum: ['BadLocation', 'MultiLocationExisted', 'AssetIdNotExists', 'AssetIdExisted']
-  },
-  /**
-   * Lookup646: pallet_evm::CodeMetadata
-   **/
-  PalletEvmCodeMetadata: {
-    _alias: {
-      size_: 'size',
-      hash_: 'hash'
-    },
-    size_: 'u64',
-    hash_: 'H256'
-  },
-  /**
-   * Lookup648: pallet_evm::pallet::Error<T>
-   **/
-  PalletEvmError: {
-    _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce', 'GasLimitTooLow', 'GasLimitTooHigh', 'Undefined', 'Reentrancy', 'TransactionMustComeFromEOA']
-  },
-  /**
-   * Lookup651: fp_rpc::TransactionStatus
-   **/
-  FpRpcTransactionStatus: {
-    transactionHash: 'H256',
-    transactionIndex: 'u32',
-    from: 'H160',
-    to: 'Option<H160>',
-    contractAddress: 'Option<H160>',
-    logs: 'Vec<EthereumLog>',
-    logsBloom: 'EthbloomBloom'
-  },
-  /**
-   * Lookup653: ethbloom::Bloom
-   **/
-  EthbloomBloom: '[u8;256]',
-  /**
-   * Lookup655: ethereum::receipt::ReceiptV3
-   **/
-  EthereumReceiptReceiptV3: {
-    _enum: {
-      Legacy: 'EthereumReceiptEip658ReceiptData',
-      EIP2930: 'EthereumReceiptEip658ReceiptData',
-      EIP1559: 'EthereumReceiptEip658ReceiptData'
-    }
-  },
-  /**
-   * Lookup656: ethereum::receipt::EIP658ReceiptData
-   **/
-  EthereumReceiptEip658ReceiptData: {
-    statusCode: 'u8',
-    usedGas: 'U256',
-    logsBloom: 'EthbloomBloom',
-    logs: 'Vec<EthereumLog>'
-  },
-  /**
-   * Lookup657: ethereum::block::Block<ethereum::transaction::TransactionV2>
-   **/
-  EthereumBlock: {
-    header: 'EthereumHeader',
-    transactions: 'Vec<EthereumTransactionTransactionV2>',
-    ommers: 'Vec<EthereumHeader>'
-  },
-  /**
-   * Lookup658: ethereum::header::Header
-   **/
-  EthereumHeader: {
-    parentHash: 'H256',
-    ommersHash: 'H256',
-    beneficiary: 'H160',
-    stateRoot: 'H256',
-    transactionsRoot: 'H256',
-    receiptsRoot: 'H256',
-    logsBloom: 'EthbloomBloom',
-    difficulty: 'U256',
-    number: 'U256',
-    gasLimit: 'U256',
-    gasUsed: 'U256',
-    timestamp: 'u64',
-    extraData: 'Bytes',
-    mixHash: 'H256',
-    nonce: 'EthereumTypesHashH64'
-  },
-  /**
-   * Lookup659: ethereum_types::hash::H64
-   **/
-  EthereumTypesHashH64: '[u8;8]',
-  /**
-   * Lookup664: pallet_ethereum::pallet::Error<T>
-   **/
-  PalletEthereumError: {
-    _enum: ['InvalidSignature', 'PreLogExists']
-  },
-  /**
-   * Lookup665: pallet_evm_coder_substrate::pallet::Error<T>
-   **/
-  PalletEvmCoderSubstrateError: {
-    _enum: ['OutOfGas', 'OutOfFund']
-  },
-  /**
-   * Lookup666: up_data_structs::SponsorshipState<pallet_evm::account::BasicCrossAccountIdRepr<sp_core::crypto::AccountId32>>
-   **/
-  UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: {
-    _enum: {
-      Disabled: 'Null',
-      Unconfirmed: 'PalletEvmAccountBasicCrossAccountIdRepr',
-      Confirmed: 'PalletEvmAccountBasicCrossAccountIdRepr'
-    }
-  },
-  /**
-   * Lookup667: pallet_evm_contract_helpers::SponsoringModeT
-   **/
-  PalletEvmContractHelpersSponsoringModeT: {
-    _enum: ['Disabled', 'Allowlisted', 'Generous']
-  },
-  /**
-   * Lookup673: pallet_evm_contract_helpers::pallet::Error<T>
-   **/
-  PalletEvmContractHelpersError: {
-    _enum: ['NoPermission', 'NoPendingSponsor', 'TooManyMethodsHaveSponsoredLimit']
-  },
-  /**
-   * Lookup674: pallet_evm_migration::pallet::Error<T>
-   **/
-  PalletEvmMigrationError: {
-    _enum: ['AccountNotEmpty', 'AccountIsNotMigrating', 'BadEvent']
-  },
-  /**
-   * Lookup675: pallet_maintenance::pallet::Error<T>
-   **/
-  PalletMaintenanceError: 'Null',
-  /**
-   * Lookup676: pallet_utility::pallet::Error<T>
-   **/
-  PalletUtilityError: {
-    _enum: ['TooManyCalls']
-  },
-  /**
-   * Lookup677: pallet_test_utils::pallet::Error<T>
-   **/
-  PalletTestUtilsError: {
-    _enum: ['TestPalletDisabled', 'TriggerRollback']
-  },
-  /**
-   * Lookup679: sp_runtime::MultiSignature
-   **/
-  SpRuntimeMultiSignature: {
-    _enum: {
-      Ed25519: 'SpCoreEd25519Signature',
-      Sr25519: 'SpCoreSr25519Signature',
-      Ecdsa: 'SpCoreEcdsaSignature'
-    }
-  },
-  /**
-   * Lookup680: sp_core::ed25519::Signature
-   **/
-  SpCoreEd25519Signature: '[u8;64]',
-  /**
-   * Lookup682: sp_core::sr25519::Signature
-   **/
-  SpCoreSr25519Signature: '[u8;64]',
-  /**
-   * Lookup683: sp_core::ecdsa::Signature
-   **/
-  SpCoreEcdsaSignature: '[u8;65]',
-  /**
-   * Lookup686: frame_system::extensions::check_spec_version::CheckSpecVersion<T>
-   **/
-  FrameSystemExtensionsCheckSpecVersion: 'Null',
-  /**
-   * Lookup687: frame_system::extensions::check_tx_version::CheckTxVersion<T>
-   **/
-  FrameSystemExtensionsCheckTxVersion: 'Null',
-  /**
-   * Lookup688: frame_system::extensions::check_genesis::CheckGenesis<T>
-   **/
-  FrameSystemExtensionsCheckGenesis: 'Null',
-  /**
-   * Lookup691: frame_system::extensions::check_nonce::CheckNonce<T>
-   **/
-  FrameSystemExtensionsCheckNonce: 'Compact<u32>',
-  /**
-   * Lookup692: frame_system::extensions::check_weight::CheckWeight<T>
-   **/
-  FrameSystemExtensionsCheckWeight: 'Null',
-  /**
-   * Lookup693: opal_runtime::runtime_common::maintenance::CheckMaintenance
-   **/
-  OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: 'Null',
-  /**
-   * Lookup694: opal_runtime::runtime_common::identity::DisableIdentityCalls
-   **/
-  OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: 'Null',
-  /**
-   * Lookup695: pallet_template_transaction_payment::ChargeTransactionPayment<opal_runtime::Runtime>
-   **/
-  PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact<u128>',
-  /**
-   * Lookup696: opal_runtime::Runtime
-   **/
-  OpalRuntimeRuntime: 'Null',
-  /**
-   * Lookup697: pallet_ethereum::FakeTransactionFinalizer<opal_runtime::Runtime>
-   **/
-  PalletEthereumFakeTransactionFinalizer: 'Null'
-};
deletedjs-packages/types/src/povinfo/definitions.tsdiffbeforeafterboth
--- a/js-packages/types/src/povinfo/definitions.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-type RpcParam = {
-  name: string;
-  type: string;
-  isOptional?: true;
-};
-
-const atParam = {name: 'at', type: 'Hash', isOptional: true};
-
-const fun = (description: string, params: RpcParam[], type: string) => ({
-  description,
-  params: [...params, atParam],
-  type,
-});
-
-export default {
-  types: {},
-  rpc: {
-    estimateExtrinsicPoV: fun(
-      'Estimate PoV size of encoded signed extrinsics',
-      [{name: 'encodedXt', type: 'Vec<Bytes>'}],
-      'UpPovEstimateRpcPovInfo',
-    ),
-  },
-};
deletedjs-packages/types/src/povinfo/index.tsdiffbeforeafterboth
--- a/js-packages/types/src/povinfo/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export * from './types.js';
deletedjs-packages/types/src/povinfo/types.tsdiffbeforeafterboth
--- a/js-packages/types/src/povinfo/types.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export type PHANTOM_POVINFO = 'povinfo';
deletedjs-packages/types/src/registry.tsdiffbeforeafterboth
--- a/js-packages/types/src/registry.ts
+++ /dev/null
@@ -1,370 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/types/types/registry';
-
-import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemCodeUpgradeAuthorization, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity, CumulusPalletParachainSystemUnincludedSegmentAncestor, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate, CumulusPalletParachainSystemUnincludedSegmentSegmentTracker, CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchDispatchClass, FrameSupportDispatchDispatchInfo, FrameSupportDispatchPays, FrameSupportDispatchPerDispatchClassU32, FrameSupportDispatchPerDispatchClassWeight, FrameSupportDispatchPerDispatchClassWeightsPerClass, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportPreimagesBounded, FrameSupportScheduleDispatchTime, FrameSupportTokensMiscBalanceStatus, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckTxVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls, OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance, OpalRuntimeRuntimeCommonSessionKeys, OpalRuntimeRuntimeHoldReason, OrmlTokensAccountData, OrmlTokensBalanceLock, OrmlTokensModuleCall, OrmlTokensModuleError, OrmlTokensModuleEvent, OrmlTokensReserveData, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, OrmlXtokensModuleCall, OrmlXtokensModuleError, OrmlXtokensModuleEvent, PalletAppPromotionCall, PalletAppPromotionError, PalletAppPromotionEvent, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesIdAmount, PalletBalancesReasons, PalletBalancesReserveData, PalletCollatorSelectionCall, PalletCollatorSelectionError, PalletCollatorSelectionEvent, PalletCollatorSelectionHoldReason, PalletCollectiveCall, PalletCollectiveError, PalletCollectiveEvent, PalletCollectiveRawOrigin, PalletCollectiveVotes, PalletCommonError, PalletCommonEvent, PalletConfigurationAppPromotionConfiguration, PalletConfigurationCall, PalletConfigurationError, PalletConfigurationEvent, PalletDemocracyCall, PalletDemocracyConviction, PalletDemocracyDelegations, PalletDemocracyError, PalletDemocracyEvent, PalletDemocracyMetadataOwner, PalletDemocracyReferendumInfo, PalletDemocracyReferendumStatus, PalletDemocracyTally, PalletDemocracyVoteAccountVote, PalletDemocracyVotePriorLock, PalletDemocracyVoteThreshold, PalletDemocracyVoteVoting, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCodeMetadata, PalletEvmCoderSubstrateError, PalletEvmContractHelpersCall, PalletEvmContractHelpersError, PalletEvmContractHelpersEvent, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletEvmMigrationEvent, PalletForeignAssetsAssetId, PalletForeignAssetsModuleAssetMetadata, PalletForeignAssetsModuleCall, PalletForeignAssetsModuleError, PalletForeignAssetsModuleEvent, PalletForeignAssetsNativeCurrency, PalletFungibleError, PalletGovOriginsOrigin, PalletIdentityBitFlags, PalletIdentityCall, PalletIdentityError, PalletIdentityEvent, PalletIdentityIdentityField, PalletIdentityIdentityInfo, PalletIdentityJudgement, PalletIdentityRegistrarInfo, PalletIdentityRegistration, PalletInflationCall, PalletMaintenanceCall, PalletMaintenanceError, PalletMaintenanceEvent, PalletMembershipCall, PalletMembershipError, PalletMembershipEvent, PalletNonfungibleError, PalletNonfungibleItemData, PalletPreimageCall, PalletPreimageError, PalletPreimageEvent, PalletPreimageRequestStatus, PalletRankedCollectiveCall, PalletRankedCollectiveError, PalletRankedCollectiveEvent, PalletRankedCollectiveMemberRecord, PalletRankedCollectiveTally, PalletRankedCollectiveVoteRecord, PalletReferendaCall, PalletReferendaCurve, PalletReferendaDecidingStatus, PalletReferendaDeposit, PalletReferendaError, PalletReferendaEvent, PalletReferendaReferendumInfo, PalletReferendaReferendumStatus, PalletReferendaTrackInfo, PalletRefungibleError, PalletSchedulerCall, PalletSchedulerError, PalletSchedulerEvent, PalletSchedulerScheduled, PalletSessionCall, PalletSessionError, PalletSessionEvent, PalletStateTrieMigrationCall, PalletStateTrieMigrationError, PalletStateTrieMigrationEvent, PalletStateTrieMigrationMigrationCompute, PalletStateTrieMigrationMigrationLimits, PalletStateTrieMigrationMigrationTask, PalletStateTrieMigrationProgress, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTestUtilsCall, PalletTestUtilsError, PalletTestUtilsEvent, PalletTimestampCall, PalletTransactionPaymentEvent, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUtilityCall, PalletUtilityError, PalletUtilityEvent, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PalletXcmQueryStatus, PalletXcmRemoteLockedFungibleRecord, PalletXcmVersionMigrationStage, ParachainInfoCall, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat, PolkadotPrimitivesV5AbridgedHostConfiguration, PolkadotPrimitivesV5AbridgedHrmpChannel, PolkadotPrimitivesV5PersistedValidationData, PolkadotPrimitivesV5UpgradeGoAhead, PolkadotPrimitivesV5UpgradeRestriction, PolkadotPrimitivesVstagingAsyncBackingParams, SpArithmeticArithmeticError, SpConsensusAuraSr25519AppSr25519Public, SpCoreCryptoKeyTypeId, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Public, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionValidityInvalidTransaction, SpRuntimeTransactionValidityTransactionValidityError, SpRuntimeTransactionValidityUnknownTransaction, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, SpWeightsWeightV2Weight, StagingXcmDoubleEncoded, StagingXcmV2BodyId, StagingXcmV2BodyPart, StagingXcmV2Instruction, StagingXcmV2Junction, StagingXcmV2MultiAsset, StagingXcmV2MultiLocation, StagingXcmV2MultiassetAssetId, StagingXcmV2MultiassetAssetInstance, StagingXcmV2MultiassetFungibility, StagingXcmV2MultiassetMultiAssetFilter, StagingXcmV2MultiassetMultiAssets, StagingXcmV2MultiassetWildFungibility, StagingXcmV2MultiassetWildMultiAsset, StagingXcmV2MultilocationJunctions, StagingXcmV2NetworkId, StagingXcmV2OriginKind, StagingXcmV2Response, StagingXcmV2TraitsError, StagingXcmV2WeightLimit, StagingXcmV2Xcm, StagingXcmV3Instruction, StagingXcmV3Junction, StagingXcmV3JunctionBodyId, StagingXcmV3JunctionBodyPart, StagingXcmV3JunctionNetworkId, StagingXcmV3Junctions, StagingXcmV3MaybeErrorCode, StagingXcmV3MultiAsset, StagingXcmV3MultiLocation, StagingXcmV3MultiassetAssetId, StagingXcmV3MultiassetAssetInstance, StagingXcmV3MultiassetFungibility, StagingXcmV3MultiassetMultiAssetFilter, StagingXcmV3MultiassetMultiAssets, StagingXcmV3MultiassetWildFungibility, StagingXcmV3MultiassetWildMultiAsset, StagingXcmV3PalletInfo, StagingXcmV3QueryResponseInfo, StagingXcmV3Response, StagingXcmV3TraitsError, StagingXcmV3TraitsOutcome, StagingXcmV3WeightLimit, StagingXcmV3Xcm, StagingXcmVersionedAssetId, StagingXcmVersionedMultiAsset, StagingXcmVersionedMultiAssets, StagingXcmVersionedMultiLocation, StagingXcmVersionedResponse, StagingXcmVersionedXcm, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExMultipleOwners, UpDataStructsCreateRefungibleExSingleOwner, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission,
-  UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsRpcCollectionFlags, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipStateAccountId32, UpDataStructsSponsorshipStateBasicCrossAccountIdRepr, UpDataStructsTokenChild, UpDataStructsTokenData, UpPovEstimateRpcPovInfo, UpPovEstimateRpcTrieKeyValue } from '@polkadot/types/lookup';
-
-declare module '@polkadot/types/types/registry' {
-  interface InterfaceTypes {
-    CumulusPalletDmpQueueCall: CumulusPalletDmpQueueCall;
-    CumulusPalletDmpQueueConfigData: CumulusPalletDmpQueueConfigData;
-    CumulusPalletDmpQueueError: CumulusPalletDmpQueueError;
-    CumulusPalletDmpQueueEvent: CumulusPalletDmpQueueEvent;
-    CumulusPalletDmpQueuePageIndexData: CumulusPalletDmpQueuePageIndexData;
-    CumulusPalletParachainSystemCall: CumulusPalletParachainSystemCall;
-    CumulusPalletParachainSystemCodeUpgradeAuthorization: CumulusPalletParachainSystemCodeUpgradeAuthorization;
-    CumulusPalletParachainSystemError: CumulusPalletParachainSystemError;
-    CumulusPalletParachainSystemEvent: CumulusPalletParachainSystemEvent;
-    CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot: CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot;
-    CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity;
-    CumulusPalletParachainSystemUnincludedSegmentAncestor: CumulusPalletParachainSystemUnincludedSegmentAncestor;
-    CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate: CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate;
-    CumulusPalletParachainSystemUnincludedSegmentSegmentTracker: CumulusPalletParachainSystemUnincludedSegmentSegmentTracker;
-    CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
-    CumulusPalletXcmCall: CumulusPalletXcmCall;
-    CumulusPalletXcmError: CumulusPalletXcmError;
-    CumulusPalletXcmEvent: CumulusPalletXcmEvent;
-    CumulusPalletXcmOrigin: CumulusPalletXcmOrigin;
-    CumulusPalletXcmpQueueCall: CumulusPalletXcmpQueueCall;
-    CumulusPalletXcmpQueueError: CumulusPalletXcmpQueueError;
-    CumulusPalletXcmpQueueEvent: CumulusPalletXcmpQueueEvent;
-    CumulusPalletXcmpQueueInboundChannelDetails: CumulusPalletXcmpQueueInboundChannelDetails;
-    CumulusPalletXcmpQueueInboundState: CumulusPalletXcmpQueueInboundState;
-    CumulusPalletXcmpQueueOutboundChannelDetails: CumulusPalletXcmpQueueOutboundChannelDetails;
-    CumulusPalletXcmpQueueOutboundState: CumulusPalletXcmpQueueOutboundState;
-    CumulusPalletXcmpQueueQueueConfigData: CumulusPalletXcmpQueueQueueConfigData;
-    CumulusPrimitivesParachainInherentParachainInherentData: CumulusPrimitivesParachainInherentParachainInherentData;
-    EthbloomBloom: EthbloomBloom;
-    EthereumBlock: EthereumBlock;
-    EthereumHeader: EthereumHeader;
-    EthereumLog: EthereumLog;
-    EthereumReceiptEip658ReceiptData: EthereumReceiptEip658ReceiptData;
-    EthereumReceiptReceiptV3: EthereumReceiptReceiptV3;
-    EthereumTransactionAccessListItem: EthereumTransactionAccessListItem;
-    EthereumTransactionEip1559Transaction: EthereumTransactionEip1559Transaction;
-    EthereumTransactionEip2930Transaction: EthereumTransactionEip2930Transaction;
-    EthereumTransactionLegacyTransaction: EthereumTransactionLegacyTransaction;
-    EthereumTransactionTransactionAction: EthereumTransactionTransactionAction;
-    EthereumTransactionTransactionSignature: EthereumTransactionTransactionSignature;
-    EthereumTransactionTransactionV2: EthereumTransactionTransactionV2;
-    EthereumTypesHashH64: EthereumTypesHashH64;
-    EvmCoreErrorExitError: EvmCoreErrorExitError;
-    EvmCoreErrorExitFatal: EvmCoreErrorExitFatal;
-    EvmCoreErrorExitReason: EvmCoreErrorExitReason;
-    EvmCoreErrorExitRevert: EvmCoreErrorExitRevert;
-    EvmCoreErrorExitSucceed: EvmCoreErrorExitSucceed;
-    FpRpcTransactionStatus: FpRpcTransactionStatus;
-    FrameSupportDispatchDispatchClass: FrameSupportDispatchDispatchClass;
-    FrameSupportDispatchDispatchInfo: FrameSupportDispatchDispatchInfo;
-    FrameSupportDispatchPays: FrameSupportDispatchPays;
-    FrameSupportDispatchPerDispatchClassU32: FrameSupportDispatchPerDispatchClassU32;
-    FrameSupportDispatchPerDispatchClassWeight: FrameSupportDispatchPerDispatchClassWeight;
-    FrameSupportDispatchPerDispatchClassWeightsPerClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
-    FrameSupportDispatchRawOrigin: FrameSupportDispatchRawOrigin;
-    FrameSupportPalletId: FrameSupportPalletId;
-    FrameSupportPreimagesBounded: FrameSupportPreimagesBounded;
-    FrameSupportScheduleDispatchTime: FrameSupportScheduleDispatchTime;
-    FrameSupportTokensMiscBalanceStatus: FrameSupportTokensMiscBalanceStatus;
-    FrameSystemAccountInfo: FrameSystemAccountInfo;
-    FrameSystemCall: FrameSystemCall;
-    FrameSystemError: FrameSystemError;
-    FrameSystemEvent: FrameSystemEvent;
-    FrameSystemEventRecord: FrameSystemEventRecord;
-    FrameSystemExtensionsCheckGenesis: FrameSystemExtensionsCheckGenesis;
-    FrameSystemExtensionsCheckNonce: FrameSystemExtensionsCheckNonce;
-    FrameSystemExtensionsCheckSpecVersion: FrameSystemExtensionsCheckSpecVersion;
-    FrameSystemExtensionsCheckTxVersion: FrameSystemExtensionsCheckTxVersion;
-    FrameSystemExtensionsCheckWeight: FrameSystemExtensionsCheckWeight;
-    FrameSystemLastRuntimeUpgradeInfo: FrameSystemLastRuntimeUpgradeInfo;
-    FrameSystemLimitsBlockLength: FrameSystemLimitsBlockLength;
-    FrameSystemLimitsBlockWeights: FrameSystemLimitsBlockWeights;
-    FrameSystemLimitsWeightsPerClass: FrameSystemLimitsWeightsPerClass;
-    FrameSystemPhase: FrameSystemPhase;
-    OpalRuntimeOriginCaller: OpalRuntimeOriginCaller;
-    OpalRuntimeRuntime: OpalRuntimeRuntime;
-    OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls: OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls;
-    OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance: OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance;
-    OpalRuntimeRuntimeCommonSessionKeys: OpalRuntimeRuntimeCommonSessionKeys;
-    OpalRuntimeRuntimeHoldReason: OpalRuntimeRuntimeHoldReason;
-    OrmlTokensAccountData: OrmlTokensAccountData;
-    OrmlTokensBalanceLock: OrmlTokensBalanceLock;
-    OrmlTokensModuleCall: OrmlTokensModuleCall;
-    OrmlTokensModuleError: OrmlTokensModuleError;
-    OrmlTokensModuleEvent: OrmlTokensModuleEvent;
-    OrmlTokensReserveData: OrmlTokensReserveData;
-    OrmlVestingModuleCall: OrmlVestingModuleCall;
-    OrmlVestingModuleError: OrmlVestingModuleError;
-    OrmlVestingModuleEvent: OrmlVestingModuleEvent;
-    OrmlVestingVestingSchedule: OrmlVestingVestingSchedule;
-    OrmlXtokensModuleCall: OrmlXtokensModuleCall;
-    OrmlXtokensModuleError: OrmlXtokensModuleError;
-    OrmlXtokensModuleEvent: OrmlXtokensModuleEvent;
-    PalletAppPromotionCall: PalletAppPromotionCall;
-    PalletAppPromotionError: PalletAppPromotionError;
-    PalletAppPromotionEvent: PalletAppPromotionEvent;
-    PalletBalancesAccountData: PalletBalancesAccountData;
-    PalletBalancesBalanceLock: PalletBalancesBalanceLock;
-    PalletBalancesCall: PalletBalancesCall;
-    PalletBalancesError: PalletBalancesError;
-    PalletBalancesEvent: PalletBalancesEvent;
-    PalletBalancesIdAmount: PalletBalancesIdAmount;
-    PalletBalancesReasons: PalletBalancesReasons;
-    PalletBalancesReserveData: PalletBalancesReserveData;
-    PalletCollatorSelectionCall: PalletCollatorSelectionCall;
-    PalletCollatorSelectionError: PalletCollatorSelectionError;
-    PalletCollatorSelectionEvent: PalletCollatorSelectionEvent;
-    PalletCollatorSelectionHoldReason: PalletCollatorSelectionHoldReason;
-    PalletCollectiveCall: PalletCollectiveCall;
-    PalletCollectiveError: PalletCollectiveError;
-    PalletCollectiveEvent: PalletCollectiveEvent;
-    PalletCollectiveRawOrigin: PalletCollectiveRawOrigin;
-    PalletCollectiveVotes: PalletCollectiveVotes;
-    PalletCommonError: PalletCommonError;
-    PalletCommonEvent: PalletCommonEvent;
-    PalletConfigurationAppPromotionConfiguration: PalletConfigurationAppPromotionConfiguration;
-    PalletConfigurationCall: PalletConfigurationCall;
-    PalletConfigurationError: PalletConfigurationError;
-    PalletConfigurationEvent: PalletConfigurationEvent;
-    PalletDemocracyCall: PalletDemocracyCall;
-    PalletDemocracyConviction: PalletDemocracyConviction;
-    PalletDemocracyDelegations: PalletDemocracyDelegations;
-    PalletDemocracyError: PalletDemocracyError;
-    PalletDemocracyEvent: PalletDemocracyEvent;
-    PalletDemocracyMetadataOwner: PalletDemocracyMetadataOwner;
-    PalletDemocracyReferendumInfo: PalletDemocracyReferendumInfo;
-    PalletDemocracyReferendumStatus: PalletDemocracyReferendumStatus;
-    PalletDemocracyTally: PalletDemocracyTally;
-    PalletDemocracyVoteAccountVote: PalletDemocracyVoteAccountVote;
-    PalletDemocracyVotePriorLock: PalletDemocracyVotePriorLock;
-    PalletDemocracyVoteThreshold: PalletDemocracyVoteThreshold;
-    PalletDemocracyVoteVoting: PalletDemocracyVoteVoting;
-    PalletEthereumCall: PalletEthereumCall;
-    PalletEthereumError: PalletEthereumError;
-    PalletEthereumEvent: PalletEthereumEvent;
-    PalletEthereumFakeTransactionFinalizer: PalletEthereumFakeTransactionFinalizer;
-    PalletEthereumRawOrigin: PalletEthereumRawOrigin;
-    PalletEvmAccountBasicCrossAccountIdRepr: PalletEvmAccountBasicCrossAccountIdRepr;
-    PalletEvmCall: PalletEvmCall;
-    PalletEvmCodeMetadata: PalletEvmCodeMetadata;
-    PalletEvmCoderSubstrateError: PalletEvmCoderSubstrateError;
-    PalletEvmContractHelpersCall: PalletEvmContractHelpersCall;
-    PalletEvmContractHelpersError: PalletEvmContractHelpersError;
-    PalletEvmContractHelpersEvent: PalletEvmContractHelpersEvent;
-    PalletEvmContractHelpersSponsoringModeT: PalletEvmContractHelpersSponsoringModeT;
-    PalletEvmError: PalletEvmError;
-    PalletEvmEvent: PalletEvmEvent;
-    PalletEvmMigrationCall: PalletEvmMigrationCall;
-    PalletEvmMigrationError: PalletEvmMigrationError;
-    PalletEvmMigrationEvent: PalletEvmMigrationEvent;
-    PalletForeignAssetsAssetId: PalletForeignAssetsAssetId;
-    PalletForeignAssetsModuleAssetMetadata: PalletForeignAssetsModuleAssetMetadata;
-    PalletForeignAssetsModuleCall: PalletForeignAssetsModuleCall;
-    PalletForeignAssetsModuleError: PalletForeignAssetsModuleError;
-    PalletForeignAssetsModuleEvent: PalletForeignAssetsModuleEvent;
-    PalletForeignAssetsNativeCurrency: PalletForeignAssetsNativeCurrency;
-    PalletFungibleError: PalletFungibleError;
-    PalletGovOriginsOrigin: PalletGovOriginsOrigin;
-    PalletIdentityBitFlags: PalletIdentityBitFlags;
-    PalletIdentityCall: PalletIdentityCall;
-    PalletIdentityError: PalletIdentityError;
-    PalletIdentityEvent: PalletIdentityEvent;
-    PalletIdentityIdentityField: PalletIdentityIdentityField;
-    PalletIdentityIdentityInfo: PalletIdentityIdentityInfo;
-    PalletIdentityJudgement: PalletIdentityJudgement;
-    PalletIdentityRegistrarInfo: PalletIdentityRegistrarInfo;
-    PalletIdentityRegistration: PalletIdentityRegistration;
-    PalletInflationCall: PalletInflationCall;
-    PalletMaintenanceCall: PalletMaintenanceCall;
-    PalletMaintenanceError: PalletMaintenanceError;
-    PalletMaintenanceEvent: PalletMaintenanceEvent;
-    PalletMembershipCall: PalletMembershipCall;
-    PalletMembershipError: PalletMembershipError;
-    PalletMembershipEvent: PalletMembershipEvent;
-    PalletNonfungibleError: PalletNonfungibleError;
-    PalletNonfungibleItemData: PalletNonfungibleItemData;
-    PalletPreimageCall: PalletPreimageCall;
-    PalletPreimageError: PalletPreimageError;
-    PalletPreimageEvent: PalletPreimageEvent;
-    PalletPreimageRequestStatus: PalletPreimageRequestStatus;
-    PalletRankedCollectiveCall: PalletRankedCollectiveCall;
-    PalletRankedCollectiveError: PalletRankedCollectiveError;
-    PalletRankedCollectiveEvent: PalletRankedCollectiveEvent;
-    PalletRankedCollectiveMemberRecord: PalletRankedCollectiveMemberRecord;
-    PalletRankedCollectiveTally: PalletRankedCollectiveTally;
-    PalletRankedCollectiveVoteRecord: PalletRankedCollectiveVoteRecord;
-    PalletReferendaCall: PalletReferendaCall;
-    PalletReferendaCurve: PalletReferendaCurve;
-    PalletReferendaDecidingStatus: PalletReferendaDecidingStatus;
-    PalletReferendaDeposit: PalletReferendaDeposit;
-    PalletReferendaError: PalletReferendaError;
-    PalletReferendaEvent: PalletReferendaEvent;
-    PalletReferendaReferendumInfo: PalletReferendaReferendumInfo;
-    PalletReferendaReferendumStatus: PalletReferendaReferendumStatus;
-    PalletReferendaTrackInfo: PalletReferendaTrackInfo;
-    PalletRefungibleError: PalletRefungibleError;
-    PalletSchedulerCall: PalletSchedulerCall;
-    PalletSchedulerError: PalletSchedulerError;
-    PalletSchedulerEvent: PalletSchedulerEvent;
-    PalletSchedulerScheduled: PalletSchedulerScheduled;
-    PalletSessionCall: PalletSessionCall;
-    PalletSessionError: PalletSessionError;
-    PalletSessionEvent: PalletSessionEvent;
-    PalletStateTrieMigrationCall: PalletStateTrieMigrationCall;
-    PalletStateTrieMigrationError: PalletStateTrieMigrationError;
-    PalletStateTrieMigrationEvent: PalletStateTrieMigrationEvent;
-    PalletStateTrieMigrationMigrationCompute: PalletStateTrieMigrationMigrationCompute;
-    PalletStateTrieMigrationMigrationLimits: PalletStateTrieMigrationMigrationLimits;
-    PalletStateTrieMigrationMigrationTask: PalletStateTrieMigrationMigrationTask;
-    PalletStateTrieMigrationProgress: PalletStateTrieMigrationProgress;
-    PalletStructureCall: PalletStructureCall;
-    PalletStructureError: PalletStructureError;
-    PalletStructureEvent: PalletStructureEvent;
-    PalletSudoCall: PalletSudoCall;
-    PalletSudoError: PalletSudoError;
-    PalletSudoEvent: PalletSudoEvent;
-    PalletTemplateTransactionPaymentChargeTransactionPayment: PalletTemplateTransactionPaymentChargeTransactionPayment;
-    PalletTestUtilsCall: PalletTestUtilsCall;
-    PalletTestUtilsError: PalletTestUtilsError;
-    PalletTestUtilsEvent: PalletTestUtilsEvent;
-    PalletTimestampCall: PalletTimestampCall;
-    PalletTransactionPaymentEvent: PalletTransactionPaymentEvent;
-    PalletTransactionPaymentReleases: PalletTransactionPaymentReleases;
-    PalletTreasuryCall: PalletTreasuryCall;
-    PalletTreasuryError: PalletTreasuryError;
-    PalletTreasuryEvent: PalletTreasuryEvent;
-    PalletTreasuryProposal: PalletTreasuryProposal;
-    PalletUniqueCall: PalletUniqueCall;
-    PalletUniqueError: PalletUniqueError;
-    PalletUtilityCall: PalletUtilityCall;
-    PalletUtilityError: PalletUtilityError;
-    PalletUtilityEvent: PalletUtilityEvent;
-    PalletXcmCall: PalletXcmCall;
-    PalletXcmError: PalletXcmError;
-    PalletXcmEvent: PalletXcmEvent;
-    PalletXcmOrigin: PalletXcmOrigin;
-    PalletXcmQueryStatus: PalletXcmQueryStatus;
-    PalletXcmRemoteLockedFungibleRecord: PalletXcmRemoteLockedFungibleRecord;
-    PalletXcmVersionMigrationStage: PalletXcmVersionMigrationStage;
-    ParachainInfoCall: ParachainInfoCall;
-    PhantomTypeUpDataStructs: PhantomTypeUpDataStructs;
-    PolkadotCorePrimitivesInboundDownwardMessage: PolkadotCorePrimitivesInboundDownwardMessage;
-    PolkadotCorePrimitivesInboundHrmpMessage: PolkadotCorePrimitivesInboundHrmpMessage;
-    PolkadotCorePrimitivesOutboundHrmpMessage: PolkadotCorePrimitivesOutboundHrmpMessage;
-    PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat: PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat;
-    PolkadotPrimitivesV5AbridgedHostConfiguration: PolkadotPrimitivesV5AbridgedHostConfiguration;
-    PolkadotPrimitivesV5AbridgedHrmpChannel: PolkadotPrimitivesV5AbridgedHrmpChannel;
-    PolkadotPrimitivesV5PersistedValidationData: PolkadotPrimitivesV5PersistedValidationData;
-    PolkadotPrimitivesV5UpgradeGoAhead: PolkadotPrimitivesV5UpgradeGoAhead;
-    PolkadotPrimitivesV5UpgradeRestriction: PolkadotPrimitivesV5UpgradeRestriction;
-    PolkadotPrimitivesVstagingAsyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams;
-    SpArithmeticArithmeticError: SpArithmeticArithmeticError;
-    SpConsensusAuraSr25519AppSr25519Public: SpConsensusAuraSr25519AppSr25519Public;
-    SpCoreCryptoKeyTypeId: SpCoreCryptoKeyTypeId;
-    SpCoreEcdsaSignature: SpCoreEcdsaSignature;
-    SpCoreEd25519Signature: SpCoreEd25519Signature;
-    SpCoreSr25519Public: SpCoreSr25519Public;
-    SpCoreSr25519Signature: SpCoreSr25519Signature;
-    SpCoreVoid: SpCoreVoid;
-    SpRuntimeDigest: SpRuntimeDigest;
-    SpRuntimeDigestDigestItem: SpRuntimeDigestDigestItem;
-    SpRuntimeDispatchError: SpRuntimeDispatchError;
-    SpRuntimeModuleError: SpRuntimeModuleError;
-    SpRuntimeMultiSignature: SpRuntimeMultiSignature;
-    SpRuntimeTokenError: SpRuntimeTokenError;
-    SpRuntimeTransactionValidityInvalidTransaction: SpRuntimeTransactionValidityInvalidTransaction;
-    SpRuntimeTransactionValidityTransactionValidityError: SpRuntimeTransactionValidityTransactionValidityError;
-    SpRuntimeTransactionValidityUnknownTransaction: SpRuntimeTransactionValidityUnknownTransaction;
-    SpRuntimeTransactionalError: SpRuntimeTransactionalError;
-    SpTrieStorageProof: SpTrieStorageProof;
-    SpVersionRuntimeVersion: SpVersionRuntimeVersion;
-    SpWeightsRuntimeDbWeight: SpWeightsRuntimeDbWeight;
-    SpWeightsWeightV2Weight: SpWeightsWeightV2Weight;
-    StagingXcmDoubleEncoded: StagingXcmDoubleEncoded;
-    StagingXcmV2BodyId: StagingXcmV2BodyId;
-    StagingXcmV2BodyPart: StagingXcmV2BodyPart;
-    StagingXcmV2Instruction: StagingXcmV2Instruction;
-    StagingXcmV2Junction: StagingXcmV2Junction;
-    StagingXcmV2MultiAsset: StagingXcmV2MultiAsset;
-    StagingXcmV2MultiLocation: StagingXcmV2MultiLocation;
-    StagingXcmV2MultiassetAssetId: StagingXcmV2MultiassetAssetId;
-    StagingXcmV2MultiassetAssetInstance: StagingXcmV2MultiassetAssetInstance;
-    StagingXcmV2MultiassetFungibility: StagingXcmV2MultiassetFungibility;
-    StagingXcmV2MultiassetMultiAssetFilter: StagingXcmV2MultiassetMultiAssetFilter;
-    StagingXcmV2MultiassetMultiAssets: StagingXcmV2MultiassetMultiAssets;
-    StagingXcmV2MultiassetWildFungibility: StagingXcmV2MultiassetWildFungibility;
-    StagingXcmV2MultiassetWildMultiAsset: StagingXcmV2MultiassetWildMultiAsset;
-    StagingXcmV2MultilocationJunctions: StagingXcmV2MultilocationJunctions;
-    StagingXcmV2NetworkId: StagingXcmV2NetworkId;
-    StagingXcmV2OriginKind: StagingXcmV2OriginKind;
-    StagingXcmV2Response: StagingXcmV2Response;
-    StagingXcmV2TraitsError: StagingXcmV2TraitsError;
-    StagingXcmV2WeightLimit: StagingXcmV2WeightLimit;
-    StagingXcmV2Xcm: StagingXcmV2Xcm;
-    StagingXcmV3Instruction: StagingXcmV3Instruction;
-    StagingXcmV3Junction: StagingXcmV3Junction;
-    StagingXcmV3JunctionBodyId: StagingXcmV3JunctionBodyId;
-    StagingXcmV3JunctionBodyPart: StagingXcmV3JunctionBodyPart;
-    StagingXcmV3JunctionNetworkId: StagingXcmV3JunctionNetworkId;
-    StagingXcmV3Junctions: StagingXcmV3Junctions;
-    StagingXcmV3MaybeErrorCode: StagingXcmV3MaybeErrorCode;
-    StagingXcmV3MultiAsset: StagingXcmV3MultiAsset;
-    StagingXcmV3MultiLocation: StagingXcmV3MultiLocation;
-    StagingXcmV3MultiassetAssetId: StagingXcmV3MultiassetAssetId;
-    StagingXcmV3MultiassetAssetInstance: StagingXcmV3MultiassetAssetInstance;
-    StagingXcmV3MultiassetFungibility: StagingXcmV3MultiassetFungibility;
-    StagingXcmV3MultiassetMultiAssetFilter: StagingXcmV3MultiassetMultiAssetFilter;
-    StagingXcmV3MultiassetMultiAssets: StagingXcmV3MultiassetMultiAssets;
-    StagingXcmV3MultiassetWildFungibility: StagingXcmV3MultiassetWildFungibility;
-    StagingXcmV3MultiassetWildMultiAsset: StagingXcmV3MultiassetWildMultiAsset;
-    StagingXcmV3PalletInfo: StagingXcmV3PalletInfo;
-    StagingXcmV3QueryResponseInfo: StagingXcmV3QueryResponseInfo;
-    StagingXcmV3Response: StagingXcmV3Response;
-    StagingXcmV3TraitsError: StagingXcmV3TraitsError;
-    StagingXcmV3TraitsOutcome: StagingXcmV3TraitsOutcome;
-    StagingXcmV3WeightLimit: StagingXcmV3WeightLimit;
-    StagingXcmV3Xcm: StagingXcmV3Xcm;
-    StagingXcmVersionedAssetId: StagingXcmVersionedAssetId;
-    StagingXcmVersionedMultiAsset: StagingXcmVersionedMultiAsset;
-    StagingXcmVersionedMultiAssets: StagingXcmVersionedMultiAssets;
-    StagingXcmVersionedMultiLocation: StagingXcmVersionedMultiLocation;
-    StagingXcmVersionedResponse: StagingXcmVersionedResponse;
-    StagingXcmVersionedXcm: StagingXcmVersionedXcm;
-    UpDataStructsAccessMode: UpDataStructsAccessMode;
-    UpDataStructsCollection: UpDataStructsCollection;
-    UpDataStructsCollectionLimits: UpDataStructsCollectionLimits;
-    UpDataStructsCollectionMode: UpDataStructsCollectionMode;
-    UpDataStructsCollectionPermissions: UpDataStructsCollectionPermissions;
-    UpDataStructsCollectionStats: UpDataStructsCollectionStats;
-    UpDataStructsCreateCollectionData: UpDataStructsCreateCollectionData;
-    UpDataStructsCreateFungibleData: UpDataStructsCreateFungibleData;
-    UpDataStructsCreateItemData: UpDataStructsCreateItemData;
-    UpDataStructsCreateItemExData: UpDataStructsCreateItemExData;
-    UpDataStructsCreateNftData: UpDataStructsCreateNftData;
-    UpDataStructsCreateNftExData: UpDataStructsCreateNftExData;
-    UpDataStructsCreateReFungibleData: UpDataStructsCreateReFungibleData;
-    UpDataStructsCreateRefungibleExMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners;
-    UpDataStructsCreateRefungibleExSingleOwner: UpDataStructsCreateRefungibleExSingleOwner;
-    UpDataStructsNestingPermissions: UpDataStructsNestingPermissions;
-    UpDataStructsOwnerRestrictedSet: UpDataStructsOwnerRestrictedSet;
-    UpDataStructsProperties: UpDataStructsProperties;
-    UpDataStructsPropertiesMapBoundedVec: UpDataStructsPropertiesMapBoundedVec;
-    UpDataStructsPropertiesMapPropertyPermission: UpDataStructsPropertiesMapPropertyPermission;
-    UpDataStructsProperty: UpDataStructsProperty;
-    UpDataStructsPropertyKeyPermission: UpDataStructsPropertyKeyPermission;
-    UpDataStructsPropertyPermission: UpDataStructsPropertyPermission;
-    UpDataStructsPropertyScope: UpDataStructsPropertyScope;
-    UpDataStructsRpcCollection: UpDataStructsRpcCollection;
-    UpDataStructsRpcCollectionFlags: UpDataStructsRpcCollectionFlags;
-    UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit;
-    UpDataStructsSponsorshipStateAccountId32: UpDataStructsSponsorshipStateAccountId32;
-    UpDataStructsSponsorshipStateBasicCrossAccountIdRepr: UpDataStructsSponsorshipStateBasicCrossAccountIdRepr;
-    UpDataStructsTokenChild: UpDataStructsTokenChild;
-    UpDataStructsTokenData: UpDataStructsTokenData;
-    UpPovEstimateRpcPovInfo: UpPovEstimateRpcPovInfo;
-    UpPovEstimateRpcTrieKeyValue: UpPovEstimateRpcTrieKeyValue;
-  } // InterfaceTypes
-} // declare module
deletedjs-packages/types/src/types-lookup.tsdiffbeforeafterboth
--- a/js-packages/types/src/types-lookup.ts
+++ /dev/null
@@ -1,5540 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-// import type lookup before we augment - in some environments
-// this is required to allow for ambient/previous definitions
-import '@polkadot/types/lookup';
-
-import type { Data } from '@polkadot/types';
-import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Set, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
-import type { ITuple } from '@polkadot/types-codec/types';
-import type { Vote } from '@polkadot/types/interfaces/elections';
-import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime';
-import type { Event } from '@polkadot/types/interfaces/system';
-
-declare module '@polkadot/types/lookup' {
-  /** @name FrameSystemAccountInfo (3) */
-  interface FrameSystemAccountInfo extends Struct {
-    readonly nonce: u32;
-    readonly consumers: u32;
-    readonly providers: u32;
-    readonly sufficients: u32;
-    readonly data: PalletBalancesAccountData;
-  }
-
-  /** @name PalletBalancesAccountData (5) */
-  interface PalletBalancesAccountData extends Struct {
-    readonly free: u128;
-    readonly reserved: u128;
-    readonly frozen: u128;
-    readonly flags: u128;
-  }
-
-  /** @name FrameSupportDispatchPerDispatchClassWeight (8) */
-  interface FrameSupportDispatchPerDispatchClassWeight extends Struct {
-    readonly normal: SpWeightsWeightV2Weight;
-    readonly operational: SpWeightsWeightV2Weight;
-    readonly mandatory: SpWeightsWeightV2Weight;
-  }
-
-  /** @name SpWeightsWeightV2Weight (9) */
-  interface SpWeightsWeightV2Weight extends Struct {
-    readonly refTime: Compact<u64>;
-    readonly proofSize: Compact<u64>;
-  }
-
-  /** @name SpRuntimeDigest (14) */
-  interface SpRuntimeDigest extends Struct {
-    readonly logs: Vec<SpRuntimeDigestDigestItem>;
-  }
-
-  /** @name SpRuntimeDigestDigestItem (16) */
-  interface SpRuntimeDigestDigestItem extends Enum {
-    readonly isOther: boolean;
-    readonly asOther: Bytes;
-    readonly isConsensus: boolean;
-    readonly asConsensus: ITuple<[U8aFixed, Bytes]>;
-    readonly isSeal: boolean;
-    readonly asSeal: ITuple<[U8aFixed, Bytes]>;
-    readonly isPreRuntime: boolean;
-    readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>;
-    readonly isRuntimeEnvironmentUpdated: boolean;
-    readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated';
-  }
-
-  /** @name FrameSystemEventRecord (19) */
-  interface FrameSystemEventRecord extends Struct {
-    readonly phase: FrameSystemPhase;
-    readonly event: Event;
-    readonly topics: Vec<H256>;
-  }
-
-  /** @name FrameSystemEvent (21) */
-  interface FrameSystemEvent extends Enum {
-    readonly isExtrinsicSuccess: boolean;
-    readonly asExtrinsicSuccess: {
-      readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
-    } & Struct;
-    readonly isExtrinsicFailed: boolean;
-    readonly asExtrinsicFailed: {
-      readonly dispatchError: SpRuntimeDispatchError;
-      readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
-    } & Struct;
-    readonly isCodeUpdated: boolean;
-    readonly isNewAccount: boolean;
-    readonly asNewAccount: {
-      readonly account: AccountId32;
-    } & Struct;
-    readonly isKilledAccount: boolean;
-    readonly asKilledAccount: {
-      readonly account: AccountId32;
-    } & Struct;
-    readonly isRemarked: boolean;
-    readonly asRemarked: {
-      readonly sender: AccountId32;
-      readonly hash_: H256;
-    } & Struct;
-    readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked';
-  }
-
-  /** @name FrameSupportDispatchDispatchInfo (22) */
-  interface FrameSupportDispatchDispatchInfo extends Struct {
-    readonly weight: SpWeightsWeightV2Weight;
-    readonly class: FrameSupportDispatchDispatchClass;
-    readonly paysFee: FrameSupportDispatchPays;
-  }
-
-  /** @name FrameSupportDispatchDispatchClass (23) */
-  interface FrameSupportDispatchDispatchClass extends Enum {
-    readonly isNormal: boolean;
-    readonly isOperational: boolean;
-    readonly isMandatory: boolean;
-    readonly type: 'Normal' | 'Operational' | 'Mandatory';
-  }
-
-  /** @name FrameSupportDispatchPays (24) */
-  interface FrameSupportDispatchPays extends Enum {
-    readonly isYes: boolean;
-    readonly isNo: boolean;
-    readonly type: 'Yes' | 'No';
-  }
-
-  /** @name SpRuntimeDispatchError (25) */
-  interface SpRuntimeDispatchError extends Enum {
-    readonly isOther: boolean;
-    readonly isCannotLookup: boolean;
-    readonly isBadOrigin: boolean;
-    readonly isModule: boolean;
-    readonly asModule: SpRuntimeModuleError;
-    readonly isConsumerRemaining: boolean;
-    readonly isNoProviders: boolean;
-    readonly isTooManyConsumers: boolean;
-    readonly isToken: boolean;
-    readonly asToken: SpRuntimeTokenError;
-    readonly isArithmetic: boolean;
-    readonly asArithmetic: SpArithmeticArithmeticError;
-    readonly isTransactional: boolean;
-    readonly asTransactional: SpRuntimeTransactionalError;
-    readonly isExhausted: boolean;
-    readonly isCorruption: boolean;
-    readonly isUnavailable: boolean;
-    readonly isRootNotAllowed: boolean;
-    readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed';
-  }
-
-  /** @name SpRuntimeModuleError (26) */
-  interface SpRuntimeModuleError extends Struct {
-    readonly index: u8;
-    readonly error: U8aFixed;
-  }
-
-  /** @name SpRuntimeTokenError (27) */
-  interface SpRuntimeTokenError extends Enum {
-    readonly isFundsUnavailable: boolean;
-    readonly isOnlyProvider: boolean;
-    readonly isBelowMinimum: boolean;
-    readonly isCannotCreate: boolean;
-    readonly isUnknownAsset: boolean;
-    readonly isFrozen: boolean;
-    readonly isUnsupported: boolean;
-    readonly isCannotCreateHold: boolean;
-    readonly isNotExpendable: boolean;
-    readonly isBlocked: boolean;
-    readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked';
-  }
-
-  /** @name SpArithmeticArithmeticError (28) */
-  interface SpArithmeticArithmeticError extends Enum {
-    readonly isUnderflow: boolean;
-    readonly isOverflow: boolean;
-    readonly isDivisionByZero: boolean;
-    readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero';
-  }
-
-  /** @name SpRuntimeTransactionalError (29) */
-  interface SpRuntimeTransactionalError extends Enum {
-    readonly isLimitReached: boolean;
-    readonly isNoLayer: boolean;
-    readonly type: 'LimitReached' | 'NoLayer';
-  }
-
-  /** @name PalletStateTrieMigrationEvent (30) */
-  interface PalletStateTrieMigrationEvent extends Enum {
-    readonly isMigrated: boolean;
-    readonly asMigrated: {
-      readonly top: u32;
-      readonly child: u32;
-      readonly compute: PalletStateTrieMigrationMigrationCompute;
-    } & Struct;
-    readonly isSlashed: boolean;
-    readonly asSlashed: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isAutoMigrationFinished: boolean;
-    readonly isHalted: boolean;
-    readonly asHalted: {
-      readonly error: PalletStateTrieMigrationError;
-    } & Struct;
-    readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted';
-  }
-
-  /** @name PalletStateTrieMigrationMigrationCompute (31) */
-  interface PalletStateTrieMigrationMigrationCompute extends Enum {
-    readonly isSigned: boolean;
-    readonly isAuto: boolean;
-    readonly type: 'Signed' | 'Auto';
-  }
-
-  /** @name PalletStateTrieMigrationError (32) */
-  interface PalletStateTrieMigrationError extends Enum {
-    readonly isMaxSignedLimits: boolean;
-    readonly isKeyTooLong: boolean;
-    readonly isNotEnoughFunds: boolean;
-    readonly isBadWitness: boolean;
-    readonly isSignedMigrationNotAllowed: boolean;
-    readonly isBadChildRoot: boolean;
-    readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot';
-  }
-
-  /** @name CumulusPalletParachainSystemEvent (33) */
-  interface CumulusPalletParachainSystemEvent extends Enum {
-    readonly isValidationFunctionStored: boolean;
-    readonly isValidationFunctionApplied: boolean;
-    readonly asValidationFunctionApplied: {
-      readonly relayChainBlockNum: u32;
-    } & Struct;
-    readonly isValidationFunctionDiscarded: boolean;
-    readonly isUpgradeAuthorized: boolean;
-    readonly asUpgradeAuthorized: {
-      readonly codeHash: H256;
-    } & Struct;
-    readonly isDownwardMessagesReceived: boolean;
-    readonly asDownwardMessagesReceived: {
-      readonly count: u32;
-    } & Struct;
-    readonly isDownwardMessagesProcessed: boolean;
-    readonly asDownwardMessagesProcessed: {
-      readonly weightUsed: SpWeightsWeightV2Weight;
-      readonly dmqHead: H256;
-    } & Struct;
-    readonly isUpwardMessageSent: boolean;
-    readonly asUpwardMessageSent: {
-      readonly messageHash: Option<U8aFixed>;
-    } & Struct;
-    readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent';
-  }
-
-  /** @name PalletCollatorSelectionEvent (35) */
-  interface PalletCollatorSelectionEvent extends Enum {
-    readonly isInvulnerableAdded: boolean;
-    readonly asInvulnerableAdded: {
-      readonly invulnerable: AccountId32;
-    } & Struct;
-    readonly isInvulnerableRemoved: boolean;
-    readonly asInvulnerableRemoved: {
-      readonly invulnerable: AccountId32;
-    } & Struct;
-    readonly isLicenseObtained: boolean;
-    readonly asLicenseObtained: {
-      readonly accountId: AccountId32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isLicenseReleased: boolean;
-    readonly asLicenseReleased: {
-      readonly accountId: AccountId32;
-      readonly depositReturned: u128;
-    } & Struct;
-    readonly isCandidateAdded: boolean;
-    readonly asCandidateAdded: {
-      readonly accountId: AccountId32;
-    } & Struct;
-    readonly isCandidateRemoved: boolean;
-    readonly asCandidateRemoved: {
-      readonly accountId: AccountId32;
-    } & Struct;
-    readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved';
-  }
-
-  /** @name PalletSessionEvent (36) */
-  interface PalletSessionEvent extends Enum {
-    readonly isNewSession: boolean;
-    readonly asNewSession: {
-      readonly sessionIndex: u32;
-    } & Struct;
-    readonly type: 'NewSession';
-  }
-
-  /** @name PalletBalancesEvent (37) */
-  interface PalletBalancesEvent extends Enum {
-    readonly isEndowed: boolean;
-    readonly asEndowed: {
-      readonly account: AccountId32;
-      readonly freeBalance: u128;
-    } & Struct;
-    readonly isDustLost: boolean;
-    readonly asDustLost: {
-      readonly account: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isTransfer: boolean;
-    readonly asTransfer: {
-      readonly from: AccountId32;
-      readonly to: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isBalanceSet: boolean;
-    readonly asBalanceSet: {
-      readonly who: AccountId32;
-      readonly free: u128;
-    } & Struct;
-    readonly isReserved: boolean;
-    readonly asReserved: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isUnreserved: boolean;
-    readonly asUnreserved: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isReserveRepatriated: boolean;
-    readonly asReserveRepatriated: {
-      readonly from: AccountId32;
-      readonly to: AccountId32;
-      readonly amount: u128;
-      readonly destinationStatus: FrameSupportTokensMiscBalanceStatus;
-    } & Struct;
-    readonly isDeposit: boolean;
-    readonly asDeposit: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isWithdraw: boolean;
-    readonly asWithdraw: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isSlashed: boolean;
-    readonly asSlashed: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isMinted: boolean;
-    readonly asMinted: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isBurned: boolean;
-    readonly asBurned: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isSuspended: boolean;
-    readonly asSuspended: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isRestored: boolean;
-    readonly asRestored: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isUpgraded: boolean;
-    readonly asUpgraded: {
-      readonly who: AccountId32;
-    } & Struct;
-    readonly isIssued: boolean;
-    readonly asIssued: {
-      readonly amount: u128;
-    } & Struct;
-    readonly isRescinded: boolean;
-    readonly asRescinded: {
-      readonly amount: u128;
-    } & Struct;
-    readonly isLocked: boolean;
-    readonly asLocked: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isUnlocked: boolean;
-    readonly asUnlocked: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isFrozen: boolean;
-    readonly asFrozen: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isThawed: boolean;
-    readonly asThawed: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed';
-  }
-
-  /** @name FrameSupportTokensMiscBalanceStatus (38) */
-  interface FrameSupportTokensMiscBalanceStatus extends Enum {
-    readonly isFree: boolean;
-    readonly isReserved: boolean;
-    readonly type: 'Free' | 'Reserved';
-  }
-
-  /** @name PalletTransactionPaymentEvent (39) */
-  interface PalletTransactionPaymentEvent extends Enum {
-    readonly isTransactionFeePaid: boolean;
-    readonly asTransactionFeePaid: {
-      readonly who: AccountId32;
-      readonly actualFee: u128;
-      readonly tip: u128;
-    } & Struct;
-    readonly type: 'TransactionFeePaid';
-  }
-
-  /** @name PalletTreasuryEvent (40) */
-  interface PalletTreasuryEvent extends Enum {
-    readonly isProposed: boolean;
-    readonly asProposed: {
-      readonly proposalIndex: u32;
-    } & Struct;
-    readonly isSpending: boolean;
-    readonly asSpending: {
-      readonly budgetRemaining: u128;
-    } & Struct;
-    readonly isAwarded: boolean;
-    readonly asAwarded: {
-      readonly proposalIndex: u32;
-      readonly award: u128;
-      readonly account: AccountId32;
-    } & Struct;
-    readonly isRejected: boolean;
-    readonly asRejected: {
-      readonly proposalIndex: u32;
-      readonly slashed: u128;
-    } & Struct;
-    readonly isBurnt: boolean;
-    readonly asBurnt: {
-      readonly burntFunds: u128;
-    } & Struct;
-    readonly isRollover: boolean;
-    readonly asRollover: {
-      readonly rolloverBalance: u128;
-    } & Struct;
-    readonly isDeposit: boolean;
-    readonly asDeposit: {
-      readonly value: u128;
-    } & Struct;
-    readonly isSpendApproved: boolean;
-    readonly asSpendApproved: {
-      readonly proposalIndex: u32;
-      readonly amount: u128;
-      readonly beneficiary: AccountId32;
-    } & Struct;
-    readonly isUpdatedInactive: boolean;
-    readonly asUpdatedInactive: {
-      readonly reactivated: u128;
-      readonly deactivated: u128;
-    } & Struct;
-    readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive';
-  }
-
-  /** @name PalletSudoEvent (41) */
-  interface PalletSudoEvent extends Enum {
-    readonly isSudid: boolean;
-    readonly asSudid: {
-      readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
-    } & Struct;
-    readonly isKeyChanged: boolean;
-    readonly asKeyChanged: {
-      readonly oldSudoer: Option<AccountId32>;
-    } & Struct;
-    readonly isSudoAsDone: boolean;
-    readonly asSudoAsDone: {
-      readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
-    } & Struct;
-    readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone';
-  }
-
-  /** @name OrmlVestingModuleEvent (45) */
-  interface OrmlVestingModuleEvent extends Enum {
-    readonly isVestingScheduleAdded: boolean;
-    readonly asVestingScheduleAdded: {
-      readonly from: AccountId32;
-      readonly to: AccountId32;
-      readonly vestingSchedule: OrmlVestingVestingSchedule;
-    } & Struct;
-    readonly isClaimed: boolean;
-    readonly asClaimed: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isVestingSchedulesUpdated: boolean;
-    readonly asVestingSchedulesUpdated: {
-      readonly who: AccountId32;
-    } & Struct;
-    readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated';
-  }
-
-  /** @name OrmlVestingVestingSchedule (46) */
-  interface OrmlVestingVestingSchedule extends Struct {
-    readonly start: u32;
-    readonly period: u32;
-    readonly periodCount: u32;
-    readonly perPeriod: Compact<u128>;
-  }
-
-  /** @name OrmlXtokensModuleEvent (48) */
-  interface OrmlXtokensModuleEvent extends Enum {
-    readonly isTransferredMultiAssets: boolean;
-    readonly asTransferredMultiAssets: {
-      readonly sender: AccountId32;
-      readonly assets: StagingXcmV3MultiassetMultiAssets;
-      readonly fee: StagingXcmV3MultiAsset;
-      readonly dest: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly type: 'TransferredMultiAssets';
-  }
-
-  /** @name StagingXcmV3MultiassetMultiAssets (49) */
-  interface StagingXcmV3MultiassetMultiAssets extends Vec<StagingXcmV3MultiAsset> {}
-
-  /** @name StagingXcmV3MultiAsset (51) */
-  interface StagingXcmV3MultiAsset extends Struct {
-    readonly id: StagingXcmV3MultiassetAssetId;
-    readonly fun: StagingXcmV3MultiassetFungibility;
-  }
-
-  /** @name StagingXcmV3MultiassetAssetId (52) */
-  interface StagingXcmV3MultiassetAssetId extends Enum {
-    readonly isConcrete: boolean;
-    readonly asConcrete: StagingXcmV3MultiLocation;
-    readonly isAbstract: boolean;
-    readonly asAbstract: U8aFixed;
-    readonly type: 'Concrete' | 'Abstract';
-  }
-
-  /** @name StagingXcmV3MultiLocation (53) */
-  interface StagingXcmV3MultiLocation extends Struct {
-    readonly parents: u8;
-    readonly interior: StagingXcmV3Junctions;
-  }
-
-  /** @name StagingXcmV3Junctions (54) */
-  interface StagingXcmV3Junctions extends Enum {
-    readonly isHere: boolean;
-    readonly isX1: boolean;
-    readonly asX1: StagingXcmV3Junction;
-    readonly isX2: boolean;
-    readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>;
-    readonly isX3: boolean;
-    readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-    readonly isX4: boolean;
-    readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-    readonly isX5: boolean;
-    readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-    readonly isX6: boolean;
-    readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-    readonly isX7: boolean;
-    readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-    readonly isX8: boolean;
-    readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
-    readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
-  }
-
-  /** @name StagingXcmV3Junction (55) */
-  interface StagingXcmV3Junction extends Enum {
-    readonly isParachain: boolean;
-    readonly asParachain: Compact<u32>;
-    readonly isAccountId32: boolean;
-    readonly asAccountId32: {
-      readonly network: Option<StagingXcmV3JunctionNetworkId>;
-      readonly id: U8aFixed;
-    } & Struct;
-    readonly isAccountIndex64: boolean;
-    readonly asAccountIndex64: {
-      readonly network: Option<StagingXcmV3JunctionNetworkId>;
-      readonly index: Compact<u64>;
-    } & Struct;
-    readonly isAccountKey20: boolean;
-    readonly asAccountKey20: {
-      readonly network: Option<StagingXcmV3JunctionNetworkId>;
-      readonly key: U8aFixed;
-    } & Struct;
-    readonly isPalletInstance: boolean;
-    readonly asPalletInstance: u8;
-    readonly isGeneralIndex: boolean;
-    readonly asGeneralIndex: Compact<u128>;
-    readonly isGeneralKey: boolean;
-    readonly asGeneralKey: {
-      readonly length: u8;
-      readonly data: U8aFixed;
-    } & Struct;
-    readonly isOnlyChild: boolean;
-    readonly isPlurality: boolean;
-    readonly asPlurality: {
-      readonly id: StagingXcmV3JunctionBodyId;
-      readonly part: StagingXcmV3JunctionBodyPart;
-    } & Struct;
-    readonly isGlobalConsensus: boolean;
-    readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId;
-    readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus';
-  }
-
-  /** @name StagingXcmV3JunctionNetworkId (58) */
-  interface StagingXcmV3JunctionNetworkId extends Enum {
-    readonly isByGenesis: boolean;
-    readonly asByGenesis: U8aFixed;
-    readonly isByFork: boolean;
-    readonly asByFork: {
-      readonly blockNumber: u64;
-      readonly blockHash: U8aFixed;
-    } & Struct;
-    readonly isPolkadot: boolean;
-    readonly isKusama: boolean;
-    readonly isWestend: boolean;
-    readonly isRococo: boolean;
-    readonly isWococo: boolean;
-    readonly isEthereum: boolean;
-    readonly asEthereum: {
-      readonly chainId: Compact<u64>;
-    } & Struct;
-    readonly isBitcoinCore: boolean;
-    readonly isBitcoinCash: boolean;
-    readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash';
-  }
-
-  /** @name StagingXcmV3JunctionBodyId (60) */
-  interface StagingXcmV3JunctionBodyId extends Enum {
-    readonly isUnit: boolean;
-    readonly isMoniker: boolean;
-    readonly asMoniker: U8aFixed;
-    readonly isIndex: boolean;
-    readonly asIndex: Compact<u32>;
-    readonly isExecutive: boolean;
-    readonly isTechnical: boolean;
-    readonly isLegislative: boolean;
-    readonly isJudicial: boolean;
-    readonly isDefense: boolean;
-    readonly isAdministration: boolean;
-    readonly isTreasury: boolean;
-    readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
-  }
-
-  /** @name StagingXcmV3JunctionBodyPart (61) */
-  interface StagingXcmV3JunctionBodyPart extends Enum {
-    readonly isVoice: boolean;
-    readonly isMembers: boolean;
-    readonly asMembers: {
-      readonly count: Compact<u32>;
-    } & Struct;
-    readonly isFraction: boolean;
-    readonly asFraction: {
-      readonly nom: Compact<u32>;
-      readonly denom: Compact<u32>;
-    } & Struct;
-    readonly isAtLeastProportion: boolean;
-    readonly asAtLeastProportion: {
-      readonly nom: Compact<u32>;
-      readonly denom: Compact<u32>;
-    } & Struct;
-    readonly isMoreThanProportion: boolean;
-    readonly asMoreThanProportion: {
-      readonly nom: Compact<u32>;
-      readonly denom: Compact<u32>;
-    } & Struct;
-    readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
-  }
-
-  /** @name StagingXcmV3MultiassetFungibility (62) */
-  interface StagingXcmV3MultiassetFungibility extends Enum {
-    readonly isFungible: boolean;
-    readonly asFungible: Compact<u128>;
-    readonly isNonFungible: boolean;
-    readonly asNonFungible: StagingXcmV3MultiassetAssetInstance;
-    readonly type: 'Fungible' | 'NonFungible';
-  }
-
-  /** @name StagingXcmV3MultiassetAssetInstance (63) */
-  interface StagingXcmV3MultiassetAssetInstance extends Enum {
-    readonly isUndefined: boolean;
-    readonly isIndex: boolean;
-    readonly asIndex: Compact<u128>;
-    readonly isArray4: boolean;
-    readonly asArray4: U8aFixed;
-    readonly isArray8: boolean;
-    readonly asArray8: U8aFixed;
-    readonly isArray16: boolean;
-    readonly asArray16: U8aFixed;
-    readonly isArray32: boolean;
-    readonly asArray32: U8aFixed;
-    readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32';
-  }
-
-  /** @name OrmlTokensModuleEvent (66) */
-  interface OrmlTokensModuleEvent extends Enum {
-    readonly isEndowed: boolean;
-    readonly asEndowed: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isDustLost: boolean;
-    readonly asDustLost: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isTransfer: boolean;
-    readonly asTransfer: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly from: AccountId32;
-      readonly to: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isReserved: boolean;
-    readonly asReserved: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isUnreserved: boolean;
-    readonly asUnreserved: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isReserveRepatriated: boolean;
-    readonly asReserveRepatriated: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly from: AccountId32;
-      readonly to: AccountId32;
-      readonly amount: u128;
-      readonly status: FrameSupportTokensMiscBalanceStatus;
-    } & Struct;
-    readonly isBalanceSet: boolean;
-    readonly asBalanceSet: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly free: u128;
-      readonly reserved: u128;
-    } & Struct;
-    readonly isTotalIssuanceSet: boolean;
-    readonly asTotalIssuanceSet: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: u128;
-    } & Struct;
-    readonly isWithdrawn: boolean;
-    readonly asWithdrawn: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isSlashed: boolean;
-    readonly asSlashed: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly freeAmount: u128;
-      readonly reservedAmount: u128;
-    } & Struct;
-    readonly isDeposited: boolean;
-    readonly asDeposited: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isLockSet: boolean;
-    readonly asLockSet: {
-      readonly lockId: U8aFixed;
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isLockRemoved: boolean;
-    readonly asLockRemoved: {
-      readonly lockId: U8aFixed;
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-    } & Struct;
-    readonly isLocked: boolean;
-    readonly asLocked: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isUnlocked: boolean;
-    readonly asUnlocked: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isIssued: boolean;
-    readonly asIssued: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: u128;
-    } & Struct;
-    readonly isRescinded: boolean;
-    readonly asRescinded: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: u128;
-    } & Struct;
-    readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded';
-  }
-
-  /** @name PalletForeignAssetsAssetId (67) */
-  interface PalletForeignAssetsAssetId extends Enum {
-    readonly isForeignAssetId: boolean;
-    readonly asForeignAssetId: u32;
-    readonly isNativeAssetId: boolean;
-    readonly asNativeAssetId: PalletForeignAssetsNativeCurrency;
-    readonly type: 'ForeignAssetId' | 'NativeAssetId';
-  }
-
-  /** @name PalletForeignAssetsNativeCurrency (68) */
-  interface PalletForeignAssetsNativeCurrency extends Enum {
-    readonly isHere: boolean;
-    readonly isParent: boolean;
-    readonly type: 'Here' | 'Parent';
-  }
-
-  /** @name PalletIdentityEvent (69) */
-  interface PalletIdentityEvent extends Enum {
-    readonly isIdentitySet: boolean;
-    readonly asIdentitySet: {
-      readonly who: AccountId32;
-    } & Struct;
-    readonly isIdentityCleared: boolean;
-    readonly asIdentityCleared: {
-      readonly who: AccountId32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isIdentityKilled: boolean;
-    readonly asIdentityKilled: {
-      readonly who: AccountId32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isIdentitiesInserted: boolean;
-    readonly asIdentitiesInserted: {
-      readonly amount: u32;
-    } & Struct;
-    readonly isIdentitiesRemoved: boolean;
-    readonly asIdentitiesRemoved: {
-      readonly amount: u32;
-    } & Struct;
-    readonly isJudgementRequested: boolean;
-    readonly asJudgementRequested: {
-      readonly who: AccountId32;
-      readonly registrarIndex: u32;
-    } & Struct;
-    readonly isJudgementUnrequested: boolean;
-    readonly asJudgementUnrequested: {
-      readonly who: AccountId32;
-      readonly registrarIndex: u32;
-    } & Struct;
-    readonly isJudgementGiven: boolean;
-    readonly asJudgementGiven: {
-      readonly target: AccountId32;
-      readonly registrarIndex: u32;
-    } & Struct;
-    readonly isRegistrarAdded: boolean;
-    readonly asRegistrarAdded: {
-      readonly registrarIndex: u32;
-    } & Struct;
-    readonly isSubIdentityAdded: boolean;
-    readonly asSubIdentityAdded: {
-      readonly sub: AccountId32;
-      readonly main: AccountId32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isSubIdentityRemoved: boolean;
-    readonly asSubIdentityRemoved: {
-      readonly sub: AccountId32;
-      readonly main: AccountId32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isSubIdentityRevoked: boolean;
-    readonly asSubIdentityRevoked: {
-      readonly sub: AccountId32;
-      readonly main: AccountId32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isSubIdentitiesInserted: boolean;
-    readonly asSubIdentitiesInserted: {
-      readonly amount: u32;
-    } & Struct;
-    readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted';
-  }
-
-  /** @name PalletPreimageEvent (70) */
-  interface PalletPreimageEvent extends Enum {
-    readonly isNoted: boolean;
-    readonly asNoted: {
-      readonly hash_: H256;
-    } & Struct;
-    readonly isRequested: boolean;
-    readonly asRequested: {
-      readonly hash_: H256;
-    } & Struct;
-    readonly isCleared: boolean;
-    readonly asCleared: {
-      readonly hash_: H256;
-    } & Struct;
-    readonly type: 'Noted' | 'Requested' | 'Cleared';
-  }
-
-  /** @name PalletDemocracyEvent (71) */
-  interface PalletDemocracyEvent extends Enum {
-    readonly isProposed: boolean;
-    readonly asProposed: {
-      readonly proposalIndex: u32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isTabled: boolean;
-    readonly asTabled: {
-      readonly proposalIndex: u32;
-      readonly deposit: u128;
-    } & Struct;
-    readonly isExternalTabled: boolean;
-    readonly isStarted: boolean;
-    readonly asStarted: {
-      readonly refIndex: u32;
-      readonly threshold: PalletDemocracyVoteThreshold;
-    } & Struct;
-    readonly isPassed: boolean;
-    readonly asPassed: {
-      readonly refIndex: u32;
-    } & Struct;
-    readonly isNotPassed: boolean;
-    readonly asNotPassed: {
-      readonly refIndex: u32;
-    } & Struct;
-    readonly isCancelled: boolean;
-    readonly asCancelled: {
-      readonly refIndex: u32;
-    } & Struct;
-    readonly isDelegated: boolean;
-    readonly asDelegated: {
-      readonly who: AccountId32;
-      readonly target: AccountId32;
-    } & Struct;
-    readonly isUndelegated: boolean;
-    readonly asUndelegated: {
-      readonly account: AccountId32;
-    } & Struct;
-    readonly isVetoed: boolean;
-    readonly asVetoed: {
-      readonly who: AccountId32;
-      readonly proposalHash: H256;
-      readonly until: u32;
-    } & Struct;
-    readonly isBlacklisted: boolean;
-    readonly asBlacklisted: {
-      readonly proposalHash: H256;
-    } & Struct;
-    readonly isVoted: boolean;
-    readonly asVoted: {
-      readonly voter: AccountId32;
-      readonly refIndex: u32;
-      readonly vote: PalletDemocracyVoteAccountVote;
-    } & Struct;
-    readonly isSeconded: boolean;
-    readonly asSeconded: {
-      readonly seconder: AccountId32;
-      readonly propIndex: u32;
-    } & Struct;
-    readonly isProposalCanceled: boolean;
-    readonly asProposalCanceled: {
-      readonly propIndex: u32;
-    } & Struct;
-    readonly isMetadataSet: boolean;
-    readonly asMetadataSet: {
-      readonly owner: PalletDemocracyMetadataOwner;
-      readonly hash_: H256;
-    } & Struct;
-    readonly isMetadataCleared: boolean;
-    readonly asMetadataCleared: {
-      readonly owner: PalletDemocracyMetadataOwner;
-      readonly hash_: H256;
-    } & Struct;
-    readonly isMetadataTransferred: boolean;
-    readonly asMetadataTransferred: {
-      readonly prevOwner: PalletDemocracyMetadataOwner;
-      readonly owner: PalletDemocracyMetadataOwner;
-      readonly hash_: H256;
-    } & Struct;
-    readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred';
-  }
-
-  /** @name PalletDemocracyVoteThreshold (72) */
-  interface PalletDemocracyVoteThreshold extends Enum {
-    readonly isSuperMajorityApprove: boolean;
-    readonly isSuperMajorityAgainst: boolean;
-    readonly isSimpleMajority: boolean;
-    readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority';
-  }
-
-  /** @name PalletDemocracyVoteAccountVote (73) */
-  interface PalletDemocracyVoteAccountVote extends Enum {
-    readonly isStandard: boolean;
-    readonly asStandard: {
-      readonly vote: Vote;
-      readonly balance: u128;
-    } & Struct;
-    readonly isSplit: boolean;
-    readonly asSplit: {
-      readonly aye: u128;
-      readonly nay: u128;
-    } & Struct;
-    readonly type: 'Standard' | 'Split';
-  }
-
-  /** @name PalletDemocracyMetadataOwner (75) */
-  interface PalletDemocracyMetadataOwner extends Enum {
-    readonly isExternal: boolean;
-    readonly isProposal: boolean;
-    readonly asProposal: u32;
-    readonly isReferendum: boolean;
-    readonly asReferendum: u32;
-    readonly type: 'External' | 'Proposal' | 'Referendum';
-  }
-
-  /** @name PalletCollectiveEvent (76) */
-  interface PalletCollectiveEvent extends Enum {
-    readonly isProposed: boolean;
-    readonly asProposed: {
-      readonly account: AccountId32;
-      readonly proposalIndex: u32;
-      readonly proposalHash: H256;
-      readonly threshold: u32;
-    } & Struct;
-    readonly isVoted: boolean;
-    readonly asVoted: {
-      readonly account: AccountId32;
-      readonly proposalHash: H256;
-      readonly voted: bool;
-      readonly yes: u32;
-      readonly no: u32;
-    } & Struct;
-    readonly isApproved: boolean;
-    readonly asApproved: {
-      readonly proposalHash: H256;
-    } & Struct;
-    readonly isDisapproved: boolean;
-    readonly asDisapproved: {
-      readonly proposalHash: H256;
-    } & Struct;
-    readonly isExecuted: boolean;
-    readonly asExecuted: {
-      readonly proposalHash: H256;
-      readonly result: Result<Null, SpRuntimeDispatchError>;
-    } & Struct;
-    readonly isMemberExecuted: boolean;
-    readonly asMemberExecuted: {
-      readonly proposalHash: H256;
-      readonly result: Result<Null, SpRuntimeDispatchError>;
-    } & Struct;
-    readonly isClosed: boolean;
-    readonly asClosed: {
-      readonly proposalHash: H256;
-      readonly yes: u32;
-      readonly no: u32;
-    } & Struct;
-    readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed';
-  }
-
-  /** @name PalletMembershipEvent (79) */
-  interface PalletMembershipEvent extends Enum {
-    readonly isMemberAdded: boolean;
-    readonly isMemberRemoved: boolean;
-    readonly isMembersSwapped: boolean;
-    readonly isMembersReset: boolean;
-    readonly isKeyChanged: boolean;
-    readonly isDummy: boolean;
-    readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy';
-  }
-
-  /** @name PalletRankedCollectiveEvent (81) */
-  interface PalletRankedCollectiveEvent extends Enum {
-    readonly isMemberAdded: boolean;
-    readonly asMemberAdded: {
-      readonly who: AccountId32;
-    } & Struct;
-    readonly isRankChanged: boolean;
-    readonly asRankChanged: {
-      readonly who: AccountId32;
-      readonly rank: u16;
-    } & Struct;
-    readonly isMemberRemoved: boolean;
-    readonly asMemberRemoved: {
-      readonly who: AccountId32;
-      readonly rank: u16;
-    } & Struct;
-    readonly isVoted: boolean;
-    readonly asVoted: {
-      readonly who: AccountId32;
-      readonly poll: u32;
-      readonly vote: PalletRankedCollectiveVoteRecord;
-      readonly tally: PalletRankedCollectiveTally;
-    } & Struct;
-    readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted';
-  }
-
-  /** @name PalletRankedCollectiveVoteRecord (83) */
-  interface PalletRankedCollectiveVoteRecord extends Enum {
-    readonly isAye: boolean;
-    readonly asAye: u32;
-    readonly isNay: boolean;
-    readonly asNay: u32;
-    readonly type: 'Aye' | 'Nay';
-  }
-
-  /** @name PalletRankedCollectiveTally (84) */
-  interface PalletRankedCollectiveTally extends Struct {
-    readonly bareAyes: u32;
-    readonly ayes: u32;
-    readonly nays: u32;
-  }
-
-  /** @name PalletReferendaEvent (85) */
-  interface PalletReferendaEvent extends Enum {
-    readonly isSubmitted: boolean;
-    readonly asSubmitted: {
-      readonly index: u32;
-      readonly track: u16;
-      readonly proposal: FrameSupportPreimagesBounded;
-    } & Struct;
-    readonly isDecisionDepositPlaced: boolean;
-    readonly asDecisionDepositPlaced: {
-      readonly index: u32;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isDecisionDepositRefunded: boolean;
-    readonly asDecisionDepositRefunded: {
-      readonly index: u32;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isDepositSlashed: boolean;
-    readonly asDepositSlashed: {
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isDecisionStarted: boolean;
-    readonly asDecisionStarted: {
-      readonly index: u32;
-      readonly track: u16;
-      readonly proposal: FrameSupportPreimagesBounded;
-      readonly tally: PalletRankedCollectiveTally;
-    } & Struct;
-    readonly isConfirmStarted: boolean;
-    readonly asConfirmStarted: {
-      readonly index: u32;
-    } & Struct;
-    readonly isConfirmAborted: boolean;
-    readonly asConfirmAborted: {
-      readonly index: u32;
-    } & Struct;
-    readonly isConfirmed: boolean;
-    readonly asConfirmed: {
-      readonly index: u32;
-      readonly tally: PalletRankedCollectiveTally;
-    } & Struct;
-    readonly isApproved: boolean;
-    readonly asApproved: {
-      readonly index: u32;
-    } & Struct;
-    readonly isRejected: boolean;
-    readonly asRejected: {
-      readonly index: u32;
-      readonly tally: PalletRankedCollectiveTally;
-    } & Struct;
-    readonly isTimedOut: boolean;
-    readonly asTimedOut: {
-      readonly index: u32;
-      readonly tally: PalletRankedCollectiveTally;
-    } & Struct;
-    readonly isCancelled: boolean;
-    readonly asCancelled: {
-      readonly index: u32;
-      readonly tally: PalletRankedCollectiveTally;
-    } & Struct;
-    readonly isKilled: boolean;
-    readonly asKilled: {
-      readonly index: u32;
-      readonly tally: PalletRankedCollectiveTally;
-    } & Struct;
-    readonly isSubmissionDepositRefunded: boolean;
-    readonly asSubmissionDepositRefunded: {
-      readonly index: u32;
-      readonly who: AccountId32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isMetadataSet: boolean;
-    readonly asMetadataSet: {
-      readonly index: u32;
-      readonly hash_: H256;
-    } & Struct;
-    readonly isMetadataCleared: boolean;
-    readonly asMetadataCleared: {
-      readonly index: u32;
-      readonly hash_: H256;
-    } & Struct;
-    readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared';
-  }
-
-  /** @name FrameSupportPreimagesBounded (86) */
-  interface FrameSupportPreimagesBounded extends Enum {
-    readonly isLegacy: boolean;
-    readonly asLegacy: {
-      readonly hash_: H256;
-    } & Struct;
-    readonly isInline: boolean;
-    readonly asInline: Bytes;
-    readonly isLookup: boolean;
-    readonly asLookup: {
-      readonly hash_: H256;
-      readonly len: u32;
-    } & Struct;
-    readonly type: 'Legacy' | 'Inline' | 'Lookup';
-  }
-
-  /** @name FrameSystemCall (88) */
-  interface FrameSystemCall extends Enum {
-    readonly isRemark: boolean;
-    readonly asRemark: {
-      readonly remark: Bytes;
-    } & Struct;
-    readonly isSetHeapPages: boolean;
-    readonly asSetHeapPages: {
-      readonly pages: u64;
-    } & Struct;
-    readonly isSetCode: boolean;
-    readonly asSetCode: {
-      readonly code: Bytes;
-    } & Struct;
-    readonly isSetCodeWithoutChecks: boolean;
-    readonly asSetCodeWithoutChecks: {
-      readonly code: Bytes;
-    } & Struct;
-    readonly isSetStorage: boolean;
-    readonly asSetStorage: {
-      readonly items: Vec<ITuple<[Bytes, Bytes]>>;
-    } & Struct;
-    readonly isKillStorage: boolean;
-    readonly asKillStorage: {
-      readonly keys_: Vec<Bytes>;
-    } & Struct;
-    readonly isKillPrefix: boolean;
-    readonly asKillPrefix: {
-      readonly prefix: Bytes;
-      readonly subkeys: u32;
-    } & Struct;
-    readonly isRemarkWithEvent: boolean;
-    readonly asRemarkWithEvent: {
-      readonly remark: Bytes;
-    } & Struct;
-    readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent';
-  }
-
-  /** @name PalletStateTrieMigrationCall (92) */
-  interface PalletStateTrieMigrationCall extends Enum {
-    readonly isControlAutoMigration: boolean;
-    readonly asControlAutoMigration: {
-      readonly maybeConfig: Option<PalletStateTrieMigrationMigrationLimits>;
-    } & Struct;
-    readonly isContinueMigrate: boolean;
-    readonly asContinueMigrate: {
-      readonly limits: PalletStateTrieMigrationMigrationLimits;
-      readonly realSizeUpper: u32;
-      readonly witnessTask: PalletStateTrieMigrationMigrationTask;
-    } & Struct;
-    readonly isMigrateCustomTop: boolean;
-    readonly asMigrateCustomTop: {
-      readonly keys_: Vec<Bytes>;
-      readonly witnessSize: u32;
-    } & Struct;
-    readonly isMigrateCustomChild: boolean;
-    readonly asMigrateCustomChild: {
-      readonly root: Bytes;
-      readonly childKeys: Vec<Bytes>;
-      readonly totalSize: u32;
-    } & Struct;
-    readonly isSetSignedMaxLimits: boolean;
-    readonly asSetSignedMaxLimits: {
-      readonly limits: PalletStateTrieMigrationMigrationLimits;
-    } & Struct;
-    readonly isForceSetProgress: boolean;
-    readonly asForceSetProgress: {
-      readonly progressTop: PalletStateTrieMigrationProgress;
-      readonly progressChild: PalletStateTrieMigrationProgress;
-    } & Struct;
-    readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress';
-  }
-
-  /** @name PalletStateTrieMigrationMigrationLimits (94) */
-  interface PalletStateTrieMigrationMigrationLimits extends Struct {
-    readonly size_: u32;
-    readonly item: u32;
-  }
-
-  /** @name PalletStateTrieMigrationMigrationTask (95) */
-  interface PalletStateTrieMigrationMigrationTask extends Struct {
-    readonly progressTop: PalletStateTrieMigrationProgress;
-    readonly progressChild: PalletStateTrieMigrationProgress;
-    readonly size_: u32;
-    readonly topItems: u32;
-    readonly childItems: u32;
-  }
-
-  /** @name PalletStateTrieMigrationProgress (96) */
-  interface PalletStateTrieMigrationProgress extends Enum {
-    readonly isToStart: boolean;
-    readonly isLastKey: boolean;
-    readonly asLastKey: Bytes;
-    readonly isComplete: boolean;
-    readonly type: 'ToStart' | 'LastKey' | 'Complete';
-  }
-
-  /** @name CumulusPalletParachainSystemCall (98) */
-  interface CumulusPalletParachainSystemCall extends Enum {
-    readonly isSetValidationData: boolean;
-    readonly asSetValidationData: {
-      readonly data: CumulusPrimitivesParachainInherentParachainInherentData;
-    } & Struct;
-    readonly isSudoSendUpwardMessage: boolean;
-    readonly asSudoSendUpwardMessage: {
-      readonly message: Bytes;
-    } & Struct;
-    readonly isAuthorizeUpgrade: boolean;
-    readonly asAuthorizeUpgrade: {
-      readonly codeHash: H256;
-      readonly checkVersion: bool;
-    } & Struct;
-    readonly isEnactAuthorizedUpgrade: boolean;
-    readonly asEnactAuthorizedUpgrade: {
-      readonly code: Bytes;
-    } & Struct;
-    readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade';
-  }
-
-  /** @name CumulusPrimitivesParachainInherentParachainInherentData (99) */
-  interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct {
-    readonly validationData: PolkadotPrimitivesV5PersistedValidationData;
-    readonly relayChainState: SpTrieStorageProof;
-    readonly downwardMessages: Vec<PolkadotCorePrimitivesInboundDownwardMessage>;
-    readonly horizontalMessages: BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>;
-  }
-
-  /** @name PolkadotPrimitivesV5PersistedValidationData (100) */
-  interface PolkadotPrimitivesV5PersistedValidationData extends Struct {
-    readonly parentHead: Bytes;
-    readonly relayParentNumber: u32;
-    readonly relayParentStorageRoot: H256;
-    readonly maxPovSize: u32;
-  }
-
-  /** @name SpTrieStorageProof (102) */
-  interface SpTrieStorageProof extends Struct {
-    readonly trieNodes: BTreeSet<Bytes>;
-  }
-
-  /** @name PolkadotCorePrimitivesInboundDownwardMessage (105) */
-  interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct {
-    readonly sentAt: u32;
-    readonly msg: Bytes;
-  }
-
-  /** @name PolkadotCorePrimitivesInboundHrmpMessage (109) */
-  interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct {
-    readonly sentAt: u32;
-    readonly data: Bytes;
-  }
-
-  /** @name ParachainInfoCall (112) */
-  type ParachainInfoCall = Null;
-
-  /** @name PalletCollatorSelectionCall (113) */
-  interface PalletCollatorSelectionCall extends Enum {
-    readonly isAddInvulnerable: boolean;
-    readonly asAddInvulnerable: {
-      readonly new_: AccountId32;
-    } & Struct;
-    readonly isRemoveInvulnerable: boolean;
-    readonly asRemoveInvulnerable: {
-      readonly who: AccountId32;
-    } & Struct;
-    readonly isGetLicense: boolean;
-    readonly isOnboard: boolean;
-    readonly isOffboard: boolean;
-    readonly isReleaseLicense: boolean;
-    readonly isForceReleaseLicense: boolean;
-    readonly asForceReleaseLicense: {
-      readonly who: AccountId32;
-    } & Struct;
-    readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense';
-  }
-
-  /** @name PalletSessionCall (114) */
-  interface PalletSessionCall extends Enum {
-    readonly isSetKeys: boolean;
-    readonly asSetKeys: {
-      readonly keys_: OpalRuntimeRuntimeCommonSessionKeys;
-      readonly proof: Bytes;
-    } & Struct;
-    readonly isPurgeKeys: boolean;
-    readonly type: 'SetKeys' | 'PurgeKeys';
-  }
-
-  /** @name OpalRuntimeRuntimeCommonSessionKeys (115) */
-  interface OpalRuntimeRuntimeCommonSessionKeys extends Struct {
-    readonly aura: SpConsensusAuraSr25519AppSr25519Public;
-  }
-
-  /** @name SpConsensusAuraSr25519AppSr25519Public (116) */
-  interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {}
-
-  /** @name SpCoreSr25519Public (117) */
-  interface SpCoreSr25519Public extends U8aFixed {}
-
-  /** @name PalletBalancesCall (118) */
-  interface PalletBalancesCall extends Enum {
-    readonly isTransferAllowDeath: boolean;
-    readonly asTransferAllowDeath: {
-      readonly dest: MultiAddress;
-      readonly value: Compact<u128>;
-    } & Struct;
-    readonly isSetBalanceDeprecated: boolean;
-    readonly asSetBalanceDeprecated: {
-      readonly who: MultiAddress;
-      readonly newFree: Compact<u128>;
-      readonly oldReserved: Compact<u128>;
-    } & Struct;
-    readonly isForceTransfer: boolean;
-    readonly asForceTransfer: {
-      readonly source: MultiAddress;
-      readonly dest: MultiAddress;
-      readonly value: Compact<u128>;
-    } & Struct;
-    readonly isTransferKeepAlive: boolean;
-    readonly asTransferKeepAlive: {
-      readonly dest: MultiAddress;
-      readonly value: Compact<u128>;
-    } & Struct;
-    readonly isTransferAll: boolean;
-    readonly asTransferAll: {
-      readonly dest: MultiAddress;
-      readonly keepAlive: bool;
-    } & Struct;
-    readonly isForceUnreserve: boolean;
-    readonly asForceUnreserve: {
-      readonly who: MultiAddress;
-      readonly amount: u128;
-    } & Struct;
-    readonly isUpgradeAccounts: boolean;
-    readonly asUpgradeAccounts: {
-      readonly who: Vec<AccountId32>;
-    } & Struct;
-    readonly isTransfer: boolean;
-    readonly asTransfer: {
-      readonly dest: MultiAddress;
-      readonly value: Compact<u128>;
-    } & Struct;
-    readonly isForceSetBalance: boolean;
-    readonly asForceSetBalance: {
-      readonly who: MultiAddress;
-      readonly newFree: Compact<u128>;
-    } & Struct;
-    readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance';
-  }
-
-  /** @name PalletTimestampCall (122) */
-  interface PalletTimestampCall extends Enum {
-    readonly isSet: boolean;
-    readonly asSet: {
-      readonly now: Compact<u64>;
-    } & Struct;
-    readonly type: 'Set';
-  }
-
-  /** @name PalletTreasuryCall (123) */
-  interface PalletTreasuryCall extends Enum {
-    readonly isProposeSpend: boolean;
-    readonly asProposeSpend: {
-      readonly value: Compact<u128>;
-      readonly beneficiary: MultiAddress;
-    } & Struct;
-    readonly isRejectProposal: boolean;
-    readonly asRejectProposal: {
-      readonly proposalId: Compact<u32>;
-    } & Struct;
-    readonly isApproveProposal: boolean;
-    readonly asApproveProposal: {
-      readonly proposalId: Compact<u32>;
-    } & Struct;
-    readonly isSpend: boolean;
-    readonly asSpend: {
-      readonly amount: Compact<u128>;
-      readonly beneficiary: MultiAddress;
-    } & Struct;
-    readonly isRemoveApproval: boolean;
-    readonly asRemoveApproval: {
-      readonly proposalId: Compact<u32>;
-    } & Struct;
-    readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval';
-  }
-
-  /** @name PalletSudoCall (124) */
-  interface PalletSudoCall extends Enum {
-    readonly isSudo: boolean;
-    readonly asSudo: {
-      readonly call: Call;
-    } & Struct;
-    readonly isSudoUncheckedWeight: boolean;
-    readonly asSudoUncheckedWeight: {
-      readonly call: Call;
-      readonly weight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isSetKey: boolean;
-    readonly asSetKey: {
-      readonly new_: MultiAddress;
-    } & Struct;
-    readonly isSudoAs: boolean;
-    readonly asSudoAs: {
-      readonly who: MultiAddress;
-      readonly call: Call;
-    } & Struct;
-    readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs';
-  }
-
-  /** @name OrmlVestingModuleCall (125) */
-  interface OrmlVestingModuleCall extends Enum {
-    readonly isClaim: boolean;
-    readonly isVestedTransfer: boolean;
-    readonly asVestedTransfer: {
-      readonly dest: MultiAddress;
-      readonly schedule: OrmlVestingVestingSchedule;
-    } & Struct;
-    readonly isUpdateVestingSchedules: boolean;
-    readonly asUpdateVestingSchedules: {
-      readonly who: MultiAddress;
-      readonly vestingSchedules: Vec<OrmlVestingVestingSchedule>;
-    } & Struct;
-    readonly isClaimFor: boolean;
-    readonly asClaimFor: {
-      readonly dest: MultiAddress;
-    } & Struct;
-    readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor';
-  }
-
-  /** @name OrmlXtokensModuleCall (127) */
-  interface OrmlXtokensModuleCall extends Enum {
-    readonly isTransfer: boolean;
-    readonly asTransfer: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: u128;
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly destWeightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isTransferMultiasset: boolean;
-    readonly asTransferMultiasset: {
-      readonly asset: StagingXcmVersionedMultiAsset;
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly destWeightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isTransferWithFee: boolean;
-    readonly asTransferWithFee: {
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: u128;
-      readonly fee: u128;
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly destWeightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isTransferMultiassetWithFee: boolean;
-    readonly asTransferMultiassetWithFee: {
-      readonly asset: StagingXcmVersionedMultiAsset;
-      readonly fee: StagingXcmVersionedMultiAsset;
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly destWeightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isTransferMulticurrencies: boolean;
-    readonly asTransferMulticurrencies: {
-      readonly currencies: Vec<ITuple<[PalletForeignAssetsAssetId, u128]>>;
-      readonly feeItem: u32;
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly destWeightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isTransferMultiassets: boolean;
-    readonly asTransferMultiassets: {
-      readonly assets: StagingXcmVersionedMultiAssets;
-      readonly feeItem: u32;
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly destWeightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets';
-  }
-
-  /** @name StagingXcmVersionedMultiLocation (128) */
-  interface StagingXcmVersionedMultiLocation extends Enum {
-    readonly isV2: boolean;
-    readonly asV2: StagingXcmV2MultiLocation;
-    readonly isV3: boolean;
-    readonly asV3: StagingXcmV3MultiLocation;
-    readonly type: 'V2' | 'V3';
-  }
-
-  /** @name StagingXcmV2MultiLocation (129) */
-  interface StagingXcmV2MultiLocation extends Struct {
-    readonly parents: u8;
-    readonly interior: StagingXcmV2MultilocationJunctions;
-  }
-
-  /** @name StagingXcmV2MultilocationJunctions (130) */
-  interface StagingXcmV2MultilocationJunctions extends Enum {
-    readonly isHere: boolean;
-    readonly isX1: boolean;
-    readonly asX1: StagingXcmV2Junction;
-    readonly isX2: boolean;
-    readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>;
-    readonly isX3: boolean;
-    readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-    readonly isX4: boolean;
-    readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-    readonly isX5: boolean;
-    readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-    readonly isX6: boolean;
-    readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-    readonly isX7: boolean;
-    readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-    readonly isX8: boolean;
-    readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
-    readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
-  }
-
-  /** @name StagingXcmV2Junction (131) */
-  interface StagingXcmV2Junction extends Enum {
-    readonly isParachain: boolean;
-    readonly asParachain: Compact<u32>;
-    readonly isAccountId32: boolean;
-    readonly asAccountId32: {
-      readonly network: StagingXcmV2NetworkId;
-      readonly id: U8aFixed;
-    } & Struct;
-    readonly isAccountIndex64: boolean;
-    readonly asAccountIndex64: {
-      readonly network: StagingXcmV2NetworkId;
-      readonly index: Compact<u64>;
-    } & Struct;
-    readonly isAccountKey20: boolean;
-    readonly asAccountKey20: {
-      readonly network: StagingXcmV2NetworkId;
-      readonly key: U8aFixed;
-    } & Struct;
-    readonly isPalletInstance: boolean;
-    readonly asPalletInstance: u8;
-    readonly isGeneralIndex: boolean;
-    readonly asGeneralIndex: Compact<u128>;
-    readonly isGeneralKey: boolean;
-    readonly asGeneralKey: Bytes;
-    readonly isOnlyChild: boolean;
-    readonly isPlurality: boolean;
-    readonly asPlurality: {
-      readonly id: StagingXcmV2BodyId;
-      readonly part: StagingXcmV2BodyPart;
-    } & Struct;
-    readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality';
-  }
-
-  /** @name StagingXcmV2NetworkId (132) */
-  interface StagingXcmV2NetworkId extends Enum {
-    readonly isAny: boolean;
-    readonly isNamed: boolean;
-    readonly asNamed: Bytes;
-    readonly isPolkadot: boolean;
-    readonly isKusama: boolean;
-    readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama';
-  }
-
-  /** @name StagingXcmV2BodyId (134) */
-  interface StagingXcmV2BodyId extends Enum {
-    readonly isUnit: boolean;
-    readonly isNamed: boolean;
-    readonly asNamed: Bytes;
-    readonly isIndex: boolean;
-    readonly asIndex: Compact<u32>;
-    readonly isExecutive: boolean;
-    readonly isTechnical: boolean;
-    readonly isLegislative: boolean;
-    readonly isJudicial: boolean;
-    readonly isDefense: boolean;
-    readonly isAdministration: boolean;
-    readonly isTreasury: boolean;
-    readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
-  }
-
-  /** @name StagingXcmV2BodyPart (135) */
-  interface StagingXcmV2BodyPart extends Enum {
-    readonly isVoice: boolean;
-    readonly isMembers: boolean;
-    readonly asMembers: {
-      readonly count: Compact<u32>;
-    } & Struct;
-    readonly isFraction: boolean;
-    readonly asFraction: {
-      readonly nom: Compact<u32>;
-      readonly denom: Compact<u32>;
-    } & Struct;
-    readonly isAtLeastProportion: boolean;
-    readonly asAtLeastProportion: {
-      readonly nom: Compact<u32>;
-      readonly denom: Compact<u32>;
-    } & Struct;
-    readonly isMoreThanProportion: boolean;
-    readonly asMoreThanProportion: {
-      readonly nom: Compact<u32>;
-      readonly denom: Compact<u32>;
-    } & Struct;
-    readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
-  }
-
-  /** @name StagingXcmV3WeightLimit (136) */
-  interface StagingXcmV3WeightLimit extends Enum {
-    readonly isUnlimited: boolean;
-    readonly isLimited: boolean;
-    readonly asLimited: SpWeightsWeightV2Weight;
-    readonly type: 'Unlimited' | 'Limited';
-  }
-
-  /** @name StagingXcmVersionedMultiAsset (137) */
-  interface StagingXcmVersionedMultiAsset extends Enum {
-    readonly isV2: boolean;
-    readonly asV2: StagingXcmV2MultiAsset;
-    readonly isV3: boolean;
-    readonly asV3: StagingXcmV3MultiAsset;
-    readonly type: 'V2' | 'V3';
-  }
-
-  /** @name StagingXcmV2MultiAsset (138) */
-  interface StagingXcmV2MultiAsset extends Struct {
-    readonly id: StagingXcmV2MultiassetAssetId;
-    readonly fun: StagingXcmV2MultiassetFungibility;
-  }
-
-  /** @name StagingXcmV2MultiassetAssetId (139) */
-  interface StagingXcmV2MultiassetAssetId extends Enum {
-    readonly isConcrete: boolean;
-    readonly asConcrete: StagingXcmV2MultiLocation;
-    readonly isAbstract: boolean;
-    readonly asAbstract: Bytes;
-    readonly type: 'Concrete' | 'Abstract';
-  }
-
-  /** @name StagingXcmV2MultiassetFungibility (140) */
-  interface StagingXcmV2MultiassetFungibility extends Enum {
-    readonly isFungible: boolean;
-    readonly asFungible: Compact<u128>;
-    readonly isNonFungible: boolean;
-    readonly asNonFungible: StagingXcmV2MultiassetAssetInstance;
-    readonly type: 'Fungible' | 'NonFungible';
-  }
-
-  /** @name StagingXcmV2MultiassetAssetInstance (141) */
-  interface StagingXcmV2MultiassetAssetInstance extends Enum {
-    readonly isUndefined: boolean;
-    readonly isIndex: boolean;
-    readonly asIndex: Compact<u128>;
-    readonly isArray4: boolean;
-    readonly asArray4: U8aFixed;
-    readonly isArray8: boolean;
-    readonly asArray8: U8aFixed;
-    readonly isArray16: boolean;
-    readonly asArray16: U8aFixed;
-    readonly isArray32: boolean;
-    readonly asArray32: U8aFixed;
-    readonly isBlob: boolean;
-    readonly asBlob: Bytes;
-    readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob';
-  }
-
-  /** @name StagingXcmVersionedMultiAssets (144) */
-  interface StagingXcmVersionedMultiAssets extends Enum {
-    readonly isV2: boolean;
-    readonly asV2: StagingXcmV2MultiassetMultiAssets;
-    readonly isV3: boolean;
-    readonly asV3: StagingXcmV3MultiassetMultiAssets;
-    readonly type: 'V2' | 'V3';
-  }
-
-  /** @name StagingXcmV2MultiassetMultiAssets (145) */
-  interface StagingXcmV2MultiassetMultiAssets extends Vec<StagingXcmV2MultiAsset> {}
-
-  /** @name OrmlTokensModuleCall (147) */
-  interface OrmlTokensModuleCall extends Enum {
-    readonly isTransfer: boolean;
-    readonly asTransfer: {
-      readonly dest: MultiAddress;
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: Compact<u128>;
-    } & Struct;
-    readonly isTransferAll: boolean;
-    readonly asTransferAll: {
-      readonly dest: MultiAddress;
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly keepAlive: bool;
-    } & Struct;
-    readonly isTransferKeepAlive: boolean;
-    readonly asTransferKeepAlive: {
-      readonly dest: MultiAddress;
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: Compact<u128>;
-    } & Struct;
-    readonly isForceTransfer: boolean;
-    readonly asForceTransfer: {
-      readonly source: MultiAddress;
-      readonly dest: MultiAddress;
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly amount: Compact<u128>;
-    } & Struct;
-    readonly isSetBalance: boolean;
-    readonly asSetBalance: {
-      readonly who: MultiAddress;
-      readonly currencyId: PalletForeignAssetsAssetId;
-      readonly newFree: Compact<u128>;
-      readonly newReserved: Compact<u128>;
-    } & Struct;
-    readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance';
-  }
-
-  /** @name PalletIdentityCall (148) */
-  interface PalletIdentityCall extends Enum {
-    readonly isAddRegistrar: boolean;
-    readonly asAddRegistrar: {
-      readonly account: MultiAddress;
-    } & Struct;
-    readonly isSetIdentity: boolean;
-    readonly asSetIdentity: {
-      readonly info: PalletIdentityIdentityInfo;
-    } & Struct;
-    readonly isSetSubs: boolean;
-    readonly asSetSubs: {
-      readonly subs: Vec<ITuple<[AccountId32, Data]>>;
-    } & Struct;
-    readonly isClearIdentity: boolean;
-    readonly isRequestJudgement: boolean;
-    readonly asRequestJudgement: {
-      readonly regIndex: Compact<u32>;
-      readonly maxFee: Compact<u128>;
-    } & Struct;
-    readonly isCancelRequest: boolean;
-    readonly asCancelRequest: {
-      readonly regIndex: u32;
-    } & Struct;
-    readonly isSetFee: boolean;
-    readonly asSetFee: {
-      readonly index: Compact<u32>;
-      readonly fee: Compact<u128>;
-    } & Struct;
-    readonly isSetAccountId: boolean;
-    readonly asSetAccountId: {
-      readonly index: Compact<u32>;
-      readonly new_: MultiAddress;
-    } & Struct;
-    readonly isSetFields: boolean;
-    readonly asSetFields: {
-      readonly index: Compact<u32>;
-      readonly fields: PalletIdentityBitFlags;
-    } & Struct;
-    readonly isProvideJudgement: boolean;
-    readonly asProvideJudgement: {
-      readonly regIndex: Compact<u32>;
-      readonly target: MultiAddress;
-      readonly judgement: PalletIdentityJudgement;
-      readonly identity: H256;
-    } & Struct;
-    readonly isKillIdentity: boolean;
-    readonly asKillIdentity: {
-      readonly target: MultiAddress;
-    } & Struct;
-    readonly isAddSub: boolean;
-    readonly asAddSub: {
-      readonly sub: MultiAddress;
-      readonly data: Data;
-    } & Struct;
-    readonly isRenameSub: boolean;
-    readonly asRenameSub: {
-      readonly sub: MultiAddress;
-      readonly data: Data;
-    } & Struct;
-    readonly isRemoveSub: boolean;
-    readonly asRemoveSub: {
-      readonly sub: MultiAddress;
-    } & Struct;
-    readonly isQuitSub: boolean;
-    readonly isForceInsertIdentities: boolean;
-    readonly asForceInsertIdentities: {
-      readonly identities: Vec<ITuple<[AccountId32, PalletIdentityRegistration]>>;
-    } & Struct;
-    readonly isForceRemoveIdentities: boolean;
-    readonly asForceRemoveIdentities: {
-      readonly identities: Vec<AccountId32>;
-    } & Struct;
-    readonly isForceSetSubs: boolean;
-    readonly asForceSetSubs: {
-      readonly subs: Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>>;
-    } & Struct;
-    readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs';
-  }
-
-  /** @name PalletIdentityIdentityInfo (149) */
-  interface PalletIdentityIdentityInfo extends Struct {
-    readonly additional: Vec<ITuple<[Data, Data]>>;
-    readonly display: Data;
-    readonly legal: Data;
-    readonly web: Data;
-    readonly riot: Data;
-    readonly email: Data;
-    readonly pgpFingerprint: Option<U8aFixed>;
-    readonly image: Data;
-    readonly twitter: Data;
-  }
-
-  /** @name PalletIdentityBitFlags (185) */
-  interface PalletIdentityBitFlags extends Set {
-    readonly isDisplay: boolean;
-    readonly isLegal: boolean;
-    readonly isWeb: boolean;
-    readonly isRiot: boolean;
-    readonly isEmail: boolean;
-    readonly isPgpFingerprint: boolean;
-    readonly isImage: boolean;
-    readonly isTwitter: boolean;
-  }
-
-  /** @name PalletIdentityIdentityField (186) */
-  interface PalletIdentityIdentityField extends Enum {
-    readonly isDisplay: boolean;
-    readonly isLegal: boolean;
-    readonly isWeb: boolean;
-    readonly isRiot: boolean;
-    readonly isEmail: boolean;
-    readonly isPgpFingerprint: boolean;
-    readonly isImage: boolean;
-    readonly isTwitter: boolean;
-    readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter';
-  }
-
-  /** @name PalletIdentityJudgement (187) */
-  interface PalletIdentityJudgement extends Enum {
-    readonly isUnknown: boolean;
-    readonly isFeePaid: boolean;
-    readonly asFeePaid: u128;
-    readonly isReasonable: boolean;
-    readonly isKnownGood: boolean;
-    readonly isOutOfDate: boolean;
-    readonly isLowQuality: boolean;
-    readonly isErroneous: boolean;
-    readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous';
-  }
-
-  /** @name PalletIdentityRegistration (190) */
-  interface PalletIdentityRegistration extends Struct {
-    readonly judgements: Vec<ITuple<[u32, PalletIdentityJudgement]>>;
-    readonly deposit: u128;
-    readonly info: PalletIdentityIdentityInfo;
-  }
-
-  /** @name PalletPreimageCall (198) */
-  interface PalletPreimageCall extends Enum {
-    readonly isNotePreimage: boolean;
-    readonly asNotePreimage: {
-      readonly bytes: Bytes;
-    } & Struct;
-    readonly isUnnotePreimage: boolean;
-    readonly asUnnotePreimage: {
-      readonly hash_: H256;
-    } & Struct;
-    readonly isRequestPreimage: boolean;
-    readonly asRequestPreimage: {
-      readonly hash_: H256;
-    } & Struct;
-    readonly isUnrequestPreimage: boolean;
-    readonly asUnrequestPreimage: {
-      readonly hash_: H256;
-    } & Struct;
-    readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage';
-  }
-
-  /** @name PalletDemocracyCall (199) */
-  interface PalletDemocracyCall extends Enum {
-    readonly isPropose: boolean;
-    readonly asPropose: {
-      readonly proposal: FrameSupportPreimagesBounded;
-      readonly value: Compact<u128>;
-    } & Struct;
-    readonly isSecond: boolean;
-    readonly asSecond: {
-      readonly proposal: Compact<u32>;
-    } & Struct;
-    readonly isVote: boolean;
-    readonly asVote: {
-      readonly refIndex: Compact<u32>;
-      readonly vote: PalletDemocracyVoteAccountVote;
-    } & Struct;
-    readonly isEmergencyCancel: boolean;
-    readonly asEmergencyCancel: {
-      readonly refIndex: u32;
-    } & Struct;
-    readonly isExternalPropose: boolean;
-    readonly asExternalPropose: {
-      readonly proposal: FrameSupportPreimagesBounded;
-    } & Struct;
-    readonly isExternalProposeMajority: boolean;
-    readonly asExternalProposeMajority: {
-      readonly proposal: FrameSupportPreimagesBounded;
-    } & Struct;
-    readonly isExternalProposeDefault: boolean;
-    readonly asExternalProposeDefault: {
-      readonly proposal: FrameSupportPreimagesBounded;
-    } & Struct;
-    readonly isFastTrack: boolean;
-    readonly asFastTrack: {
-      readonly proposalHash: H256;
-      readonly votingPeriod: u32;
-      readonly delay: u32;
-    } & Struct;
-    readonly isVetoExternal: boolean;
-    readonly asVetoExternal: {
-      readonly proposalHash: H256;
-    } & Struct;
-    readonly isCancelReferendum: boolean;
-    readonly asCancelReferendum: {
-      readonly refIndex: Compact<u32>;
-    } & Struct;
-    readonly isDelegate: boolean;
-    readonly asDelegate: {
-      readonly to: MultiAddress;
-      readonly conviction: PalletDemocracyConviction;
-      readonly balance: u128;
-    } & Struct;
-    readonly isUndelegate: boolean;
-    readonly isClearPublicProposals: boolean;
-    readonly isUnlock: boolean;
-    readonly asUnlock: {
-      readonly target: MultiAddress;
-    } & Struct;
-    readonly isRemoveVote: boolean;
-    readonly asRemoveVote: {
-      readonly index: u32;
-    } & Struct;
-    readonly isRemoveOtherVote: boolean;
-    readonly asRemoveOtherVote: {
-      readonly target: MultiAddress;
-      readonly index: u32;
-    } & Struct;
-    readonly isBlacklist: boolean;
-    readonly asBlacklist: {
-      readonly proposalHash: H256;
-      readonly maybeRefIndex: Option<u32>;
-    } & Struct;
-    readonly isCancelProposal: boolean;
-    readonly asCancelProposal: {
-      readonly propIndex: Compact<u32>;
-    } & Struct;
-    readonly isSetMetadata: boolean;
-    readonly asSetMetadata: {
-      readonly owner: PalletDemocracyMetadataOwner;
-      readonly maybeHash: Option<H256>;
-    } & Struct;
-    readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata';
-  }
-
-  /** @name PalletDemocracyConviction (200) */
-  interface PalletDemocracyConviction extends Enum {
-    readonly isNone: boolean;
-    readonly isLocked1x: boolean;
-    readonly isLocked2x: boolean;
-    readonly isLocked3x: boolean;
-    readonly isLocked4x: boolean;
-    readonly isLocked5x: boolean;
-    readonly isLocked6x: boolean;
-    readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x';
-  }
-
-  /** @name PalletCollectiveCall (203) */
-  interface PalletCollectiveCall extends Enum {
-    readonly isSetMembers: boolean;
-    readonly asSetMembers: {
-      readonly newMembers: Vec<AccountId32>;
-      readonly prime: Option<AccountId32>;
-      readonly oldCount: u32;
-    } & Struct;
-    readonly isExecute: boolean;
-    readonly asExecute: {
-      readonly proposal: Call;
-      readonly lengthBound: Compact<u32>;
-    } & Struct;
-    readonly isPropose: boolean;
-    readonly asPropose: {
-      readonly threshold: Compact<u32>;
-      readonly proposal: Call;
-      readonly lengthBound: Compact<u32>;
-    } & Struct;
-    readonly isVote: boolean;
-    readonly asVote: {
-      readonly proposal: H256;
-      readonly index: Compact<u32>;
-      readonly approve: bool;
-    } & Struct;
-    readonly isDisapproveProposal: boolean;
-    readonly asDisapproveProposal: {
-      readonly proposalHash: H256;
-    } & Struct;
-    readonly isClose: boolean;
-    readonly asClose: {
-      readonly proposalHash: H256;
-      readonly index: Compact<u32>;
-      readonly proposalWeightBound: SpWeightsWeightV2Weight;
-      readonly lengthBound: Compact<u32>;
-    } & Struct;
-    readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close';
-  }
-
-  /** @name PalletMembershipCall (205) */
-  interface PalletMembershipCall extends Enum {
-    readonly isAddMember: boolean;
-    readonly asAddMember: {
-      readonly who: MultiAddress;
-    } & Struct;
-    readonly isRemoveMember: boolean;
-    readonly asRemoveMember: {
-      readonly who: MultiAddress;
-    } & Struct;
-    readonly isSwapMember: boolean;
-    readonly asSwapMember: {
-      readonly remove: MultiAddress;
-      readonly add: MultiAddress;
-    } & Struct;
-    readonly isResetMembers: boolean;
-    readonly asResetMembers: {
-      readonly members: Vec<AccountId32>;
-    } & Struct;
-    readonly isChangeKey: boolean;
-    readonly asChangeKey: {
-      readonly new_: MultiAddress;
-    } & Struct;
-    readonly isSetPrime: boolean;
-    readonly asSetPrime: {
-      readonly who: MultiAddress;
-    } & Struct;
-    readonly isClearPrime: boolean;
-    readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime';
-  }
-
-  /** @name PalletRankedCollectiveCall (207) */
-  interface PalletRankedCollectiveCall extends Enum {
-    readonly isAddMember: boolean;
-    readonly asAddMember: {
-      readonly who: MultiAddress;
-    } & Struct;
-    readonly isPromoteMember: boolean;
-    readonly asPromoteMember: {
-      readonly who: MultiAddress;
-    } & Struct;
-    readonly isDemoteMember: boolean;
-    readonly asDemoteMember: {
-      readonly who: MultiAddress;
-    } & Struct;
-    readonly isRemoveMember: boolean;
-    readonly asRemoveMember: {
-      readonly who: MultiAddress;
-      readonly minRank: u16;
-    } & Struct;
-    readonly isVote: boolean;
-    readonly asVote: {
-      readonly poll: u32;
-      readonly aye: bool;
-    } & Struct;
-    readonly isCleanupPoll: boolean;
-    readonly asCleanupPoll: {
-      readonly pollIndex: u32;
-      readonly max: u32;
-    } & Struct;
-    readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll';
-  }
-
-  /** @name PalletReferendaCall (208) */
-  interface PalletReferendaCall extends Enum {
-    readonly isSubmit: boolean;
-    readonly asSubmit: {
-      readonly proposalOrigin: OpalRuntimeOriginCaller;
-      readonly proposal: FrameSupportPreimagesBounded;
-      readonly enactmentMoment: FrameSupportScheduleDispatchTime;
-    } & Struct;
-    readonly isPlaceDecisionDeposit: boolean;
-    readonly asPlaceDecisionDeposit: {
-      readonly index: u32;
-    } & Struct;
-    readonly isRefundDecisionDeposit: boolean;
-    readonly asRefundDecisionDeposit: {
-      readonly index: u32;
-    } & Struct;
-    readonly isCancel: boolean;
-    readonly asCancel: {
-      readonly index: u32;
-    } & Struct;
-    readonly isKill: boolean;
-    readonly asKill: {
-      readonly index: u32;
-    } & Struct;
-    readonly isNudgeReferendum: boolean;
-    readonly asNudgeReferendum: {
-      readonly index: u32;
-    } & Struct;
-    readonly isOneFewerDeciding: boolean;
-    readonly asOneFewerDeciding: {
-      readonly track: u16;
-    } & Struct;
-    readonly isRefundSubmissionDeposit: boolean;
-    readonly asRefundSubmissionDeposit: {
-      readonly index: u32;
-    } & Struct;
-    readonly isSetMetadata: boolean;
-    readonly asSetMetadata: {
-      readonly index: u32;
-      readonly maybeHash: Option<H256>;
-    } & Struct;
-    readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata';
-  }
-
-  /** @name OpalRuntimeOriginCaller (209) */
-  interface OpalRuntimeOriginCaller extends Enum {
-    readonly isSystem: boolean;
-    readonly asSystem: FrameSupportDispatchRawOrigin;
-    readonly isVoid: boolean;
-    readonly isCouncil: boolean;
-    readonly asCouncil: PalletCollectiveRawOrigin;
-    readonly isTechnicalCommittee: boolean;
-    readonly asTechnicalCommittee: PalletCollectiveRawOrigin;
-    readonly isPolkadotXcm: boolean;
-    readonly asPolkadotXcm: PalletXcmOrigin;
-    readonly isCumulusXcm: boolean;
-    readonly asCumulusXcm: CumulusPalletXcmOrigin;
-    readonly isOrigins: boolean;
-    readonly asOrigins: PalletGovOriginsOrigin;
-    readonly isEthereum: boolean;
-    readonly asEthereum: PalletEthereumRawOrigin;
-    readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum';
-  }
-
-  /** @name FrameSupportDispatchRawOrigin (210) */
-  interface FrameSupportDispatchRawOrigin extends Enum {
-    readonly isRoot: boolean;
-    readonly isSigned: boolean;
-    readonly asSigned: AccountId32;
-    readonly isNone: boolean;
-    readonly type: 'Root' | 'Signed' | 'None';
-  }
-
-  /** @name PalletCollectiveRawOrigin (211) */
-  interface PalletCollectiveRawOrigin extends Enum {
-    readonly isMembers: boolean;
-    readonly asMembers: ITuple<[u32, u32]>;
-    readonly isMember: boolean;
-    readonly asMember: AccountId32;
-    readonly isPhantom: boolean;
-    readonly type: 'Members' | 'Member' | 'Phantom';
-  }
-
-  /** @name PalletGovOriginsOrigin (213) */
-  interface PalletGovOriginsOrigin extends Enum {
-    readonly isFellowshipProposition: boolean;
-    readonly type: 'FellowshipProposition';
-  }
-
-  /** @name PalletXcmOrigin (214) */
-  interface PalletXcmOrigin extends Enum {
-    readonly isXcm: boolean;
-    readonly asXcm: StagingXcmV3MultiLocation;
-    readonly isResponse: boolean;
-    readonly asResponse: StagingXcmV3MultiLocation;
-    readonly type: 'Xcm' | 'Response';
-  }
-
-  /** @name CumulusPalletXcmOrigin (215) */
-  interface CumulusPalletXcmOrigin extends Enum {
-    readonly isRelay: boolean;
-    readonly isSiblingParachain: boolean;
-    readonly asSiblingParachain: u32;
-    readonly type: 'Relay' | 'SiblingParachain';
-  }
-
-  /** @name PalletEthereumRawOrigin (216) */
-  interface PalletEthereumRawOrigin extends Enum {
-    readonly isEthereumTransaction: boolean;
-    readonly asEthereumTransaction: H160;
-    readonly type: 'EthereumTransaction';
-  }
-
-  /** @name SpCoreVoid (218) */
-  type SpCoreVoid = Null;
-
-  /** @name FrameSupportScheduleDispatchTime (219) */
-  interface FrameSupportScheduleDispatchTime extends Enum {
-    readonly isAt: boolean;
-    readonly asAt: u32;
-    readonly isAfter: boolean;
-    readonly asAfter: u32;
-    readonly type: 'At' | 'After';
-  }
-
-  /** @name PalletSchedulerCall (220) */
-  interface PalletSchedulerCall extends Enum {
-    readonly isSchedule: boolean;
-    readonly asSchedule: {
-      readonly when: u32;
-      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-      readonly priority: u8;
-      readonly call: Call;
-    } & Struct;
-    readonly isCancel: boolean;
-    readonly asCancel: {
-      readonly when: u32;
-      readonly index: u32;
-    } & Struct;
-    readonly isScheduleNamed: boolean;
-    readonly asScheduleNamed: {
-      readonly id: U8aFixed;
-      readonly when: u32;
-      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-      readonly priority: u8;
-      readonly call: Call;
-    } & Struct;
-    readonly isCancelNamed: boolean;
-    readonly asCancelNamed: {
-      readonly id: U8aFixed;
-    } & Struct;
-    readonly isScheduleAfter: boolean;
-    readonly asScheduleAfter: {
-      readonly after: u32;
-      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-      readonly priority: u8;
-      readonly call: Call;
-    } & Struct;
-    readonly isScheduleNamedAfter: boolean;
-    readonly asScheduleNamedAfter: {
-      readonly id: U8aFixed;
-      readonly after: u32;
-      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-      readonly priority: u8;
-      readonly call: Call;
-    } & Struct;
-    readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter';
-  }
-
-  /** @name CumulusPalletXcmpQueueCall (223) */
-  interface CumulusPalletXcmpQueueCall extends Enum {
-    readonly isServiceOverweight: boolean;
-    readonly asServiceOverweight: {
-      readonly index: u64;
-      readonly weightLimit: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isSuspendXcmExecution: boolean;
-    readonly isResumeXcmExecution: boolean;
-    readonly isUpdateSuspendThreshold: boolean;
-    readonly asUpdateSuspendThreshold: {
-      readonly new_: u32;
-    } & Struct;
-    readonly isUpdateDropThreshold: boolean;
-    readonly asUpdateDropThreshold: {
-      readonly new_: u32;
-    } & Struct;
-    readonly isUpdateResumeThreshold: boolean;
-    readonly asUpdateResumeThreshold: {
-      readonly new_: u32;
-    } & Struct;
-    readonly isUpdateThresholdWeight: boolean;
-    readonly asUpdateThresholdWeight: {
-      readonly new_: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isUpdateWeightRestrictDecay: boolean;
-    readonly asUpdateWeightRestrictDecay: {
-      readonly new_: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isUpdateXcmpMaxIndividualWeight: boolean;
-    readonly asUpdateXcmpMaxIndividualWeight: {
-      readonly new_: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight';
-  }
-
-  /** @name PalletXcmCall (224) */
-  interface PalletXcmCall extends Enum {
-    readonly isSend: boolean;
-    readonly asSend: {
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly message: StagingXcmVersionedXcm;
-    } & Struct;
-    readonly isTeleportAssets: boolean;
-    readonly asTeleportAssets: {
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly beneficiary: StagingXcmVersionedMultiLocation;
-      readonly assets: StagingXcmVersionedMultiAssets;
-      readonly feeAssetItem: u32;
-    } & Struct;
-    readonly isReserveTransferAssets: boolean;
-    readonly asReserveTransferAssets: {
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly beneficiary: StagingXcmVersionedMultiLocation;
-      readonly assets: StagingXcmVersionedMultiAssets;
-      readonly feeAssetItem: u32;
-    } & Struct;
-    readonly isExecute: boolean;
-    readonly asExecute: {
-      readonly message: StagingXcmVersionedXcm;
-      readonly maxWeight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isForceXcmVersion: boolean;
-    readonly asForceXcmVersion: {
-      readonly location: StagingXcmV3MultiLocation;
-      readonly version: u32;
-    } & Struct;
-    readonly isForceDefaultXcmVersion: boolean;
-    readonly asForceDefaultXcmVersion: {
-      readonly maybeXcmVersion: Option<u32>;
-    } & Struct;
-    readonly isForceSubscribeVersionNotify: boolean;
-    readonly asForceSubscribeVersionNotify: {
-      readonly location: StagingXcmVersionedMultiLocation;
-    } & Struct;
-    readonly isForceUnsubscribeVersionNotify: boolean;
-    readonly asForceUnsubscribeVersionNotify: {
-      readonly location: StagingXcmVersionedMultiLocation;
-    } & Struct;
-    readonly isLimitedReserveTransferAssets: boolean;
-    readonly asLimitedReserveTransferAssets: {
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly beneficiary: StagingXcmVersionedMultiLocation;
-      readonly assets: StagingXcmVersionedMultiAssets;
-      readonly feeAssetItem: u32;
-      readonly weightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isLimitedTeleportAssets: boolean;
-    readonly asLimitedTeleportAssets: {
-      readonly dest: StagingXcmVersionedMultiLocation;
-      readonly beneficiary: StagingXcmVersionedMultiLocation;
-      readonly assets: StagingXcmVersionedMultiAssets;
-      readonly feeAssetItem: u32;
-      readonly weightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isForceSuspension: boolean;
-    readonly asForceSuspension: {
-      readonly suspended: bool;
-    } & Struct;
-    readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension';
-  }
-
-  /** @name StagingXcmVersionedXcm (225) */
-  interface StagingXcmVersionedXcm extends Enum {
-    readonly isV2: boolean;
-    readonly asV2: StagingXcmV2Xcm;
-    readonly isV3: boolean;
-    readonly asV3: StagingXcmV3Xcm;
-    readonly type: 'V2' | 'V3';
-  }
-
-  /** @name StagingXcmV2Xcm (226) */
-  interface StagingXcmV2Xcm extends Vec<StagingXcmV2Instruction> {}
-
-  /** @name StagingXcmV2Instruction (228) */
-  interface StagingXcmV2Instruction extends Enum {
-    readonly isWithdrawAsset: boolean;
-    readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets;
-    readonly isReserveAssetDeposited: boolean;
-    readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets;
-    readonly isReceiveTeleportedAsset: boolean;
-    readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets;
-    readonly isQueryResponse: boolean;
-    readonly asQueryResponse: {
-      readonly queryId: Compact<u64>;
-      readonly response: StagingXcmV2Response;
-      readonly maxWeight: Compact<u64>;
-    } & Struct;
-    readonly isTransferAsset: boolean;
-    readonly asTransferAsset: {
-      readonly assets: StagingXcmV2MultiassetMultiAssets;
-      readonly beneficiary: StagingXcmV2MultiLocation;
-    } & Struct;
-    readonly isTransferReserveAsset: boolean;
-    readonly asTransferReserveAsset: {
-      readonly assets: StagingXcmV2MultiassetMultiAssets;
-      readonly dest: StagingXcmV2MultiLocation;
-      readonly xcm: StagingXcmV2Xcm;
-    } & Struct;
-    readonly isTransact: boolean;
-    readonly asTransact: {
-      readonly originType: StagingXcmV2OriginKind;
-      readonly requireWeightAtMost: Compact<u64>;
-      readonly call: StagingXcmDoubleEncoded;
-    } & Struct;
-    readonly isHrmpNewChannelOpenRequest: boolean;
-    readonly asHrmpNewChannelOpenRequest: {
-      readonly sender: Compact<u32>;
-      readonly maxMessageSize: Compact<u32>;
-      readonly maxCapacity: Compact<u32>;
-    } & Struct;
-    readonly isHrmpChannelAccepted: boolean;
-    readonly asHrmpChannelAccepted: {
-      readonly recipient: Compact<u32>;
-    } & Struct;
-    readonly isHrmpChannelClosing: boolean;
-    readonly asHrmpChannelClosing: {
-      readonly initiator: Compact<u32>;
-      readonly sender: Compact<u32>;
-      readonly recipient: Compact<u32>;
-    } & Struct;
-    readonly isClearOrigin: boolean;
-    readonly isDescendOrigin: boolean;
-    readonly asDescendOrigin: StagingXcmV2MultilocationJunctions;
-    readonly isReportError: boolean;
-    readonly asReportError: {
-      readonly queryId: Compact<u64>;
-      readonly dest: StagingXcmV2MultiLocation;
-      readonly maxResponseWeight: Compact<u64>;
-    } & Struct;
-    readonly isDepositAsset: boolean;
-    readonly asDepositAsset: {
-      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-      readonly maxAssets: Compact<u32>;
-      readonly beneficiary: StagingXcmV2MultiLocation;
-    } & Struct;
-    readonly isDepositReserveAsset: boolean;
-    readonly asDepositReserveAsset: {
-      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-      readonly maxAssets: Compact<u32>;
-      readonly dest: StagingXcmV2MultiLocation;
-      readonly xcm: StagingXcmV2Xcm;
-    } & Struct;
-    readonly isExchangeAsset: boolean;
-    readonly asExchangeAsset: {
-      readonly give: StagingXcmV2MultiassetMultiAssetFilter;
-      readonly receive: StagingXcmV2MultiassetMultiAssets;
-    } & Struct;
-    readonly isInitiateReserveWithdraw: boolean;
-    readonly asInitiateReserveWithdraw: {
-      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-      readonly reserve: StagingXcmV2MultiLocation;
-      readonly xcm: StagingXcmV2Xcm;
-    } & Struct;
-    readonly isInitiateTeleport: boolean;
-    readonly asInitiateTeleport: {
-      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-      readonly dest: StagingXcmV2MultiLocation;
-      readonly xcm: StagingXcmV2Xcm;
-    } & Struct;
-    readonly isQueryHolding: boolean;
-    readonly asQueryHolding: {
-      readonly queryId: Compact<u64>;
-      readonly dest: StagingXcmV2MultiLocation;
-      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
-      readonly maxResponseWeight: Compact<u64>;
-    } & Struct;
-    readonly isBuyExecution: boolean;
-    readonly asBuyExecution: {
-      readonly fees: StagingXcmV2MultiAsset;
-      readonly weightLimit: StagingXcmV2WeightLimit;
-    } & Struct;
-    readonly isRefundSurplus: boolean;
-    readonly isSetErrorHandler: boolean;
-    readonly asSetErrorHandler: StagingXcmV2Xcm;
-    readonly isSetAppendix: boolean;
-    readonly asSetAppendix: StagingXcmV2Xcm;
-    readonly isClearError: boolean;
-    readonly isClaimAsset: boolean;
-    readonly asClaimAsset: {
-      readonly assets: StagingXcmV2MultiassetMultiAssets;
-      readonly ticket: StagingXcmV2MultiLocation;
-    } & Struct;
-    readonly isTrap: boolean;
-    readonly asTrap: Compact<u64>;
-    readonly isSubscribeVersion: boolean;
-    readonly asSubscribeVersion: {
-      readonly queryId: Compact<u64>;
-      readonly maxResponseWeight: Compact<u64>;
-    } & Struct;
-    readonly isUnsubscribeVersion: boolean;
-    readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion';
-  }
-
-  /** @name StagingXcmV2Response (229) */
-  interface StagingXcmV2Response extends Enum {
-    readonly isNull: boolean;
-    readonly isAssets: boolean;
-    readonly asAssets: StagingXcmV2MultiassetMultiAssets;
-    readonly isExecutionResult: boolean;
-    readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV2TraitsError]>>;
-    readonly isVersion: boolean;
-    readonly asVersion: u32;
-    readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version';
-  }
-
-  /** @name StagingXcmV2TraitsError (232) */
-  interface StagingXcmV2TraitsError extends Enum {
-    readonly isOverflow: boolean;
-    readonly isUnimplemented: boolean;
-    readonly isUntrustedReserveLocation: boolean;
-    readonly isUntrustedTeleportLocation: boolean;
-    readonly isMultiLocationFull: boolean;
-    readonly isMultiLocationNotInvertible: boolean;
-    readonly isBadOrigin: boolean;
-    readonly isInvalidLocation: boolean;
-    readonly isAssetNotFound: boolean;
-    readonly isFailedToTransactAsset: boolean;
-    readonly isNotWithdrawable: boolean;
-    readonly isLocationCannotHold: boolean;
-    readonly isExceedsMaxMessageSize: boolean;
-    readonly isDestinationUnsupported: boolean;
-    readonly isTransport: boolean;
-    readonly isUnroutable: boolean;
-    readonly isUnknownClaim: boolean;
-    readonly isFailedToDecode: boolean;
-    readonly isMaxWeightInvalid: boolean;
-    readonly isNotHoldingFees: boolean;
-    readonly isTooExpensive: boolean;
-    readonly isTrap: boolean;
-    readonly asTrap: u64;
-    readonly isUnhandledXcmVersion: boolean;
-    readonly isWeightLimitReached: boolean;
-    readonly asWeightLimitReached: u64;
-    readonly isBarrier: boolean;
-    readonly isWeightNotComputable: boolean;
-    readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable';
-  }
-
-  /** @name StagingXcmV2OriginKind (233) */
-  interface StagingXcmV2OriginKind extends Enum {
-    readonly isNative: boolean;
-    readonly isSovereignAccount: boolean;
-    readonly isSuperuser: boolean;
-    readonly isXcm: boolean;
-    readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm';
-  }
-
-  /** @name StagingXcmDoubleEncoded (234) */
-  interface StagingXcmDoubleEncoded extends Struct {
-    readonly encoded: Bytes;
-  }
-
-  /** @name StagingXcmV2MultiassetMultiAssetFilter (235) */
-  interface StagingXcmV2MultiassetMultiAssetFilter extends Enum {
-    readonly isDefinite: boolean;
-    readonly asDefinite: StagingXcmV2MultiassetMultiAssets;
-    readonly isWild: boolean;
-    readonly asWild: StagingXcmV2MultiassetWildMultiAsset;
-    readonly type: 'Definite' | 'Wild';
-  }
-
-  /** @name StagingXcmV2MultiassetWildMultiAsset (236) */
-  interface StagingXcmV2MultiassetWildMultiAsset extends Enum {
-    readonly isAll: boolean;
-    readonly isAllOf: boolean;
-    readonly asAllOf: {
-      readonly id: StagingXcmV2MultiassetAssetId;
-      readonly fun: StagingXcmV2MultiassetWildFungibility;
-    } & Struct;
-    readonly type: 'All' | 'AllOf';
-  }
-
-  /** @name StagingXcmV2MultiassetWildFungibility (237) */
-  interface StagingXcmV2MultiassetWildFungibility extends Enum {
-    readonly isFungible: boolean;
-    readonly isNonFungible: boolean;
-    readonly type: 'Fungible' | 'NonFungible';
-  }
-
-  /** @name StagingXcmV2WeightLimit (238) */
-  interface StagingXcmV2WeightLimit extends Enum {
-    readonly isUnlimited: boolean;
-    readonly isLimited: boolean;
-    readonly asLimited: Compact<u64>;
-    readonly type: 'Unlimited' | 'Limited';
-  }
-
-  /** @name StagingXcmV3Xcm (239) */
-  interface StagingXcmV3Xcm extends Vec<StagingXcmV3Instruction> {}
-
-  /** @name StagingXcmV3Instruction (241) */
-  interface StagingXcmV3Instruction extends Enum {
-    readonly isWithdrawAsset: boolean;
-    readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets;
-    readonly isReserveAssetDeposited: boolean;
-    readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets;
-    readonly isReceiveTeleportedAsset: boolean;
-    readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets;
-    readonly isQueryResponse: boolean;
-    readonly asQueryResponse: {
-      readonly queryId: Compact<u64>;
-      readonly response: StagingXcmV3Response;
-      readonly maxWeight: SpWeightsWeightV2Weight;
-      readonly querier: Option<StagingXcmV3MultiLocation>;
-    } & Struct;
-    readonly isTransferAsset: boolean;
-    readonly asTransferAsset: {
-      readonly assets: StagingXcmV3MultiassetMultiAssets;
-      readonly beneficiary: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly isTransferReserveAsset: boolean;
-    readonly asTransferReserveAsset: {
-      readonly assets: StagingXcmV3MultiassetMultiAssets;
-      readonly dest: StagingXcmV3MultiLocation;
-      readonly xcm: StagingXcmV3Xcm;
-    } & Struct;
-    readonly isTransact: boolean;
-    readonly asTransact: {
-      readonly originKind: StagingXcmV2OriginKind;
-      readonly requireWeightAtMost: SpWeightsWeightV2Weight;
-      readonly call: StagingXcmDoubleEncoded;
-    } & Struct;
-    readonly isHrmpNewChannelOpenRequest: boolean;
-    readonly asHrmpNewChannelOpenRequest: {
-      readonly sender: Compact<u32>;
-      readonly maxMessageSize: Compact<u32>;
-      readonly maxCapacity: Compact<u32>;
-    } & Struct;
-    readonly isHrmpChannelAccepted: boolean;
-    readonly asHrmpChannelAccepted: {
-      readonly recipient: Compact<u32>;
-    } & Struct;
-    readonly isHrmpChannelClosing: boolean;
-    readonly asHrmpChannelClosing: {
-      readonly initiator: Compact<u32>;
-      readonly sender: Compact<u32>;
-      readonly recipient: Compact<u32>;
-    } & Struct;
-    readonly isClearOrigin: boolean;
-    readonly isDescendOrigin: boolean;
-    readonly asDescendOrigin: StagingXcmV3Junctions;
-    readonly isReportError: boolean;
-    readonly asReportError: StagingXcmV3QueryResponseInfo;
-    readonly isDepositAsset: boolean;
-    readonly asDepositAsset: {
-      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-      readonly beneficiary: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly isDepositReserveAsset: boolean;
-    readonly asDepositReserveAsset: {
-      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-      readonly dest: StagingXcmV3MultiLocation;
-      readonly xcm: StagingXcmV3Xcm;
-    } & Struct;
-    readonly isExchangeAsset: boolean;
-    readonly asExchangeAsset: {
-      readonly give: StagingXcmV3MultiassetMultiAssetFilter;
-      readonly want: StagingXcmV3MultiassetMultiAssets;
-      readonly maximal: bool;
-    } & Struct;
-    readonly isInitiateReserveWithdraw: boolean;
-    readonly asInitiateReserveWithdraw: {
-      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-      readonly reserve: StagingXcmV3MultiLocation;
-      readonly xcm: StagingXcmV3Xcm;
-    } & Struct;
-    readonly isInitiateTeleport: boolean;
-    readonly asInitiateTeleport: {
-      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-      readonly dest: StagingXcmV3MultiLocation;
-      readonly xcm: StagingXcmV3Xcm;
-    } & Struct;
-    readonly isReportHolding: boolean;
-    readonly asReportHolding: {
-      readonly responseInfo: StagingXcmV3QueryResponseInfo;
-      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
-    } & Struct;
-    readonly isBuyExecution: boolean;
-    readonly asBuyExecution: {
-      readonly fees: StagingXcmV3MultiAsset;
-      readonly weightLimit: StagingXcmV3WeightLimit;
-    } & Struct;
-    readonly isRefundSurplus: boolean;
-    readonly isSetErrorHandler: boolean;
-    readonly asSetErrorHandler: StagingXcmV3Xcm;
-    readonly isSetAppendix: boolean;
-    readonly asSetAppendix: StagingXcmV3Xcm;
-    readonly isClearError: boolean;
-    readonly isClaimAsset: boolean;
-    readonly asClaimAsset: {
-      readonly assets: StagingXcmV3MultiassetMultiAssets;
-      readonly ticket: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly isTrap: boolean;
-    readonly asTrap: Compact<u64>;
-    readonly isSubscribeVersion: boolean;
-    readonly asSubscribeVersion: {
-      readonly queryId: Compact<u64>;
-      readonly maxResponseWeight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isUnsubscribeVersion: boolean;
-    readonly isBurnAsset: boolean;
-    readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets;
-    readonly isExpectAsset: boolean;
-    readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets;
-    readonly isExpectOrigin: boolean;
-    readonly asExpectOrigin: Option<StagingXcmV3MultiLocation>;
-    readonly isExpectError: boolean;
-    readonly asExpectError: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
-    readonly isExpectTransactStatus: boolean;
-    readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode;
-    readonly isQueryPallet: boolean;
-    readonly asQueryPallet: {
-      readonly moduleName: Bytes;
-      readonly responseInfo: StagingXcmV3QueryResponseInfo;
-    } & Struct;
-    readonly isExpectPallet: boolean;
-    readonly asExpectPallet: {
-      readonly index: Compact<u32>;
-      readonly name: Bytes;
-      readonly moduleName: Bytes;
-      readonly crateMajor: Compact<u32>;
-      readonly minCrateMinor: Compact<u32>;
-    } & Struct;
-    readonly isReportTransactStatus: boolean;
-    readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo;
-    readonly isClearTransactStatus: boolean;
-    readonly isUniversalOrigin: boolean;
-    readonly asUniversalOrigin: StagingXcmV3Junction;
-    readonly isExportMessage: boolean;
-    readonly asExportMessage: {
-      readonly network: StagingXcmV3JunctionNetworkId;
-      readonly destination: StagingXcmV3Junctions;
-      readonly xcm: StagingXcmV3Xcm;
-    } & Struct;
-    readonly isLockAsset: boolean;
-    readonly asLockAsset: {
-      readonly asset: StagingXcmV3MultiAsset;
-      readonly unlocker: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly isUnlockAsset: boolean;
-    readonly asUnlockAsset: {
-      readonly asset: StagingXcmV3MultiAsset;
-      readonly target: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly isNoteUnlockable: boolean;
-    readonly asNoteUnlockable: {
-      readonly asset: StagingXcmV3MultiAsset;
-      readonly owner: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly isRequestUnlock: boolean;
-    readonly asRequestUnlock: {
-      readonly asset: StagingXcmV3MultiAsset;
-      readonly locker: StagingXcmV3MultiLocation;
-    } & Struct;
-    readonly isSetFeesMode: boolean;
-    readonly asSetFeesMode: {
-      readonly jitWithdraw: bool;
-    } & Struct;
-    readonly isSetTopic: boolean;
-    readonly asSetTopic: U8aFixed;
-    readonly isClearTopic: boolean;
-    readonly isAliasOrigin: boolean;
-    readonly asAliasOrigin: StagingXcmV3MultiLocation;
-    readonly isUnpaidExecution: boolean;
-    readonly asUnpaidExecution: {
-      readonly weightLimit: StagingXcmV3WeightLimit;
-      readonly checkOrigin: Option<StagingXcmV3MultiLocation>;
-    } & Struct;
-    readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution';
-  }
-
-  /** @name StagingXcmV3Response (242) */
-  interface StagingXcmV3Response extends Enum {
-    readonly isNull: boolean;
-    readonly isAssets: boolean;
-    readonly asAssets: StagingXcmV3MultiassetMultiAssets;
-    readonly isExecutionResult: boolean;
-    readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
-    readonly isVersion: boolean;
-    readonly asVersion: u32;
-    readonly isPalletsInfo: boolean;
-    readonly asPalletsInfo: Vec<StagingXcmV3PalletInfo>;
-    readonly isDispatchResult: boolean;
-    readonly asDispatchResult: StagingXcmV3MaybeErrorCode;
-    readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult';
-  }
-
-  /** @name StagingXcmV3TraitsError (245) */
-  interface StagingXcmV3TraitsError extends Enum {
-    readonly isOverflow: boolean;
-    readonly isUnimplemented: boolean;
-    readonly isUntrustedReserveLocation: boolean;
-    readonly isUntrustedTeleportLocation: boolean;
-    readonly isLocationFull: boolean;
-    readonly isLocationNotInvertible: boolean;
-    readonly isBadOrigin: boolean;
-    readonly isInvalidLocation: boolean;
-    readonly isAssetNotFound: boolean;
-    readonly isFailedToTransactAsset: boolean;
-    readonly isNotWithdrawable: boolean;
-    readonly isLocationCannotHold: boolean;
-    readonly isExceedsMaxMessageSize: boolean;
-    readonly isDestinationUnsupported: boolean;
-    readonly isTransport: boolean;
-    readonly isUnroutable: boolean;
-    readonly isUnknownClaim: boolean;
-    readonly isFailedToDecode: boolean;
-    readonly isMaxWeightInvalid: boolean;
-    readonly isNotHoldingFees: boolean;
-    readonly isTooExpensive: boolean;
-    readonly isTrap: boolean;
-    readonly asTrap: u64;
-    readonly isExpectationFalse: boolean;
-    readonly isPalletNotFound: boolean;
-    readonly isNameMismatch: boolean;
-    readonly isVersionIncompatible: boolean;
-    readonly isHoldingWouldOverflow: boolean;
-    readonly isExportError: boolean;
-    readonly isReanchorFailed: boolean;
-    readonly isNoDeal: boolean;
-    readonly isFeesNotMet: boolean;
-    readonly isLockError: boolean;
-    readonly isNoPermission: boolean;
-    readonly isUnanchored: boolean;
-    readonly isNotDepositable: boolean;
-    readonly isUnhandledXcmVersion: boolean;
-    readonly isWeightLimitReached: boolean;
-    readonly asWeightLimitReached: SpWeightsWeightV2Weight;
-    readonly isBarrier: boolean;
-    readonly isWeightNotComputable: boolean;
-    readonly isExceedsStackLimit: boolean;
-    readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit';
-  }
-
-  /** @name StagingXcmV3PalletInfo (247) */
-  interface StagingXcmV3PalletInfo extends Struct {
-    readonly index: Compact<u32>;
-    readonly name: Bytes;
-    readonly moduleName: Bytes;
-    readonly major: Compact<u32>;
-    readonly minor: Compact<u32>;
-    readonly patch: Compact<u32>;
-  }
-
-  /** @name StagingXcmV3MaybeErrorCode (250) */
-  interface StagingXcmV3MaybeErrorCode extends Enum {
-    readonly isSuccess: boolean;
-    readonly isError: boolean;
-    readonly asError: Bytes;
-    readonly isTruncatedError: boolean;
-    readonly asTruncatedError: Bytes;
-    readonly type: 'Success' | 'Error' | 'TruncatedError';
-  }
-
-  /** @name StagingXcmV3QueryResponseInfo (253) */
-  interface StagingXcmV3QueryResponseInfo extends Struct {
-    readonly destination: StagingXcmV3MultiLocation;
-    readonly queryId: Compact<u64>;
-    readonly maxWeight: SpWeightsWeightV2Weight;
-  }
-
-  /** @name StagingXcmV3MultiassetMultiAssetFilter (254) */
-  interface StagingXcmV3MultiassetMultiAssetFilter extends Enum {
-    readonly isDefinite: boolean;
-    readonly asDefinite: StagingXcmV3MultiassetMultiAssets;
-    readonly isWild: boolean;
-    readonly asWild: StagingXcmV3MultiassetWildMultiAsset;
-    readonly type: 'Definite' | 'Wild';
-  }
-
-  /** @name StagingXcmV3MultiassetWildMultiAsset (255) */
-  interface StagingXcmV3MultiassetWildMultiAsset extends Enum {
-    readonly isAll: boolean;
-    readonly isAllOf: boolean;
-    readonly asAllOf: {
-      readonly id: StagingXcmV3MultiassetAssetId;
-      readonly fun: StagingXcmV3MultiassetWildFungibility;
-    } & Struct;
-    readonly isAllCounted: boolean;
-    readonly asAllCounted: Compact<u32>;
-    readonly isAllOfCounted: boolean;
-    readonly asAllOfCounted: {
-      readonly id: StagingXcmV3MultiassetAssetId;
-      readonly fun: StagingXcmV3MultiassetWildFungibility;
-      readonly count: Compact<u32>;
-    } & Struct;
-    readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted';
-  }
-
-  /** @name StagingXcmV3MultiassetWildFungibility (256) */
-  interface StagingXcmV3MultiassetWildFungibility extends Enum {
-    readonly isFungible: boolean;
-    readonly isNonFungible: boolean;
-    readonly type: 'Fungible' | 'NonFungible';
-  }
-
-  /** @name CumulusPalletXcmCall (265) */
-  type CumulusPalletXcmCall = Null;
-
-  /** @name CumulusPalletDmpQueueCall (266) */
-  interface CumulusPalletDmpQueueCall extends Enum {
-    readonly isServiceOverweight: boolean;
-    readonly asServiceOverweight: {
-      readonly index: u64;
-      readonly weightLimit: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly type: 'ServiceOverweight';
-  }
-
-  /** @name PalletInflationCall (267) */
-  interface PalletInflationCall extends Enum {
-    readonly isStartInflation: boolean;
-    readonly asStartInflation: {
-      readonly inflationStartRelayBlock: u32;
-    } & Struct;
-    readonly type: 'StartInflation';
-  }
-
-  /** @name PalletUniqueCall (268) */
-  interface PalletUniqueCall extends Enum {
-    readonly isCreateCollection: boolean;
-    readonly asCreateCollection: {
-      readonly collectionName: Vec<u16>;
-      readonly collectionDescription: Vec<u16>;
-      readonly tokenPrefix: Bytes;
-      readonly mode: UpDataStructsCollectionMode;
-    } & Struct;
-    readonly isCreateCollectionEx: boolean;
-    readonly asCreateCollectionEx: {
-      readonly data: UpDataStructsCreateCollectionData;
-    } & Struct;
-    readonly isDestroyCollection: boolean;
-    readonly asDestroyCollection: {
-      readonly collectionId: u32;
-    } & Struct;
-    readonly isAddToAllowList: boolean;
-    readonly asAddToAllowList: {
-      readonly collectionId: u32;
-      readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
-    } & Struct;
-    readonly isRemoveFromAllowList: boolean;
-    readonly asRemoveFromAllowList: {
-      readonly collectionId: u32;
-      readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
-    } & Struct;
-    readonly isChangeCollectionOwner: boolean;
-    readonly asChangeCollectionOwner: {
-      readonly collectionId: u32;
-      readonly newOwner: AccountId32;
-    } & Struct;
-    readonly isAddCollectionAdmin: boolean;
-    readonly asAddCollectionAdmin: {
-      readonly collectionId: u32;
-      readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr;
-    } & Struct;
-    readonly isRemoveCollectionAdmin: boolean;
-    readonly asRemoveCollectionAdmin: {
-      readonly collectionId: u32;
-      readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr;
-    } & Struct;
-    readonly isSetCollectionSponsor: boolean;
-    readonly asSetCollectionSponsor: {
-      readonly collectionId: u32;
-      readonly newSponsor: AccountId32;
-    } & Struct;
-    readonly isConfirmSponsorship: boolean;
-    readonly asConfirmSponsorship: {
-      readonly collectionId: u32;
-    } & Struct;
-    readonly isRemoveCollectionSponsor: boolean;
-    readonly asRemoveCollectionSponsor: {
-      readonly collectionId: u32;
-    } & Struct;
-    readonly isCreateItem: boolean;
-    readonly asCreateItem: {
-      readonly collectionId: u32;
-      readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly data: UpDataStructsCreateItemData;
-    } & Struct;
-    readonly isCreateMultipleItems: boolean;
-    readonly asCreateMultipleItems: {
-      readonly collectionId: u32;
-      readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly itemsData: Vec<UpDataStructsCreateItemData>;
-    } & Struct;
-    readonly isSetCollectionProperties: boolean;
-    readonly asSetCollectionProperties: {
-      readonly collectionId: u32;
-      readonly properties: Vec<UpDataStructsProperty>;
-    } & Struct;
-    readonly isDeleteCollectionProperties: boolean;
-    readonly asDeleteCollectionProperties: {
-      readonly collectionId: u32;
-      readonly propertyKeys: Vec<Bytes>;
-    } & Struct;
-    readonly isSetTokenProperties: boolean;
-    readonly asSetTokenProperties: {
-      readonly collectionId: u32;
-      readonly tokenId: u32;
-      readonly properties: Vec<UpDataStructsProperty>;
-    } & Struct;
-    readonly isDeleteTokenProperties: boolean;
-    readonly asDeleteTokenProperties: {
-      readonly collectionId: u32;
-      readonly tokenId: u32;
-      readonly propertyKeys: Vec<Bytes>;
-    } & Struct;
-    readonly isSetTokenPropertyPermissions: boolean;
-    readonly asSetTokenPropertyPermissions: {
-      readonly collectionId: u32;
-      readonly propertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
-    } & Struct;
-    readonly isCreateMultipleItemsEx: boolean;
-    readonly asCreateMultipleItemsEx: {
-      readonly collectionId: u32;
-      readonly data: UpDataStructsCreateItemExData;
-    } & Struct;
-    readonly isSetTransfersEnabledFlag: boolean;
-    readonly asSetTransfersEnabledFlag: {
-      readonly collectionId: u32;
-      readonly value: bool;
-    } & Struct;
-    readonly isBurnItem: boolean;
-    readonly asBurnItem: {
-      readonly collectionId: u32;
-      readonly itemId: u32;
-      readonly value: u128;
-    } & Struct;
-    readonly isBurnFrom: boolean;
-    readonly asBurnFrom: {
-      readonly collectionId: u32;
-      readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly itemId: u32;
-      readonly value: u128;
-    } & Struct;
-    readonly isTransfer: boolean;
-    readonly asTransfer: {
-      readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly collectionId: u32;
-      readonly itemId: u32;
-      readonly value: u128;
-    } & Struct;
-    readonly isApprove: boolean;
-    readonly asApprove: {
-      readonly spender: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly collectionId: u32;
-      readonly itemId: u32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isApproveFrom: boolean;
-    readonly asApproveFrom: {
-      readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly to: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly collectionId: u32;
-      readonly itemId: u32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isTransferFrom: boolean;
-    readonly asTransferFrom: {
-      readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly collectionId: u32;
-      readonly itemId: u32;
-      readonly value: u128;
-    } & Struct;
-    readonly isSetCollectionLimits: boolean;
-    readonly asSetCollectionLimits: {
-      readonly collectionId: u32;
-      readonly newLimit: UpDataStructsCollectionLimits;
-    } & Struct;
-    readonly isSetCollectionPermissions: boolean;
-    readonly asSetCollectionPermissions: {
-      readonly collectionId: u32;
-      readonly newPermission: UpDataStructsCollectionPermissions;
-    } & Struct;
-    readonly isRepartition: boolean;
-    readonly asRepartition: {
-      readonly collectionId: u32;
-      readonly tokenId: u32;
-      readonly amount: u128;
-    } & Struct;
-    readonly isSetAllowanceForAll: boolean;
-    readonly asSetAllowanceForAll: {
-      readonly collectionId: u32;
-      readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
-      readonly approve: bool;
-    } & Struct;
-    readonly isForceRepairCollection: boolean;
-    readonly asForceRepairCollection: {
-      readonly collectionId: u32;
-    } & Struct;
-    readonly isForceRepairItem: boolean;
-    readonly asForceRepairItem: {
-      readonly collectionId: u32;
-      readonly itemId: u32;
-    } & Struct;
-    readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem';
-  }
-
-  /** @name UpDataStructsCollectionMode (273) */
-  interface UpDataStructsCollectionMode extends Enum {
-    readonly isNft: boolean;
-    readonly isFungible: boolean;
-    readonly asFungible: u8;
-    readonly isReFungible: boolean;
-    readonly type: 'Nft' | 'Fungible' | 'ReFungible';
-  }
-
-  /** @name UpDataStructsCreateCollectionData (274) */
-  interface UpDataStructsCreateCollectionData extends Struct {
-    readonly mode: UpDataStructsCollectionMode;
-    readonly access: Option<UpDataStructsAccessMode>;
-    readonly name: Vec<u16>;
-    readonly description: Vec<u16>;
-    readonly tokenPrefix: Bytes;
-    readonly limits: Option<UpDataStructsCollectionLimits>;
-    readonly permissions: Option<UpDataStructsCollectionPermissions>;
-    readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
-    readonly properties: Vec<UpDataStructsProperty>;
-    readonly adminList: Vec<PalletEvmAccountBasicCrossAccountIdRepr>;
-    readonly pendingSponsor: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
-    readonly flags: U8aFixed;
-  }
-
-  /** @name PalletEvmAccountBasicCrossAccountIdRepr (275) */
-  interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum {
-    readonly isSubstrate: boolean;
-    readonly asSubstrate: AccountId32;
-    readonly isEthereum: boolean;
-    readonly asEthereum: H160;
-    readonly type: 'Substrate' | 'Ethereum';
-  }
-
-  /** @name UpDataStructsAccessMode (277) */
-  interface UpDataStructsAccessMode extends Enum {
-    readonly isNormal: boolean;
-    readonly isAllowList: boolean;
-    readonly type: 'Normal' | 'AllowList';
-  }
-
-  /** @name UpDataStructsCollectionLimits (279) */
-  interface UpDataStructsCollectionLimits extends Struct {
-    readonly accountTokenOwnershipLimit: Option<u32>;
-    readonly sponsoredDataSize: Option<u32>;
-    readonly sponsoredDataRateLimit: Option<UpDataStructsSponsoringRateLimit>;
-    readonly tokenLimit: Option<u32>;
-    readonly sponsorTransferTimeout: Option<u32>;
-    readonly sponsorApproveTimeout: Option<u32>;
-    readonly ownerCanTransfer: Option<bool>;
-    readonly ownerCanDestroy: Option<bool>;
-    readonly transfersEnabled: Option<bool>;
-  }
-
-  /** @name UpDataStructsSponsoringRateLimit (281) */
-  interface UpDataStructsSponsoringRateLimit extends Enum {
-    readonly isSponsoringDisabled: boolean;
-    readonly isBlocks: boolean;
-    readonly asBlocks: u32;
-    readonly type: 'SponsoringDisabled' | 'Blocks';
-  }
-
-  /** @name UpDataStructsCollectionPermissions (284) */
-  interface UpDataStructsCollectionPermissions extends Struct {
-    readonly access: Option<UpDataStructsAccessMode>;
-    readonly mintMode: Option<bool>;
-    readonly nesting: Option<UpDataStructsNestingPermissions>;
-  }
-
-  /** @name UpDataStructsNestingPermissions (286) */
-  interface UpDataStructsNestingPermissions extends Struct {
-    readonly tokenOwner: bool;
-    readonly collectionAdmin: bool;
-    readonly restricted: Option<UpDataStructsOwnerRestrictedSet>;
-  }
-
-  /** @name UpDataStructsOwnerRestrictedSet (288) */
-  interface UpDataStructsOwnerRestrictedSet extends BTreeSet<u32> {}
-
-  /** @name UpDataStructsPropertyKeyPermission (294) */
-  interface UpDataStructsPropertyKeyPermission extends Struct {
-    readonly key: Bytes;
-    readonly permission: UpDataStructsPropertyPermission;
-  }
-
-  /** @name UpDataStructsPropertyPermission (296) */
-  interface UpDataStructsPropertyPermission extends Struct {
-    readonly mutable: bool;
-    readonly collectionAdmin: bool;
-    readonly tokenOwner: bool;
-  }
-
-  /** @name UpDataStructsProperty (299) */
-  interface UpDataStructsProperty extends Struct {
-    readonly key: Bytes;
-    readonly value: Bytes;
-  }
-
-  /** @name UpDataStructsCreateItemData (304) */
-  interface UpDataStructsCreateItemData extends Enum {
-    readonly isNft: boolean;
-    readonly asNft: UpDataStructsCreateNftData;
-    readonly isFungible: boolean;
-    readonly asFungible: UpDataStructsCreateFungibleData;
-    readonly isReFungible: boolean;
-    readonly asReFungible: UpDataStructsCreateReFungibleData;
-    readonly type: 'Nft' | 'Fungible' | 'ReFungible';
-  }
-
-  /** @name UpDataStructsCreateNftData (305) */
-  interface UpDataStructsCreateNftData extends Struct {
-    readonly properties: Vec<UpDataStructsProperty>;
-  }
-
-  /** @name UpDataStructsCreateFungibleData (306) */
-  interface UpDataStructsCreateFungibleData extends Struct {
-    readonly value: u128;
-  }
-
-  /** @name UpDataStructsCreateReFungibleData (307) */
-  interface UpDataStructsCreateReFungibleData extends Struct {
-    readonly pieces: u128;
-    readonly properties: Vec<UpDataStructsProperty>;
-  }
-
-  /** @name UpDataStructsCreateItemExData (311) */
-  interface UpDataStructsCreateItemExData extends Enum {
-    readonly isNft: boolean;
-    readonly asNft: Vec<UpDataStructsCreateNftExData>;
-    readonly isFungible: boolean;
-    readonly asFungible: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
-    readonly isRefungibleMultipleItems: boolean;
-    readonly asRefungibleMultipleItems: Vec<UpDataStructsCreateRefungibleExSingleOwner>;
-    readonly isRefungibleMultipleOwners: boolean;
-    readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners;
-    readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
-  }
-
-  /** @name UpDataStructsCreateNftExData (313) */
-  interface UpDataStructsCreateNftExData extends Struct {
-    readonly properties: Vec<UpDataStructsProperty>;
-    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-  }
-
-  /** @name UpDataStructsCreateRefungibleExSingleOwner (320) */
-  interface UpDataStructsCreateRefungibleExSingleOwner extends Struct {
-    readonly user: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly pieces: u128;
-    readonly properties: Vec<UpDataStructsProperty>;
-  }
-
-  /** @name UpDataStructsCreateRefungibleExMultipleOwners (322) */
-  interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct {
-    readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
-    readonly properties: Vec<UpDataStructsProperty>;
-  }
-
-  /** @name PalletConfigurationCall (323) */
-  interface PalletConfigurationCall extends Enum {
-    readonly isSetWeightToFeeCoefficientOverride: boolean;
-    readonly asSetWeightToFeeCoefficientOverride: {
-      readonly coeff: Option<u64>;
-    } & Struct;
-    readonly isSetMinGasPriceOverride: boolean;
-    readonly asSetMinGasPriceOverride: {
-      readonly coeff: Option<u64>;
-    } & Struct;
-    readonly isSetAppPromotionConfigurationOverride: boolean;
-    readonly asSetAppPromotionConfigurationOverride: {
-      readonly configuration: PalletConfigurationAppPromotionConfiguration;
-    } & Struct;
-    readonly isSetCollatorSelectionDesiredCollators: boolean;
-    readonly asSetCollatorSelectionDesiredCollators: {
-      readonly max: Option<u32>;
-    } & Struct;
-    readonly isSetCollatorSelectionLicenseBond: boolean;
-    readonly asSetCollatorSelectionLicenseBond: {
-      readonly amount: Option<u128>;
-    } & Struct;
-    readonly isSetCollatorSelectionKickThreshold: boolean;
-    readonly asSetCollatorSelectionKickThreshold: {
-      readonly threshold: Option<u32>;
-    } & Struct;
-    readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold';
-  }
-
-  /** @name PalletConfigurationAppPromotionConfiguration (325) */
-  interface PalletConfigurationAppPromotionConfiguration extends Struct {
-    readonly recalculationInterval: Option<u32>;
-    readonly pendingInterval: Option<u32>;
-    readonly intervalIncome: Option<Perbill>;
-    readonly maxStakersPerCalculation: Option<u8>;
-  }
-
-  /** @name PalletStructureCall (330) */
-  type PalletStructureCall = Null;
-
-  /** @name PalletAppPromotionCall (331) */
-  interface PalletAppPromotionCall extends Enum {
-    readonly isSetAdminAddress: boolean;
-    readonly asSetAdminAddress: {
-      readonly admin: PalletEvmAccountBasicCrossAccountIdRepr;
-    } & Struct;
-    readonly isStake: boolean;
-    readonly asStake: {
-      readonly amount: u128;
-    } & Struct;
-    readonly isUnstakeAll: boolean;
-    readonly isSponsorCollection: boolean;
-    readonly asSponsorCollection: {
-      readonly collectionId: u32;
-    } & Struct;
-    readonly isStopSponsoringCollection: boolean;
-    readonly asStopSponsoringCollection: {
-      readonly collectionId: u32;
-    } & Struct;
-    readonly isSponsorContract: boolean;
-    readonly asSponsorContract: {
-      readonly contractId: H160;
-    } & Struct;
-    readonly isStopSponsoringContract: boolean;
-    readonly asStopSponsoringContract: {
-      readonly contractId: H160;
-    } & Struct;
-    readonly isPayoutStakers: boolean;
-    readonly asPayoutStakers: {
-      readonly stakersNumber: Option<u8>;
-    } & Struct;
-    readonly isUnstakePartial: boolean;
-    readonly asUnstakePartial: {
-      readonly amount: u128;
-    } & Struct;
-    readonly isForceUnstake: boolean;
-    readonly asForceUnstake: {
-      readonly pendingBlocks: Vec<u32>;
-    } & Struct;
-    readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake';
-  }
-
-  /** @name PalletForeignAssetsModuleCall (333) */
-  interface PalletForeignAssetsModuleCall extends Enum {
-    readonly isRegisterForeignAsset: boolean;
-    readonly asRegisterForeignAsset: {
-      readonly owner: AccountId32;
-      readonly location: StagingXcmVersionedMultiLocation;
-      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-    } & Struct;
-    readonly isUpdateForeignAsset: boolean;
-    readonly asUpdateForeignAsset: {
-      readonly foreignAssetId: u32;
-      readonly location: StagingXcmVersionedMultiLocation;
-      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-    } & Struct;
-    readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset';
-  }
-
-  /** @name PalletForeignAssetsModuleAssetMetadata (334) */
-  interface PalletForeignAssetsModuleAssetMetadata extends Struct {
-    readonly name: Bytes;
-    readonly symbol: Bytes;
-    readonly decimals: u8;
-    readonly minimalBalance: u128;
-  }
-
-  /** @name PalletEvmCall (337) */
-  interface PalletEvmCall extends Enum {
-    readonly isWithdraw: boolean;
-    readonly asWithdraw: {
-      readonly address: H160;
-      readonly value: u128;
-    } & Struct;
-    readonly isCall: boolean;
-    readonly asCall: {
-      readonly source: H160;
-      readonly target: H160;
-      readonly input: Bytes;
-      readonly value: U256;
-      readonly gasLimit: u64;
-      readonly maxFeePerGas: U256;
-      readonly maxPriorityFeePerGas: Option<U256>;
-      readonly nonce: Option<U256>;
-      readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
-    } & Struct;
-    readonly isCreate: boolean;
-    readonly asCreate: {
-      readonly source: H160;
-      readonly init: Bytes;
-      readonly value: U256;
-      readonly gasLimit: u64;
-      readonly maxFeePerGas: U256;
-      readonly maxPriorityFeePerGas: Option<U256>;
-      readonly nonce: Option<U256>;
-      readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
-    } & Struct;
-    readonly isCreate2: boolean;
-    readonly asCreate2: {
-      readonly source: H160;
-      readonly init: Bytes;
-      readonly salt: H256;
-      readonly value: U256;
-      readonly gasLimit: u64;
-      readonly maxFeePerGas: U256;
-      readonly maxPriorityFeePerGas: Option<U256>;
-      readonly nonce: Option<U256>;
-      readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
-    } & Struct;
-    readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
-  }
-
-  /** @name PalletEthereumCall (344) */
-  interface PalletEthereumCall extends Enum {
-    readonly isTransact: boolean;
-    readonly asTransact: {
-      readonly transaction: EthereumTransactionTransactionV2;
-    } & Struct;
-    readonly type: 'Transact';
-  }
-
-  /** @name EthereumTransactionTransactionV2 (345) */
-  interface EthereumTransactionTransactionV2 extends Enum {
-    readonly isLegacy: boolean;
-    readonly asLegacy: EthereumTransactionLegacyTransaction;
-    readonly isEip2930: boolean;
-    readonly asEip2930: EthereumTransactionEip2930Transaction;
-    readonly isEip1559: boolean;
-    readonly asEip1559: EthereumTransactionEip1559Transaction;
-    readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
-  }
-
-  /** @name EthereumTransactionLegacyTransaction (346) */
-  interface EthereumTransactionLegacyTransaction extends Struct {
-    readonly nonce: U256;
-    readonly gasPrice: U256;
-    readonly gasLimit: U256;
-    readonly action: EthereumTransactionTransactionAction;
-    readonly value: U256;
-    readonly input: Bytes;
-    readonly signature: EthereumTransactionTransactionSignature;
-  }
-
-  /** @name EthereumTransactionTransactionAction (347) */
-  interface EthereumTransactionTransactionAction extends Enum {
-    readonly isCall: boolean;
-    readonly asCall: H160;
-    readonly isCreate: boolean;
-    readonly type: 'Call' | 'Create';
-  }
-
-  /** @name EthereumTransactionTransactionSignature (348) */
-  interface EthereumTransactionTransactionSignature extends Struct {
-    readonly v: u64;
-    readonly r: H256;
-    readonly s: H256;
-  }
-
-  /** @name EthereumTransactionEip2930Transaction (350) */
-  interface EthereumTransactionEip2930Transaction extends Struct {
-    readonly chainId: u64;
-    readonly nonce: U256;
-    readonly gasPrice: U256;
-    readonly gasLimit: U256;
-    readonly action: EthereumTransactionTransactionAction;
-    readonly value: U256;
-    readonly input: Bytes;
-    readonly accessList: Vec<EthereumTransactionAccessListItem>;
-    readonly oddYParity: bool;
-    readonly r: H256;
-    readonly s: H256;
-  }
-
-  /** @name EthereumTransactionAccessListItem (352) */
-  interface EthereumTransactionAccessListItem extends Struct {
-    readonly address: H160;
-    readonly storageKeys: Vec<H256>;
-  }
-
-  /** @name EthereumTransactionEip1559Transaction (353) */
-  interface EthereumTransactionEip1559Transaction extends Struct {
-    readonly chainId: u64;
-    readonly nonce: U256;
-    readonly maxPriorityFeePerGas: U256;
-    readonly maxFeePerGas: U256;
-    readonly gasLimit: U256;
-    readonly action: EthereumTransactionTransactionAction;
-    readonly value: U256;
-    readonly input: Bytes;
-    readonly accessList: Vec<EthereumTransactionAccessListItem>;
-    readonly oddYParity: bool;
-    readonly r: H256;
-    readonly s: H256;
-  }
-
-  /** @name PalletEvmContractHelpersCall (354) */
-  interface PalletEvmContractHelpersCall extends Enum {
-    readonly isMigrateFromSelfSponsoring: boolean;
-    readonly asMigrateFromSelfSponsoring: {
-      readonly addresses: Vec<H160>;
-    } & Struct;
-    readonly type: 'MigrateFromSelfSponsoring';
-  }
-
-  /** @name PalletEvmMigrationCall (356) */
-  interface PalletEvmMigrationCall extends Enum {
-    readonly isBegin: boolean;
-    readonly asBegin: {
-      readonly address: H160;
-    } & Struct;
-    readonly isSetData: boolean;
-    readonly asSetData: {
-      readonly address: H160;
-      readonly data: Vec<ITuple<[H256, H256]>>;
-    } & Struct;
-    readonly isFinish: boolean;
-    readonly asFinish: {
-      readonly address: H160;
-      readonly code: Bytes;
-    } & Struct;
-    readonly isInsertEthLogs: boolean;
-    readonly asInsertEthLogs: {
-      readonly logs: Vec<EthereumLog>;
-    } & Struct;
-    readonly isInsertEvents: boolean;
-    readonly asInsertEvents: {
-      readonly events: Vec<Bytes>;
-    } & Struct;
-    readonly isRemoveRmrkData: boolean;
-    readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData';
-  }
-
-  /** @name EthereumLog (360) */
-  interface EthereumLog extends Struct {
-    readonly address: H160;
-    readonly topics: Vec<H256>;
-    readonly data: Bytes;
-  }
-
-  /** @name PalletMaintenanceCall (361) */
-  interface PalletMaintenanceCall extends Enum {
-    readonly isEnable: boolean;
-    readonly isDisable: boolean;
-    readonly type: 'Enable' | 'Disable';
-  }
-
-  /** @name PalletUtilityCall (362) */
-  interface PalletUtilityCall extends Enum {
-    readonly isBatch: boolean;
-    readonly asBatch: {
-      readonly calls: Vec<Call>;
-    } & Struct;
-    readonly isAsDerivative: boolean;
-    readonly asAsDerivative: {
-      readonly index: u16;
-      readonly call: Call;
-    } & Struct;
-    readonly isBatchAll: boolean;
-    readonly asBatchAll: {
-      readonly calls: Vec<Call>;
-    } & Struct;
-    readonly isDispatchAs: boolean;
-    readonly asDispatchAs: {
-      readonly asOrigin: OpalRuntimeOriginCaller;
-      readonly call: Call;
-    } & Struct;
-    readonly isForceBatch: boolean;
-    readonly asForceBatch: {
-      readonly calls: Vec<Call>;
-    } & Struct;
-    readonly isWithWeight: boolean;
-    readonly asWithWeight: {
-      readonly call: Call;
-      readonly weight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight';
-  }
-
-  /** @name PalletTestUtilsCall (364) */
-  interface PalletTestUtilsCall extends Enum {
-    readonly isEnable: boolean;
-    readonly isSetTestValue: boolean;
-    readonly asSetTestValue: {
-      readonly value: u32;
-    } & Struct;
-    readonly isSetTestValueAndRollback: boolean;
-    readonly asSetTestValueAndRollback: {
-      readonly value: u32;
-    } & Struct;
-    readonly isIncTestValue: boolean;
-    readonly isJustTakeFee: boolean;
-    readonly isBatchAll: boolean;
-    readonly asBatchAll: {
-      readonly calls: Vec<Call>;
-    } & Struct;
-    readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll';
-  }
-
-  /** @name PalletSchedulerEvent (366) */
-  interface PalletSchedulerEvent extends Enum {
-    readonly isScheduled: boolean;
-    readonly asScheduled: {
-      readonly when: u32;
-      readonly index: u32;
-    } & Struct;
-    readonly isCanceled: boolean;
-    readonly asCanceled: {
-      readonly when: u32;
-      readonly index: u32;
-    } & Struct;
-    readonly isDispatched: boolean;
-    readonly asDispatched: {
-      readonly task: ITuple<[u32, u32]>;
-      readonly id: Option<U8aFixed>;
-      readonly result: Result<Null, SpRuntimeDispatchError>;
-    } & Struct;
-    readonly isCallUnavailable: boolean;
-    readonly asCallUnavailable: {
-      readonly task: ITuple<[u32, u32]>;
-      readonly id: Option<U8aFixed>;
-    } & Struct;
-    readonly isPeriodicFailed: boolean;
-    readonly asPeriodicFailed: {
-      readonly task: ITuple<[u32, u32]>;
-      readonly id: Option<U8aFixed>;
-    } & Struct;
-    readonly isPermanentlyOverweight: boolean;
-    readonly asPermanentlyOverweight: {
-      readonly task: ITuple<[u32, u32]>;
-      readonly id: Option<U8aFixed>;
-    } & Struct;
-    readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight';
-  }
-
-  /** @name CumulusPalletXcmpQueueEvent (367) */
-  interface CumulusPalletXcmpQueueEvent extends Enum {
-    readonly isSuccess: boolean;
-    readonly asSuccess: {
-      readonly messageHash: U8aFixed;
-      readonly messageId: U8aFixed;
-      readonly weight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isFail: boolean;
-    readonly asFail: {
-      readonly messageHash: U8aFixed;
-      readonly messageId: U8aFixed;
-      readonly error: StagingXcmV3TraitsError;
-      readonly weight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isBadVersion: boolean;
-    readonly asBadVersion: {
-      readonly messageHash: U8aFixed;
-    } & Struct;
-    readonly isBadFormat: boolean;
-    readonly asBadFormat: {
-      readonly messageHash: U8aFixed;
-    } & Struct;
-    readonly isXcmpMessageSent: boolean;
-    readonly asXcmpMessageSent: {
-      readonly messageHash: U8aFixed;
-    } & Struct;
-    readonly isOverweightEnqueued: boolean;
-    readonly asOverweightEnqueued: {
-      readonly sender: u32;
-      readonly sentAt: u32;
-      readonly index: u64;
-      readonly required: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isOverweightServiced: boolean;
-    readonly asOverweightServiced: {
-      readonly index: u64;
-      readonly used: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced';
-  }
-
-  /** @name PalletXcmEvent (368) */
-  interface PalletXcmEvent extends Enum {
-    readonly isAttempted: boolean;
-    readonly asAttempted: {
-      readonly outcome: StagingXcmV3TraitsOutcome;
-    } & Struct;
-    readonly isSent: boolean;
-    readonly asSent: {
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly destination: StagingXcmV3MultiLocation;
-      readonly message: StagingXcmV3Xcm;
-      readonly messageId: U8aFixed;
-    } & Struct;
-    readonly isUnexpectedResponse: boolean;
-    readonly asUnexpectedResponse: {
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly queryId: u64;
-    } & Struct;
-    readonly isResponseReady: boolean;
-    readonly asResponseReady: {
-      readonly queryId: u64;
-      readonly response: StagingXcmV3Response;
-    } & Struct;
-    readonly isNotified: boolean;
-    readonly asNotified: {
-      readonly queryId: u64;
-      readonly palletIndex: u8;
-      readonly callIndex: u8;
-    } & Struct;
-    readonly isNotifyOverweight: boolean;
-    readonly asNotifyOverweight: {
-      readonly queryId: u64;
-      readonly palletIndex: u8;
-      readonly callIndex: u8;
-      readonly actualWeight: SpWeightsWeightV2Weight;
-      readonly maxBudgetedWeight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isNotifyDispatchError: boolean;
-    readonly asNotifyDispatchError: {
-      readonly queryId: u64;
-      readonly palletIndex: u8;
-      readonly callIndex: u8;
-    } & Struct;
-    readonly isNotifyDecodeFailed: boolean;
-    readonly asNotifyDecodeFailed: {
-      readonly queryId: u64;
-      readonly palletIndex: u8;
-      readonly callIndex: u8;
-    } & Struct;
-    readonly isInvalidResponder: boolean;
-    readonly asInvalidResponder: {
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly queryId: u64;
-      readonly expectedLocation: Option<StagingXcmV3MultiLocation>;
-    } & Struct;
-    readonly isInvalidResponderVersion: boolean;
-    readonly asInvalidResponderVersion: {
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly queryId: u64;
-    } & Struct;
-    readonly isResponseTaken: boolean;
-    readonly asResponseTaken: {
-      readonly queryId: u64;
-    } & Struct;
-    readonly isAssetsTrapped: boolean;
-    readonly asAssetsTrapped: {
-      readonly hash_: H256;
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly assets: StagingXcmVersionedMultiAssets;
-    } & Struct;
-    readonly isVersionChangeNotified: boolean;
-    readonly asVersionChangeNotified: {
-      readonly destination: StagingXcmV3MultiLocation;
-      readonly result: u32;
-      readonly cost: StagingXcmV3MultiassetMultiAssets;
-      readonly messageId: U8aFixed;
-    } & Struct;
-    readonly isSupportedVersionChanged: boolean;
-    readonly asSupportedVersionChanged: {
-      readonly location: StagingXcmV3MultiLocation;
-      readonly version: u32;
-    } & Struct;
-    readonly isNotifyTargetSendFail: boolean;
-    readonly asNotifyTargetSendFail: {
-      readonly location: StagingXcmV3MultiLocation;
-      readonly queryId: u64;
-      readonly error: StagingXcmV3TraitsError;
-    } & Struct;
-    readonly isNotifyTargetMigrationFail: boolean;
-    readonly asNotifyTargetMigrationFail: {
-      readonly location: StagingXcmVersionedMultiLocation;
-      readonly queryId: u64;
-    } & Struct;
-    readonly isInvalidQuerierVersion: boolean;
-    readonly asInvalidQuerierVersion: {
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly queryId: u64;
-    } & Struct;
-    readonly isInvalidQuerier: boolean;
-    readonly asInvalidQuerier: {
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly queryId: u64;
-      readonly expectedQuerier: StagingXcmV3MultiLocation;
-      readonly maybeActualQuerier: Option<StagingXcmV3MultiLocation>;
-    } & Struct;
-    readonly isVersionNotifyStarted: boolean;
-    readonly asVersionNotifyStarted: {
-      readonly destination: StagingXcmV3MultiLocation;
-      readonly cost: StagingXcmV3MultiassetMultiAssets;
-      readonly messageId: U8aFixed;
-    } & Struct;
-    readonly isVersionNotifyRequested: boolean;
-    readonly asVersionNotifyRequested: {
-      readonly destination: StagingXcmV3MultiLocation;
-      readonly cost: StagingXcmV3MultiassetMultiAssets;
-      readonly messageId: U8aFixed;
-    } & Struct;
-    readonly isVersionNotifyUnrequested: boolean;
-    readonly asVersionNotifyUnrequested: {
-      readonly destination: StagingXcmV3MultiLocation;
-      readonly cost: StagingXcmV3MultiassetMultiAssets;
-      readonly messageId: U8aFixed;
-    } & Struct;
-    readonly isFeesPaid: boolean;
-    readonly asFeesPaid: {
-      readonly paying: StagingXcmV3MultiLocation;
-      readonly fees: StagingXcmV3MultiassetMultiAssets;
-    } & Struct;
-    readonly isAssetsClaimed: boolean;
-    readonly asAssetsClaimed: {
-      readonly hash_: H256;
-      readonly origin: StagingXcmV3MultiLocation;
-      readonly assets: StagingXcmVersionedMultiAssets;
-    } & Struct;
-    readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed';
-  }
-
-  /** @name StagingXcmV3TraitsOutcome (369) */
-  interface StagingXcmV3TraitsOutcome extends Enum {
-    readonly isComplete: boolean;
-    readonly asComplete: SpWeightsWeightV2Weight;
-    readonly isIncomplete: boolean;
-    readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>;
-    readonly isError: boolean;
-    readonly asError: StagingXcmV3TraitsError;
-    readonly type: 'Complete' | 'Incomplete' | 'Error';
-  }
-
-  /** @name CumulusPalletXcmEvent (370) */
-  interface CumulusPalletXcmEvent extends Enum {
-    readonly isInvalidFormat: boolean;
-    readonly asInvalidFormat: U8aFixed;
-    readonly isUnsupportedVersion: boolean;
-    readonly asUnsupportedVersion: U8aFixed;
-    readonly isExecutedDownward: boolean;
-    readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>;
-    readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward';
-  }
-
-  /** @name CumulusPalletDmpQueueEvent (371) */
-  interface CumulusPalletDmpQueueEvent extends Enum {
-    readonly isInvalidFormat: boolean;
-    readonly asInvalidFormat: {
-      readonly messageHash: U8aFixed;
-    } & Struct;
-    readonly isUnsupportedVersion: boolean;
-    readonly asUnsupportedVersion: {
-      readonly messageHash: U8aFixed;
-    } & Struct;
-    readonly isExecutedDownward: boolean;
-    readonly asExecutedDownward: {
-      readonly messageHash: U8aFixed;
-      readonly messageId: U8aFixed;
-      readonly outcome: StagingXcmV3TraitsOutcome;
-    } & Struct;
-    readonly isWeightExhausted: boolean;
-    readonly asWeightExhausted: {
-      readonly messageHash: U8aFixed;
-      readonly messageId: U8aFixed;
-      readonly remainingWeight: SpWeightsWeightV2Weight;
-      readonly requiredWeight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isOverweightEnqueued: boolean;
-    readonly asOverweightEnqueued: {
-      readonly messageHash: U8aFixed;
-      readonly messageId: U8aFixed;
-      readonly overweightIndex: u64;
-      readonly requiredWeight: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isOverweightServiced: boolean;
-    readonly asOverweightServiced: {
-      readonly overweightIndex: u64;
-      readonly weightUsed: SpWeightsWeightV2Weight;
-    } & Struct;
-    readonly isMaxMessagesExhausted: boolean;
-    readonly asMaxMessagesExhausted: {
-      readonly messageHash: U8aFixed;
-    } & Struct;
-    readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted';
-  }
-
-  /** @name PalletConfigurationEvent (372) */
-  interface PalletConfigurationEvent extends Enum {
-    readonly isNewDesiredCollators: boolean;
-    readonly asNewDesiredCollators: {
-      readonly desiredCollators: Option<u32>;
-    } & Struct;
-    readonly isNewCollatorLicenseBond: boolean;
-    readonly asNewCollatorLicenseBond: {
-      readonly bondCost: Option<u128>;
-    } & Struct;
-    readonly isNewCollatorKickThreshold: boolean;
-    readonly asNewCollatorKickThreshold: {
-      readonly lengthInBlocks: Option<u32>;
-    } & Struct;
-    readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold';
-  }
-
-  /** @name PalletCommonEvent (373) */
-  interface PalletCommonEvent extends Enum {
-    readonly isCollectionCreated: boolean;
-    readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>;
-    readonly isCollectionDestroyed: boolean;
-    readonly asCollectionDestroyed: u32;
-    readonly isItemCreated: boolean;
-    readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-    readonly isItemDestroyed: boolean;
-    readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-    readonly isTransfer: boolean;
-    readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-    readonly isApproved: boolean;
-    readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
-    readonly isApprovedForAll: boolean;
-    readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
-    readonly isCollectionPropertySet: boolean;
-    readonly asCollectionPropertySet: ITuple<[u32, Bytes]>;
-    readonly isCollectionPropertyDeleted: boolean;
-    readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>;
-    readonly isTokenPropertySet: boolean;
-    readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>;
-    readonly isTokenPropertyDeleted: boolean;
-    readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>;
-    readonly isPropertyPermissionSet: boolean;
-    readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>;
-    readonly isAllowListAddressAdded: boolean;
-    readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-    readonly isAllowListAddressRemoved: boolean;
-    readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-    readonly isCollectionAdminAdded: boolean;
-    readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-    readonly isCollectionAdminRemoved: boolean;
-    readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
-    readonly isCollectionLimitSet: boolean;
-    readonly asCollectionLimitSet: u32;
-    readonly isCollectionOwnerChanged: boolean;
-    readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>;
-    readonly isCollectionPermissionSet: boolean;
-    readonly asCollectionPermissionSet: u32;
-    readonly isCollectionSponsorSet: boolean;
-    readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>;
-    readonly isSponsorshipConfirmed: boolean;
-    readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>;
-    readonly isCollectionSponsorRemoved: boolean;
-    readonly asCollectionSponsorRemoved: u32;
-    readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved';
-  }
-
-  /** @name PalletStructureEvent (374) */
-  interface PalletStructureEvent extends Enum {
-    readonly isExecuted: boolean;
-    readonly asExecuted: Result<Null, SpRuntimeDispatchError>;
-    readonly type: 'Executed';
-  }
-
-  /** @name PalletAppPromotionEvent (375) */
-  interface PalletAppPromotionEvent extends Enum {
-    readonly isStakingRecalculation: boolean;
-    readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
-    readonly isStake: boolean;
-    readonly asStake: ITuple<[AccountId32, u128]>;
-    readonly isUnstake: boolean;
-    readonly asUnstake: ITuple<[AccountId32, u128]>;
-    readonly isSetAdmin: boolean;
-    readonly asSetAdmin: AccountId32;
-    readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin';
-  }
-
-  /** @name PalletForeignAssetsModuleEvent (376) */
-  interface PalletForeignAssetsModuleEvent extends Enum {
-    readonly isForeignAssetRegistered: boolean;
-    readonly asForeignAssetRegistered: {
-      readonly assetId: u32;
-      readonly assetAddress: StagingXcmV3MultiLocation;
-      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-    } & Struct;
-    readonly isForeignAssetUpdated: boolean;
-    readonly asForeignAssetUpdated: {
-      readonly assetId: u32;
-      readonly assetAddress: StagingXcmV3MultiLocation;
-      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-    } & Struct;
-    readonly isAssetRegistered: boolean;
-    readonly asAssetRegistered: {
-      readonly assetId: PalletForeignAssetsAssetId;
-      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-    } & Struct;
-    readonly isAssetUpdated: boolean;
-    readonly asAssetUpdated: {
-      readonly assetId: PalletForeignAssetsAssetId;
-      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
-    } & Struct;
-    readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated';
-  }
-
-  /** @name PalletEvmEvent (377) */
-  interface PalletEvmEvent extends Enum {
-    readonly isLog: boolean;
-    readonly asLog: {
-      readonly log: EthereumLog;
-    } & Struct;
-    readonly isCreated: boolean;
-    readonly asCreated: {
-      readonly address: H160;
-    } & Struct;
-    readonly isCreatedFailed: boolean;
-    readonly asCreatedFailed: {
-      readonly address: H160;
-    } & Struct;
-    readonly isExecuted: boolean;
-    readonly asExecuted: {
-      readonly address: H160;
-    } & Struct;
-    readonly isExecutedFailed: boolean;
-    readonly asExecutedFailed: {
-      readonly address: H160;
-    } & Struct;
-    readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed';
-  }
-
-  /** @name PalletEthereumEvent (378) */
-  interface PalletEthereumEvent extends Enum {
-    readonly isExecuted: boolean;
-    readonly asExecuted: {
-      readonly from: H160;
-      readonly to: H160;
-      readonly transactionHash: H256;
-      readonly exitReason: EvmCoreErrorExitReason;
-      readonly extraData: Bytes;
-    } & Struct;
-    readonly type: 'Executed';
-  }
-
-  /** @name EvmCoreErrorExitReason (379) */
-  interface EvmCoreErrorExitReason extends Enum {
-    readonly isSucceed: boolean;
-    readonly asSucceed: EvmCoreErrorExitSucceed;
-    readonly isError: boolean;
-    readonly asError: EvmCoreErrorExitError;
-    readonly isRevert: boolean;
-    readonly asRevert: EvmCoreErrorExitRevert;
-    readonly isFatal: boolean;
-    readonly asFatal: EvmCoreErrorExitFatal;
-    readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal';
-  }
-
-  /** @name EvmCoreErrorExitSucceed (380) */
-  interface EvmCoreErrorExitSucceed extends Enum {
-    readonly isStopped: boolean;
-    readonly isReturned: boolean;
-    readonly isSuicided: boolean;
-    readonly type: 'Stopped' | 'Returned' | 'Suicided';
-  }
-
-  /** @name EvmCoreErrorExitError (381) */
-  interface EvmCoreErrorExitError extends Enum {
-    readonly isStackUnderflow: boolean;
-    readonly isStackOverflow: boolean;
-    readonly isInvalidJump: boolean;
-    readonly isInvalidRange: boolean;
-    readonly isDesignatedInvalid: boolean;
-    readonly isCallTooDeep: boolean;
-    readonly isCreateCollision: boolean;
-    readonly isCreateContractLimit: boolean;
-    readonly isOutOfOffset: boolean;
-    readonly isOutOfGas: boolean;
-    readonly isOutOfFund: boolean;
-    readonly isPcUnderflow: boolean;
-    readonly isCreateEmpty: boolean;
-    readonly isOther: boolean;
-    readonly asOther: Text;
-    readonly isMaxNonce: boolean;
-    readonly isInvalidCode: boolean;
-    readonly asInvalidCode: u8;
-    readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode';
-  }
-
-  /** @name EvmCoreErrorExitRevert (385) */
-  interface EvmCoreErrorExitRevert extends Enum {
-    readonly isReverted: boolean;
-    readonly type: 'Reverted';
-  }
-
-  /** @name EvmCoreErrorExitFatal (386) */
-  interface EvmCoreErrorExitFatal extends Enum {
-    readonly isNotSupported: boolean;
-    readonly isUnhandledInterrupt: boolean;
-    readonly isCallErrorAsFatal: boolean;
-    readonly asCallErrorAsFatal: EvmCoreErrorExitError;
-    readonly isOther: boolean;
-    readonly asOther: Text;
-    readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other';
-  }
-
-  /** @name PalletEvmContractHelpersEvent (387) */
-  interface PalletEvmContractHelpersEvent extends Enum {
-    readonly isContractSponsorSet: boolean;
-    readonly asContractSponsorSet: ITuple<[H160, AccountId32]>;
-    readonly isContractSponsorshipConfirmed: boolean;
-    readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>;
-    readonly isContractSponsorRemoved: boolean;
-    readonly asContractSponsorRemoved: H160;
-    readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved';
-  }
-
-  /** @name PalletEvmMigrationEvent (388) */
-  interface PalletEvmMigrationEvent extends Enum {
-    readonly isTestEvent: boolean;
-    readonly type: 'TestEvent';
-  }
-
-  /** @name PalletMaintenanceEvent (389) */
-  interface PalletMaintenanceEvent extends Enum {
-    readonly isMaintenanceEnabled: boolean;
-    readonly isMaintenanceDisabled: boolean;
-    readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled';
-  }
-
-  /** @name PalletUtilityEvent (390) */
-  interface PalletUtilityEvent extends Enum {
-    readonly isBatchInterrupted: boolean;
-    readonly asBatchInterrupted: {
-      readonly index: u32;
-      readonly error: SpRuntimeDispatchError;
-    } & Struct;
-    readonly isBatchCompleted: boolean;
-    readonly isBatchCompletedWithErrors: boolean;
-    readonly isItemCompleted: boolean;
-    readonly isItemFailed: boolean;
-    readonly asItemFailed: {
-      readonly error: SpRuntimeDispatchError;
-    } & Struct;
-    readonly isDispatchedAs: boolean;
-    readonly asDispatchedAs: {
-      readonly result: Result<Null, SpRuntimeDispatchError>;
-    } & Struct;
-    readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs';
-  }
-
-  /** @name PalletTestUtilsEvent (391) */
-  interface PalletTestUtilsEvent extends Enum {
-    readonly isValueIsSet: boolean;
-    readonly isShouldRollback: boolean;
-    readonly isBatchCompleted: boolean;
-    readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted';
-  }
-
-  /** @name FrameSystemPhase (392) */
-  interface FrameSystemPhase extends Enum {
-    readonly isApplyExtrinsic: boolean;
-    readonly asApplyExtrinsic: u32;
-    readonly isFinalization: boolean;
-    readonly isInitialization: boolean;
-    readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization';
-  }
-
-  /** @name FrameSystemLastRuntimeUpgradeInfo (394) */
-  interface FrameSystemLastRuntimeUpgradeInfo extends Struct {
-    readonly specVersion: Compact<u32>;
-    readonly specName: Text;
-  }
-
-  /** @name FrameSystemLimitsBlockWeights (395) */
-  interface FrameSystemLimitsBlockWeights extends Struct {
-    readonly baseBlock: SpWeightsWeightV2Weight;
-    readonly maxBlock: SpWeightsWeightV2Weight;
-    readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
-  }
-
-  /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (396) */
-  interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct {
-    readonly normal: FrameSystemLimitsWeightsPerClass;
-    readonly operational: FrameSystemLimitsWeightsPerClass;
-    readonly mandatory: FrameSystemLimitsWeightsPerClass;
-  }
-
-  /** @name FrameSystemLimitsWeightsPerClass (397) */
-  interface FrameSystemLimitsWeightsPerClass extends Struct {
-    readonly baseExtrinsic: SpWeightsWeightV2Weight;
-    readonly maxExtrinsic: Option<SpWeightsWeightV2Weight>;
-    readonly maxTotal: Option<SpWeightsWeightV2Weight>;
-    readonly reserved: Option<SpWeightsWeightV2Weight>;
-  }
-
-  /** @name FrameSystemLimitsBlockLength (399) */
-  interface FrameSystemLimitsBlockLength extends Struct {
-    readonly max: FrameSupportDispatchPerDispatchClassU32;
-  }
-
-  /** @name FrameSupportDispatchPerDispatchClassU32 (400) */
-  interface FrameSupportDispatchPerDispatchClassU32 extends Struct {
-    readonly normal: u32;
-    readonly operational: u32;
-    readonly mandatory: u32;
-  }
-
-  /** @name SpWeightsRuntimeDbWeight (401) */
-  interface SpWeightsRuntimeDbWeight extends Struct {
-    readonly read: u64;
-    readonly write: u64;
-  }
-
-  /** @name SpVersionRuntimeVersion (402) */
-  interface SpVersionRuntimeVersion extends Struct {
-    readonly specName: Text;
-    readonly implName: Text;
-    readonly authoringVersion: u32;
-    readonly specVersion: u32;
-    readonly implVersion: u32;
-    readonly apis: Vec<ITuple<[U8aFixed, u32]>>;
-    readonly transactionVersion: u32;
-    readonly stateVersion: u8;
-  }
-
-  /** @name FrameSystemError (406) */
-  interface FrameSystemError extends Enum {
-    readonly isInvalidSpecName: boolean;
-    readonly isSpecVersionNeedsToIncrease: boolean;
-    readonly isFailedToExtractRuntimeVersion: boolean;
-    readonly isNonDefaultComposite: boolean;
-    readonly isNonZeroRefCount: boolean;
-    readonly isCallFiltered: boolean;
-    readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered';
-  }
-
-  /** @name CumulusPalletParachainSystemUnincludedSegmentAncestor (408) */
-  interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct {
-    readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
-    readonly paraHeadHash: Option<H256>;
-    readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
-  }
-
-  /** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth (409) */
-  interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct {
-    readonly umpMsgCount: u32;
-    readonly umpTotalBytes: u32;
-    readonly hrmpOutgoing: BTreeMap<u32, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate>;
-  }
-
-  /** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate (411) */
-  interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct {
-    readonly msgCount: u32;
-    readonly totalBytes: u32;
-  }
-
-  /** @name PolkadotPrimitivesV5UpgradeGoAhead (415) */
-  interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum {
-    readonly isAbort: boolean;
-    readonly isGoAhead: boolean;
-    readonly type: 'Abort' | 'GoAhead';
-  }
-
-  /** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker (416) */
-  interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct {
-    readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
-    readonly hrmpWatermark: Option<u32>;
-    readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
-  }
-
-  /** @name PolkadotPrimitivesV5UpgradeRestriction (418) */
-  interface PolkadotPrimitivesV5UpgradeRestriction extends Enum {
-    readonly isPresent: boolean;
-    readonly type: 'Present';
-  }
-
-  /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (419) */
-  interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct {
-    readonly dmqMqcHead: H256;
-    readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity;
-    readonly ingressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
-    readonly egressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
-  }
-
-  /** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity (420) */
-  interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct {
-    readonly remainingCount: u32;
-    readonly remainingSize: u32;
-  }
-
-  /** @name PolkadotPrimitivesV5AbridgedHrmpChannel (423) */
-  interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct {
-    readonly maxCapacity: u32;
-    readonly maxTotalSize: u32;
-    readonly maxMessageSize: u32;
-    readonly msgCount: u32;
-    readonly totalSize: u32;
-    readonly mqcHead: Option<H256>;
-  }
-
-  /** @name PolkadotPrimitivesV5AbridgedHostConfiguration (424) */
-  interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct {
-    readonly maxCodeSize: u32;
-    readonly maxHeadDataSize: u32;
-    readonly maxUpwardQueueCount: u32;
-    readonly maxUpwardQueueSize: u32;
-    readonly maxUpwardMessageSize: u32;
-    readonly maxUpwardMessageNumPerCandidate: u32;
-    readonly hrmpMaxMessageNumPerCandidate: u32;
-    readonly validationUpgradeCooldown: u32;
-    readonly validationUpgradeDelay: u32;
-    readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams;
-  }
-
-  /** @name PolkadotPrimitivesVstagingAsyncBackingParams (425) */
-  interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct {
-    readonly maxCandidateDepth: u32;
-    readonly allowedAncestryLen: u32;
-  }
-
-  /** @name PolkadotCorePrimitivesOutboundHrmpMessage (431) */
-  interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct {
-    readonly recipient: u32;
-    readonly data: Bytes;
-  }
-
-  /** @name CumulusPalletParachainSystemCodeUpgradeAuthorization (432) */
-  interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct {
-    readonly codeHash: H256;
-    readonly checkVersion: bool;
-  }
-
-  /** @name CumulusPalletParachainSystemError (433) */
-  interface CumulusPalletParachainSystemError extends Enum {
-    readonly isOverlappingUpgrades: boolean;
-    readonly isProhibitedByPolkadot: boolean;
-    readonly isTooBig: boolean;
-    readonly isValidationDataNotAvailable: boolean;
-    readonly isHostConfigurationNotAvailable: boolean;
-    readonly isNotScheduled: boolean;
-    readonly isNothingAuthorized: boolean;
-    readonly isUnauthorized: boolean;
-    readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized';
-  }
-
-  /** @name PalletCollatorSelectionError (435) */
-  interface PalletCollatorSelectionError extends Enum {
-    readonly isTooManyCandidates: boolean;
-    readonly isUnknown: boolean;
-    readonly isPermission: boolean;
-    readonly isAlreadyHoldingLicense: boolean;
-    readonly isNoLicense: boolean;
-    readonly isAlreadyCandidate: boolean;
-    readonly isNotCandidate: boolean;
-    readonly isTooManyInvulnerables: boolean;
-    readonly isTooFewInvulnerables: boolean;
-    readonly isAlreadyInvulnerable: boolean;
-    readonly isNotInvulnerable: boolean;
-    readonly isNoAssociatedValidatorId: boolean;
-    readonly isValidatorNotRegistered: boolean;
-    readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered';
-  }
-
-  /** @name SpCoreCryptoKeyTypeId (439) */
-  interface SpCoreCryptoKeyTypeId extends U8aFixed {}
-
-  /** @name PalletSessionError (440) */
-  interface PalletSessionError extends Enum {
-    readonly isInvalidProof: boolean;
-    readonly isNoAssociatedValidatorId: boolean;
-    readonly isDuplicatedKey: boolean;
-    readonly isNoKeys: boolean;
-    readonly isNoAccount: boolean;
-    readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount';
-  }
-
-  /** @name PalletBalancesBalanceLock (446) */
-  interface PalletBalancesBalanceLock extends Struct {
-    readonly id: U8aFixed;
-    readonly amount: u128;
-    readonly reasons: PalletBalancesReasons;
-  }
-
-  /** @name PalletBalancesReasons (447) */
-  interface PalletBalancesReasons extends Enum {
-    readonly isFee: boolean;
-    readonly isMisc: boolean;
-    readonly isAll: boolean;
-    readonly type: 'Fee' | 'Misc' | 'All';
-  }
-
-  /** @name PalletBalancesReserveData (450) */
-  interface PalletBalancesReserveData extends Struct {
-    readonly id: U8aFixed;
-    readonly amount: u128;
-  }
-
-  /** @name OpalRuntimeRuntimeHoldReason (454) */
-  interface OpalRuntimeRuntimeHoldReason extends Enum {
-    readonly isCollatorSelection: boolean;
-    readonly asCollatorSelection: PalletCollatorSelectionHoldReason;
-    readonly type: 'CollatorSelection';
-  }
-
-  /** @name PalletCollatorSelectionHoldReason (455) */
-  interface PalletCollatorSelectionHoldReason extends Enum {
-    readonly isLicenseBond: boolean;
-    readonly type: 'LicenseBond';
-  }
-
-  /** @name PalletBalancesIdAmount (458) */
-  interface PalletBalancesIdAmount extends Struct {
-    readonly id: U8aFixed;
-    readonly amount: u128;
-  }
-
-  /** @name PalletBalancesError (460) */
-  interface PalletBalancesError extends Enum {
-    readonly isVestingBalance: boolean;
-    readonly isLiquidityRestrictions: boolean;
-    readonly isInsufficientBalance: boolean;
-    readonly isExistentialDeposit: boolean;
-    readonly isExpendability: boolean;
-    readonly isExistingVestingSchedule: boolean;
-    readonly isDeadAccount: boolean;
-    readonly isTooManyReserves: boolean;
-    readonly isTooManyHolds: boolean;
-    readonly isTooManyFreezes: boolean;
-    readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes';
-  }
-
-  /** @name PalletTransactionPaymentReleases (462) */
-  interface PalletTransactionPaymentReleases extends Enum {
-    readonly isV1Ancient: boolean;
-    readonly isV2: boolean;
-    readonly type: 'V1Ancient' | 'V2';
-  }
-
-  /** @name PalletTreasuryProposal (463) */
-  interface PalletTreasuryProposal extends Struct {
-    readonly proposer: AccountId32;
-    readonly value: u128;
-    readonly beneficiary: AccountId32;
-    readonly bond: u128;
-  }
-
-  /** @name FrameSupportPalletId (466) */
-  interface FrameSupportPalletId extends U8aFixed {}
-
-  /** @name PalletTreasuryError (467) */
-  interface PalletTreasuryError extends Enum {
-    readonly isInsufficientProposersBalance: boolean;
-    readonly isInvalidIndex: boolean;
-    readonly isTooManyApprovals: boolean;
-    readonly isInsufficientPermission: boolean;
-    readonly isProposalNotApproved: boolean;
-    readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved';
-  }
-
-  /** @name PalletSudoError (468) */
-  interface PalletSudoError extends Enum {
-    readonly isRequireSudo: boolean;
-    readonly type: 'RequireSudo';
-  }
-
-  /** @name OrmlVestingModuleError (470) */
-  interface OrmlVestingModuleError extends Enum {
-    readonly isZeroVestingPeriod: boolean;
-    readonly isZeroVestingPeriodCount: boolean;
-    readonly isInsufficientBalanceToLock: boolean;
-    readonly isTooManyVestingSchedules: boolean;
-    readonly isAmountLow: boolean;
-    readonly isMaxVestingSchedulesExceeded: boolean;
-    readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
-  }
-
-  /** @name OrmlXtokensModuleError (471) */
-  interface OrmlXtokensModuleError extends Enum {
-    readonly isAssetHasNoReserve: boolean;
-    readonly isNotCrossChainTransfer: boolean;
-    readonly isInvalidDest: boolean;
-    readonly isNotCrossChainTransferableCurrency: boolean;
-    readonly isUnweighableMessage: boolean;
-    readonly isXcmExecutionFailed: boolean;
-    readonly isCannotReanchor: boolean;
-    readonly isInvalidAncestry: boolean;
-    readonly isInvalidAsset: boolean;
-    readonly isDestinationNotInvertible: boolean;
-    readonly isBadVersion: boolean;
-    readonly isDistinctReserveForAssetAndFee: boolean;
-    readonly isZeroFee: boolean;
-    readonly isZeroAmount: boolean;
-    readonly isTooManyAssetsBeingSent: boolean;
-    readonly isAssetIndexNonExistent: boolean;
-    readonly isFeeNotEnough: boolean;
-    readonly isNotSupportedMultiLocation: boolean;
-    readonly isMinXcmFeeNotDefined: boolean;
-    readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined';
-  }
-
-  /** @name OrmlTokensBalanceLock (474) */
-  interface OrmlTokensBalanceLock extends Struct {
-    readonly id: U8aFixed;
-    readonly amount: u128;
-  }
-
-  /** @name OrmlTokensAccountData (476) */
-  interface OrmlTokensAccountData extends Struct {
-    readonly free: u128;
-    readonly reserved: u128;
-    readonly frozen: u128;
-  }
-
-  /** @name OrmlTokensReserveData (478) */
-  interface OrmlTokensReserveData extends Struct {
-    readonly id: Null;
-    readonly amount: u128;
-  }
-
-  /** @name OrmlTokensModuleError (480) */
-  interface OrmlTokensModuleError extends Enum {
-    readonly isBalanceTooLow: boolean;
-    readonly isAmountIntoBalanceFailed: boolean;
-    readonly isLiquidityRestrictions: boolean;
-    readonly isMaxLocksExceeded: boolean;
-    readonly isKeepAlive: boolean;
-    readonly isExistentialDeposit: boolean;
-    readonly isDeadAccount: boolean;
-    readonly isTooManyReserves: boolean;
-    readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves';
-  }
-
-  /** @name PalletIdentityRegistrarInfo (485) */
-  interface PalletIdentityRegistrarInfo extends Struct {
-    readonly account: AccountId32;
-    readonly fee: u128;
-    readonly fields: PalletIdentityBitFlags;
-  }
-
-  /** @name PalletIdentityError (487) */
-  interface PalletIdentityError extends Enum {
-    readonly isTooManySubAccounts: boolean;
-    readonly isNotFound: boolean;
-    readonly isNotNamed: boolean;
-    readonly isEmptyIndex: boolean;
-    readonly isFeeChanged: boolean;
-    readonly isNoIdentity: boolean;
-    readonly isStickyJudgement: boolean;
-    readonly isJudgementGiven: boolean;
-    readonly isInvalidJudgement: boolean;
-    readonly isInvalidIndex: boolean;
-    readonly isInvalidTarget: boolean;
-    readonly isTooManyFields: boolean;
-    readonly isTooManyRegistrars: boolean;
-    readonly isAlreadyClaimed: boolean;
-    readonly isNotSub: boolean;
-    readonly isNotOwned: boolean;
-    readonly isJudgementForDifferentIdentity: boolean;
-    readonly isJudgementPaymentFailed: boolean;
-    readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed';
-  }
-
-  /** @name PalletPreimageRequestStatus (488) */
-  interface PalletPreimageRequestStatus extends Enum {
-    readonly isUnrequested: boolean;
-    readonly asUnrequested: {
-      readonly deposit: ITuple<[AccountId32, u128]>;
-      readonly len: u32;
-    } & Struct;
-    readonly isRequested: boolean;
-    readonly asRequested: {
-      readonly deposit: Option<ITuple<[AccountId32, u128]>>;
-      readonly count: u32;
-      readonly len: Option<u32>;
-    } & Struct;
-    readonly type: 'Unrequested' | 'Requested';
-  }
-
-  /** @name PalletPreimageError (493) */
-  interface PalletPreimageError extends Enum {
-    readonly isTooBig: boolean;
-    readonly isAlreadyNoted: boolean;
-    readonly isNotAuthorized: boolean;
-    readonly isNotNoted: boolean;
-    readonly isRequested: boolean;
-    readonly isNotRequested: boolean;
-    readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested';
-  }
-
-  /** @name PalletDemocracyReferendumInfo (499) */
-  interface PalletDemocracyReferendumInfo extends Enum {
-    readonly isOngoing: boolean;
-    readonly asOngoing: PalletDemocracyReferendumStatus;
-    readonly isFinished: boolean;
-    readonly asFinished: {
-      readonly approved: bool;
-      readonly end: u32;
-    } & Struct;
-    readonly type: 'Ongoing' | 'Finished';
-  }
-
-  /** @name PalletDemocracyReferendumStatus (500) */
-  interface PalletDemocracyReferendumStatus extends Struct {
-    readonly end: u32;
-    readonly proposal: FrameSupportPreimagesBounded;
-    readonly threshold: PalletDemocracyVoteThreshold;
-    readonly delay: u32;
-    readonly tally: PalletDemocracyTally;
-  }
-
-  /** @name PalletDemocracyTally (501) */
-  interface PalletDemocracyTally extends Struct {
-    readonly ayes: u128;
-    readonly nays: u128;
-    readonly turnout: u128;
-  }
-
-  /** @name PalletDemocracyVoteVoting (502) */
-  interface PalletDemocracyVoteVoting extends Enum {
-    readonly isDirect: boolean;
-    readonly asDirect: {
-      readonly votes: Vec<ITuple<[u32, PalletDemocracyVoteAccountVote]>>;
-      readonly delegations: PalletDemocracyDelegations;
-      readonly prior: PalletDemocracyVotePriorLock;
-    } & Struct;
-    readonly isDelegating: boolean;
-    readonly asDelegating: {
-      readonly balance: u128;
-      readonly target: AccountId32;
-      readonly conviction: PalletDemocracyConviction;
-      readonly delegations: PalletDemocracyDelegations;
-      readonly prior: PalletDemocracyVotePriorLock;
-    } & Struct;
-    readonly type: 'Direct' | 'Delegating';
-  }
-
-  /** @name PalletDemocracyDelegations (506) */
-  interface PalletDemocracyDelegations extends Struct {
-    readonly votes: u128;
-    readonly capital: u128;
-  }
-
-  /** @name PalletDemocracyVotePriorLock (507) */
-  interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {}
-
-  /** @name PalletDemocracyError (510) */
-  interface PalletDemocracyError extends Enum {
-    readonly isValueLow: boolean;
-    readonly isProposalMissing: boolean;
-    readonly isAlreadyCanceled: boolean;
-    readonly isDuplicateProposal: boolean;
-    readonly isProposalBlacklisted: boolean;
-    readonly isNotSimpleMajority: boolean;
-    readonly isInvalidHash: boolean;
-    readonly isNoProposal: boolean;
-    readonly isAlreadyVetoed: boolean;
-    readonly isReferendumInvalid: boolean;
-    readonly isNoneWaiting: boolean;
-    readonly isNotVoter: boolean;
-    readonly isNoPermission: boolean;
-    readonly isAlreadyDelegating: boolean;
-    readonly isInsufficientFunds: boolean;
-    readonly isNotDelegating: boolean;
-    readonly isVotesExist: boolean;
-    readonly isInstantNotAllowed: boolean;
-    readonly isNonsense: boolean;
-    readonly isWrongUpperBound: boolean;
-    readonly isMaxVotesReached: boolean;
-    readonly isTooMany: boolean;
-    readonly isVotingPeriodLow: boolean;
-    readonly isPreimageNotExist: boolean;
-    readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist';
-  }
-
-  /** @name PalletCollectiveVotes (512) */
-  interface PalletCollectiveVotes extends Struct {
-    readonly index: u32;
-    readonly threshold: u32;
-    readonly ayes: Vec<AccountId32>;
-    readonly nays: Vec<AccountId32>;
-    readonly end: u32;
-  }
-
-  /** @name PalletCollectiveError (513) */
-  interface PalletCollectiveError extends Enum {
-    readonly isNotMember: boolean;
-    readonly isDuplicateProposal: boolean;
-    readonly isProposalMissing: boolean;
-    readonly isWrongIndex: boolean;
-    readonly isDuplicateVote: boolean;
-    readonly isAlreadyInitialized: boolean;
-    readonly isTooEarly: boolean;
-    readonly isTooManyProposals: boolean;
-    readonly isWrongProposalWeight: boolean;
-    readonly isWrongProposalLength: boolean;
-    readonly isPrimeAccountNotMember: boolean;
-    readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember';
-  }
-
-  /** @name PalletMembershipError (517) */
-  interface PalletMembershipError extends Enum {
-    readonly isAlreadyMember: boolean;
-    readonly isNotMember: boolean;
-    readonly isTooManyMembers: boolean;
-    readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers';
-  }
-
-  /** @name PalletRankedCollectiveMemberRecord (520) */
-  interface PalletRankedCollectiveMemberRecord extends Struct {
-    readonly rank: u16;
-  }
-
-  /** @name PalletRankedCollectiveError (525) */
-  interface PalletRankedCollectiveError extends Enum {
-    readonly isAlreadyMember: boolean;
-    readonly isNotMember: boolean;
-    readonly isNotPolling: boolean;
-    readonly isOngoing: boolean;
-    readonly isNoneRemaining: boolean;
-    readonly isCorruption: boolean;
-    readonly isRankTooLow: boolean;
-    readonly isInvalidWitness: boolean;
-    readonly isNoPermission: boolean;
-    readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission';
-  }
-
-  /** @name PalletReferendaReferendumInfo (526) */
-  interface PalletReferendaReferendumInfo extends Enum {
-    readonly isOngoing: boolean;
-    readonly asOngoing: PalletReferendaReferendumStatus;
-    readonly isApproved: boolean;
-    readonly asApproved: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-    readonly isRejected: boolean;
-    readonly asRejected: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-    readonly isCancelled: boolean;
-    readonly asCancelled: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-    readonly isTimedOut: boolean;
-    readonly asTimedOut: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
-    readonly isKilled: boolean;
-    readonly asKilled: u32;
-    readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed';
-  }
-
-  /** @name PalletReferendaReferendumStatus (527) */
-  interface PalletReferendaReferendumStatus extends Struct {
-    readonly track: u16;
-    readonly origin: OpalRuntimeOriginCaller;
-    readonly proposal: FrameSupportPreimagesBounded;
-    readonly enactment: FrameSupportScheduleDispatchTime;
-    readonly submitted: u32;
-    readonly submissionDeposit: PalletReferendaDeposit;
-    readonly decisionDeposit: Option<PalletReferendaDeposit>;
-    readonly deciding: Option<PalletReferendaDecidingStatus>;
-    readonly tally: PalletRankedCollectiveTally;
-    readonly inQueue: bool;
-    readonly alarm: Option<ITuple<[u32, ITuple<[u32, u32]>]>>;
-  }
-
-  /** @name PalletReferendaDeposit (528) */
-  interface PalletReferendaDeposit extends Struct {
-    readonly who: AccountId32;
-    readonly amount: u128;
-  }
-
-  /** @name PalletReferendaDecidingStatus (531) */
-  interface PalletReferendaDecidingStatus extends Struct {
-    readonly since: u32;
-    readonly confirming: Option<u32>;
-  }
-
-  /** @name PalletReferendaTrackInfo (537) */
-  interface PalletReferendaTrackInfo extends Struct {
-    readonly name: Text;
-    readonly maxDeciding: u32;
-    readonly decisionDeposit: u128;
-    readonly preparePeriod: u32;
-    readonly decisionPeriod: u32;
-    readonly confirmPeriod: u32;
-    readonly minEnactmentPeriod: u32;
-    readonly minApproval: PalletReferendaCurve;
-    readonly minSupport: PalletReferendaCurve;
-  }
-
-  /** @name PalletReferendaCurve (538) */
-  interface PalletReferendaCurve extends Enum {
-    readonly isLinearDecreasing: boolean;
-    readonly asLinearDecreasing: {
-      readonly length: Perbill;
-      readonly floor: Perbill;
-      readonly ceil: Perbill;
-    } & Struct;
-    readonly isSteppedDecreasing: boolean;
-    readonly asSteppedDecreasing: {
-      readonly begin: Perbill;
-      readonly end: Perbill;
-      readonly step: Perbill;
-      readonly period: Perbill;
-    } & Struct;
-    readonly isReciprocal: boolean;
-    readonly asReciprocal: {
-      readonly factor: i64;
-      readonly xOffset: i64;
-      readonly yOffset: i64;
-    } & Struct;
-    readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal';
-  }
-
-  /** @name PalletReferendaError (541) */
-  interface PalletReferendaError extends Enum {
-    readonly isNotOngoing: boolean;
-    readonly isHasDeposit: boolean;
-    readonly isBadTrack: boolean;
-    readonly isFull: boolean;
-    readonly isQueueEmpty: boolean;
-    readonly isBadReferendum: boolean;
-    readonly isNothingToDo: boolean;
-    readonly isNoTrack: boolean;
-    readonly isUnfinished: boolean;
-    readonly isNoPermission: boolean;
-    readonly isNoDeposit: boolean;
-    readonly isBadStatus: boolean;
-    readonly isPreimageNotExist: boolean;
-    readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist';
-  }
-
-  /** @name PalletSchedulerScheduled (544) */
-  interface PalletSchedulerScheduled extends Struct {
-    readonly maybeId: Option<U8aFixed>;
-    readonly priority: u8;
-    readonly call: FrameSupportPreimagesBounded;
-    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
-    readonly origin: OpalRuntimeOriginCaller;
-  }
-
-  /** @name PalletSchedulerError (546) */
-  interface PalletSchedulerError extends Enum {
-    readonly isFailedToSchedule: boolean;
-    readonly isNotFound: boolean;
-    readonly isTargetBlockNumberInPast: boolean;
-    readonly isRescheduleNoChange: boolean;
-    readonly isNamed: boolean;
-    readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named';
-  }
-
-  /** @name CumulusPalletXcmpQueueInboundChannelDetails (548) */
-  interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
-    readonly sender: u32;
-    readonly state: CumulusPalletXcmpQueueInboundState;
-    readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat]>>;
-  }
-
-  /** @name CumulusPalletXcmpQueueInboundState (549) */
-  interface CumulusPalletXcmpQueueInboundState extends Enum {
-    readonly isOk: boolean;
-    readonly isSuspended: boolean;
-    readonly type: 'Ok' | 'Suspended';
-  }
-
-  /** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat (552) */
-  interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum {
-    readonly isConcatenatedVersionedXcm: boolean;
-    readonly isConcatenatedEncodedBlob: boolean;
-    readonly isSignals: boolean;
-    readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
-  }
-
-  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (555) */
-  interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
-    readonly recipient: u32;
-    readonly state: CumulusPalletXcmpQueueOutboundState;
-    readonly signalsExist: bool;
-    readonly firstIndex: u16;
-    readonly lastIndex: u16;
-  }
-
-  /** @name CumulusPalletXcmpQueueOutboundState (556) */
-  interface CumulusPalletXcmpQueueOutboundState extends Enum {
-    readonly isOk: boolean;
-    readonly isSuspended: boolean;
-    readonly type: 'Ok' | 'Suspended';
-  }
-
-  /** @name CumulusPalletXcmpQueueQueueConfigData (558) */
-  interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
-    readonly suspendThreshold: u32;
-    readonly dropThreshold: u32;
-    readonly resumeThreshold: u32;
-    readonly thresholdWeight: SpWeightsWeightV2Weight;
-    readonly weightRestrictDecay: SpWeightsWeightV2Weight;
-    readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight;
-  }
-
-  /** @name CumulusPalletXcmpQueueError (560) */
-  interface CumulusPalletXcmpQueueError extends Enum {
-    readonly isFailedToSend: boolean;
-    readonly isBadXcmOrigin: boolean;
-    readonly isBadXcm: boolean;
-    readonly isBadOverweightIndex: boolean;
-    readonly isWeightOverLimit: boolean;
-    readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
-  }
-
-  /** @name PalletXcmQueryStatus (561) */
-  interface PalletXcmQueryStatus extends Enum {
-    readonly isPending: boolean;
-    readonly asPending: {
-      readonly responder: StagingXcmVersionedMultiLocation;
-      readonly maybeMatchQuerier: Option<StagingXcmVersionedMultiLocation>;
-      readonly maybeNotify: Option<ITuple<[u8, u8]>>;
-      readonly timeout: u32;
-    } & Struct;
-    readonly isVersionNotifier: boolean;
-    readonly asVersionNotifier: {
-      readonly origin: StagingXcmVersionedMultiLocation;
-      readonly isActive: bool;
-    } & Struct;
-    readonly isReady: boolean;
-    readonly asReady: {
-      readonly response: StagingXcmVersionedResponse;
-      readonly at: u32;
-    } & Struct;
-    readonly type: 'Pending' | 'VersionNotifier' | 'Ready';
-  }
-
-  /** @name StagingXcmVersionedResponse (565) */
-  interface StagingXcmVersionedResponse extends Enum {
-    readonly isV2: boolean;
-    readonly asV2: StagingXcmV2Response;
-    readonly isV3: boolean;
-    readonly asV3: StagingXcmV3Response;
-    readonly type: 'V2' | 'V3';
-  }
-
-  /** @name PalletXcmVersionMigrationStage (571) */
-  interface PalletXcmVersionMigrationStage extends Enum {
-    readonly isMigrateSupportedVersion: boolean;
-    readonly isMigrateVersionNotifiers: boolean;
-    readonly isNotifyCurrentTargets: boolean;
-    readonly asNotifyCurrentTargets: Option<Bytes>;
-    readonly isMigrateAndNotifyOldTargets: boolean;
-    readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets';
-  }
-
-  /** @name StagingXcmVersionedAssetId (574) */
-  interface StagingXcmVersionedAssetId extends Enum {
-    readonly isV3: boolean;
-    readonly asV3: StagingXcmV3MultiassetAssetId;
-    readonly type: 'V3';
-  }
-
-  /** @name PalletXcmRemoteLockedFungibleRecord (575) */
-  interface PalletXcmRemoteLockedFungibleRecord extends Struct {
-    readonly amount: u128;
-    readonly owner: StagingXcmVersionedMultiLocation;
-    readonly locker: StagingXcmVersionedMultiLocation;
-    readonly consumers: Vec<ITuple<[Null, u128]>>;
-  }
-
-  /** @name PalletXcmError (582) */
-  interface PalletXcmError extends Enum {
-    readonly isUnreachable: boolean;
-    readonly isSendFailure: boolean;
-    readonly isFiltered: boolean;
-    readonly isUnweighableMessage: boolean;
-    readonly isDestinationNotInvertible: boolean;
-    readonly isEmpty: boolean;
-    readonly isCannotReanchor: boolean;
-    readonly isTooManyAssets: boolean;
-    readonly isInvalidOrigin: boolean;
-    readonly isBadVersion: boolean;
-    readonly isBadLocation: boolean;
-    readonly isNoSubscription: boolean;
-    readonly isAlreadySubscribed: boolean;
-    readonly isInvalidAsset: boolean;
-    readonly isLowBalance: boolean;
-    readonly isTooManyLocks: boolean;
-    readonly isAccountNotSovereign: boolean;
-    readonly isFeesNotMet: boolean;
-    readonly isLockNotFound: boolean;
-    readonly isInUse: boolean;
-    readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse';
-  }
-
-  /** @name CumulusPalletXcmError (583) */
-  type CumulusPalletXcmError = Null;
-
-  /** @name CumulusPalletDmpQueueConfigData (584) */
-  interface CumulusPalletDmpQueueConfigData extends Struct {
-    readonly maxIndividual: SpWeightsWeightV2Weight;
-  }
-
-  /** @name CumulusPalletDmpQueuePageIndexData (585) */
-  interface CumulusPalletDmpQueuePageIndexData extends Struct {
-    readonly beginUsed: u32;
-    readonly endUsed: u32;
-    readonly overweightCount: u64;
-  }
-
-  /** @name CumulusPalletDmpQueueError (588) */
-  interface CumulusPalletDmpQueueError extends Enum {
-    readonly isUnknown: boolean;
-    readonly isOverLimit: boolean;
-    readonly type: 'Unknown' | 'OverLimit';
-  }
-
-  /** @name PalletUniqueError (592) */
-  interface PalletUniqueError extends Enum {
-    readonly isCollectionDecimalPointLimitExceeded: boolean;
-    readonly isEmptyArgument: boolean;
-    readonly isRepartitionCalledOnNonRefungibleCollection: boolean;
-    readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection';
-  }
-
-  /** @name PalletConfigurationError (593) */
-  interface PalletConfigurationError extends Enum {
-    readonly isInconsistentConfiguration: boolean;
-    readonly type: 'InconsistentConfiguration';
-  }
-
-  /** @name UpDataStructsCollection (594) */
-  interface UpDataStructsCollection extends Struct {
-    readonly owner: AccountId32;
-    readonly mode: UpDataStructsCollectionMode;
-    readonly name: Vec<u16>;
-    readonly description: Vec<u16>;
-    readonly tokenPrefix: Bytes;
-    readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
-    readonly limits: UpDataStructsCollectionLimits;
-    readonly permissions: UpDataStructsCollectionPermissions;
-    readonly flags: U8aFixed;
-  }
-
-  /** @name UpDataStructsSponsorshipStateAccountId32 (595) */
-  interface UpDataStructsSponsorshipStateAccountId32 extends Enum {
-    readonly isDisabled: boolean;
-    readonly isUnconfirmed: boolean;
-    readonly asUnconfirmed: AccountId32;
-    readonly isConfirmed: boolean;
-    readonly asConfirmed: AccountId32;
-    readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
-  }
-
-  /** @name UpDataStructsProperties (596) */
-  interface UpDataStructsProperties extends Struct {
-    readonly map: UpDataStructsPropertiesMapBoundedVec;
-    readonly consumedSpace: u32;
-    readonly reserved: u32;
-  }
-
-  /** @name UpDataStructsPropertiesMapBoundedVec (597) */
-  interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap<Bytes, Bytes> {}
-
-  /** @name UpDataStructsPropertiesMapPropertyPermission (602) */
-  interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap<Bytes, UpDataStructsPropertyPermission> {}
-
-  /** @name UpDataStructsCollectionStats (609) */
-  interface UpDataStructsCollectionStats extends Struct {
-    readonly created: u32;
-    readonly destroyed: u32;
-    readonly alive: u32;
-  }
-
-  /** @name UpDataStructsTokenChild (610) */
-  interface UpDataStructsTokenChild extends Struct {
-    readonly token: u32;
-    readonly collection: u32;
-  }
-
-  /** @name PhantomTypeUpDataStructs (611) */
-  interface PhantomTypeUpDataStructs extends Vec<ITuple<[UpDataStructsTokenData, UpDataStructsRpcCollection, UpPovEstimateRpcPovInfo]>> {}
-
-  /** @name UpDataStructsTokenData (613) */
-  interface UpDataStructsTokenData extends Struct {
-    readonly properties: Vec<UpDataStructsProperty>;
-    readonly owner: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
-    readonly pieces: u128;
-  }
-
-  /** @name UpDataStructsRpcCollection (614) */
-  interface UpDataStructsRpcCollection extends Struct {
-    readonly owner: AccountId32;
-    readonly mode: UpDataStructsCollectionMode;
-    readonly name: Vec<u16>;
-    readonly description: Vec<u16>;
-    readonly tokenPrefix: Bytes;
-    readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
-    readonly limits: UpDataStructsCollectionLimits;
-    readonly permissions: UpDataStructsCollectionPermissions;
-    readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
-    readonly properties: Vec<UpDataStructsProperty>;
-    readonly readOnly: bool;
-    readonly flags: UpDataStructsRpcCollectionFlags;
-  }
-
-  /** @name UpDataStructsRpcCollectionFlags (615) */
-  interface UpDataStructsRpcCollectionFlags extends Struct {
-    readonly foreign: bool;
-    readonly erc721metadata: bool;
-  }
-
-  /** @name UpPovEstimateRpcPovInfo (616) */
-  interface UpPovEstimateRpcPovInfo extends Struct {
-    readonly proofSize: u64;
-    readonly compactProofSize: u64;
-    readonly compressedProofSize: u64;
-    readonly results: Vec<Result<Result<Null, SpRuntimeDispatchError>, SpRuntimeTransactionValidityTransactionValidityError>>;
-    readonly keyValues: Vec<UpPovEstimateRpcTrieKeyValue>;
-  }
-
-  /** @name SpRuntimeTransactionValidityTransactionValidityError (619) */
-  interface SpRuntimeTransactionValidityTransactionValidityError extends Enum {
-    readonly isInvalid: boolean;
-    readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction;
-    readonly isUnknown: boolean;
-    readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction;
-    readonly type: 'Invalid' | 'Unknown';
-  }
-
-  /** @name SpRuntimeTransactionValidityInvalidTransaction (620) */
-  interface SpRuntimeTransactionValidityInvalidTransaction extends Enum {
-    readonly isCall: boolean;
-    readonly isPayment: boolean;
-    readonly isFuture: boolean;
-    readonly isStale: boolean;
-    readonly isBadProof: boolean;
-    readonly isAncientBirthBlock: boolean;
-    readonly isExhaustsResources: boolean;
-    readonly isCustom: boolean;
-    readonly asCustom: u8;
-    readonly isBadMandatory: boolean;
-    readonly isMandatoryValidation: boolean;
-    readonly isBadSigner: boolean;
-    readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner';
-  }
-
-  /** @name SpRuntimeTransactionValidityUnknownTransaction (621) */
-  interface SpRuntimeTransactionValidityUnknownTransaction extends Enum {
-    readonly isCannotLookup: boolean;
-    readonly isNoUnsignedValidator: boolean;
-    readonly isCustom: boolean;
-    readonly asCustom: u8;
-    readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom';
-  }
-
-  /** @name UpPovEstimateRpcTrieKeyValue (623) */
-  interface UpPovEstimateRpcTrieKeyValue extends Struct {
-    readonly key: Bytes;
-    readonly value: Bytes;
-  }
-
-  /** @name PalletCommonError (625) */
-  interface PalletCommonError extends Enum {
-    readonly isCollectionNotFound: boolean;
-    readonly isMustBeTokenOwner: boolean;
-    readonly isNoPermission: boolean;
-    readonly isCantDestroyNotEmptyCollection: boolean;
-    readonly isPublicMintingNotAllowed: boolean;
-    readonly isAddressNotInAllowlist: boolean;
-    readonly isCollectionNameLimitExceeded: boolean;
-    readonly isCollectionDescriptionLimitExceeded: boolean;
-    readonly isCollectionTokenPrefixLimitExceeded: boolean;
-    readonly isTotalCollectionsLimitExceeded: boolean;
-    readonly isCollectionAdminCountExceeded: boolean;
-    readonly isCollectionLimitBoundsExceeded: boolean;
-    readonly isOwnerPermissionsCantBeReverted: boolean;
-    readonly isTransferNotAllowed: boolean;
-    readonly isAccountTokenLimitExceeded: boolean;
-    readonly isCollectionTokenLimitExceeded: boolean;
-    readonly isMetadataFlagFrozen: boolean;
-    readonly isTokenNotFound: boolean;
-    readonly isTokenValueTooLow: boolean;
-    readonly isApprovedValueTooLow: boolean;
-    readonly isCantApproveMoreThanOwned: boolean;
-    readonly isAddressIsNotEthMirror: boolean;
-    readonly isAddressIsZero: boolean;
-    readonly isUnsupportedOperation: boolean;
-    readonly isNotSufficientFounds: boolean;
-    readonly isUserIsNotAllowedToNest: boolean;
-    readonly isSourceCollectionIsNotAllowedToNest: boolean;
-    readonly isCollectionFieldSizeExceeded: boolean;
-    readonly isNoSpaceForProperty: boolean;
-    readonly isPropertyLimitReached: boolean;
-    readonly isPropertyKeyIsTooLong: boolean;
-    readonly isInvalidCharacterInPropertyKey: boolean;
-    readonly isEmptyPropertyKey: boolean;
-    readonly isCollectionIsExternal: boolean;
-    readonly isCollectionIsInternal: boolean;
-    readonly isConfirmSponsorshipFail: boolean;
-    readonly isUserIsNotCollectionAdmin: boolean;
-    readonly isFungibleItemsHaveNoId: boolean;
-    readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId';
-  }
-
-  /** @name PalletFungibleError (627) */
-  interface PalletFungibleError extends Enum {
-    readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
-    readonly isFungibleItemsDontHaveData: boolean;
-    readonly isFungibleDisallowsNesting: boolean;
-    readonly isSettingPropertiesNotAllowed: boolean;
-    readonly isSettingAllowanceForAllNotAllowed: boolean;
-    readonly isFungibleTokensAreAlwaysValid: boolean;
-    readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid';
-  }
-
-  /** @name PalletRefungibleError (632) */
-  interface PalletRefungibleError extends Enum {
-    readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
-    readonly isWrongRefungiblePieces: boolean;
-    readonly isRepartitionWhileNotOwningAllPieces: boolean;
-    readonly isRefungibleDisallowsNesting: boolean;
-    readonly isSettingPropertiesNotAllowed: boolean;
-    readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
-  }
-
-  /** @name PalletNonfungibleItemData (633) */
-  interface PalletNonfungibleItemData extends Struct {
-    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
-  }
-
-  /** @name UpDataStructsPropertyScope (635) */
-  interface UpDataStructsPropertyScope extends Enum {
-    readonly isNone: boolean;
-    readonly isRmrk: boolean;
-    readonly type: 'None' | 'Rmrk';
-  }
-
-  /** @name PalletNonfungibleError (638) */
-  interface PalletNonfungibleError extends Enum {
-    readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
-    readonly isNonfungibleItemsHaveNoAmount: boolean;
-    readonly isCantBurnNftWithChildren: boolean;
-    readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren';
-  }
-
-  /** @name PalletStructureError (639) */
-  interface PalletStructureError extends Enum {
-    readonly isOuroborosDetected: boolean;
-    readonly isDepthLimit: boolean;
-    readonly isBreadthLimit: boolean;
-    readonly isTokenNotFound: boolean;
-    readonly isCantNestTokenUnderCollection: boolean;
-    readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection';
-  }
-
-  /** @name PalletAppPromotionError (644) */
-  interface PalletAppPromotionError extends Enum {
-    readonly isAdminNotSet: boolean;
-    readonly isNoPermission: boolean;
-    readonly isNotSufficientFunds: boolean;
-    readonly isPendingForBlockOverflow: boolean;
-    readonly isSponsorNotSet: boolean;
-    readonly isInsufficientStakedBalance: boolean;
-    readonly isInconsistencyState: boolean;
-    readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState';
-  }
-
-  /** @name PalletForeignAssetsModuleError (645) */
-  interface PalletForeignAssetsModuleError extends Enum {
-    readonly isBadLocation: boolean;
-    readonly isMultiLocationExisted: boolean;
-    readonly isAssetIdNotExists: boolean;
-    readonly isAssetIdExisted: boolean;
-    readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted';
-  }
-
-  /** @name PalletEvmCodeMetadata (646) */
-  interface PalletEvmCodeMetadata extends Struct {
-    readonly size_: u64;
-    readonly hash_: H256;
-  }
-
-  /** @name PalletEvmError (648) */
-  interface PalletEvmError extends Enum {
-    readonly isBalanceLow: boolean;
-    readonly isFeeOverflow: boolean;
-    readonly isPaymentOverflow: boolean;
-    readonly isWithdrawFailed: boolean;
-    readonly isGasPriceTooLow: boolean;
-    readonly isInvalidNonce: boolean;
-    readonly isGasLimitTooLow: boolean;
-    readonly isGasLimitTooHigh: boolean;
-    readonly isUndefined: boolean;
-    readonly isReentrancy: boolean;
-    readonly isTransactionMustComeFromEOA: boolean;
-    readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA';
-  }
-
-  /** @name FpRpcTransactionStatus (651) */
-  interface FpRpcTransactionStatus extends Struct {
-    readonly transactionHash: H256;
-    readonly transactionIndex: u32;
-    readonly from: H160;
-    readonly to: Option<H160>;
-    readonly contractAddress: Option<H160>;
-    readonly logs: Vec<EthereumLog>;
-    readonly logsBloom: EthbloomBloom;
-  }
-
-  /** @name EthbloomBloom (653) */
-  interface EthbloomBloom extends U8aFixed {}
-
-  /** @name EthereumReceiptReceiptV3 (655) */
-  interface EthereumReceiptReceiptV3 extends Enum {
-    readonly isLegacy: boolean;
-    readonly asLegacy: EthereumReceiptEip658ReceiptData;
-    readonly isEip2930: boolean;
-    readonly asEip2930: EthereumReceiptEip658ReceiptData;
-    readonly isEip1559: boolean;
-    readonly asEip1559: EthereumReceiptEip658ReceiptData;
-    readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
-  }
-
-  /** @name EthereumReceiptEip658ReceiptData (656) */
-  interface EthereumReceiptEip658ReceiptData extends Struct {
-    readonly statusCode: u8;
-    readonly usedGas: U256;
-    readonly logsBloom: EthbloomBloom;
-    readonly logs: Vec<EthereumLog>;
-  }
-
-  /** @name EthereumBlock (657) */
-  interface EthereumBlock extends Struct {
-    readonly header: EthereumHeader;
-    readonly transactions: Vec<EthereumTransactionTransactionV2>;
-    readonly ommers: Vec<EthereumHeader>;
-  }
-
-  /** @name EthereumHeader (658) */
-  interface EthereumHeader extends Struct {
-    readonly parentHash: H256;
-    readonly ommersHash: H256;
-    readonly beneficiary: H160;
-    readonly stateRoot: H256;
-    readonly transactionsRoot: H256;
-    readonly receiptsRoot: H256;
-    readonly logsBloom: EthbloomBloom;
-    readonly difficulty: U256;
-    readonly number: U256;
-    readonly gasLimit: U256;
-    readonly gasUsed: U256;
-    readonly timestamp: u64;
-    readonly extraData: Bytes;
-    readonly mixHash: H256;
-    readonly nonce: EthereumTypesHashH64;
-  }
-
-  /** @name EthereumTypesHashH64 (659) */
-  interface EthereumTypesHashH64 extends U8aFixed {}
-
-  /** @name PalletEthereumError (664) */
-  interface PalletEthereumError extends Enum {
-    readonly isInvalidSignature: boolean;
-    readonly isPreLogExists: boolean;
-    readonly type: 'InvalidSignature' | 'PreLogExists';
-  }
-
-  /** @name PalletEvmCoderSubstrateError (665) */
-  interface PalletEvmCoderSubstrateError extends Enum {
-    readonly isOutOfGas: boolean;
-    readonly isOutOfFund: boolean;
-    readonly type: 'OutOfGas' | 'OutOfFund';
-  }
-
-  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (666) */
-  interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum {
-    readonly isDisabled: boolean;
-    readonly isUnconfirmed: boolean;
-    readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly isConfirmed: boolean;
-    readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
-    readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
-  }
-
-  /** @name PalletEvmContractHelpersSponsoringModeT (667) */
-  interface PalletEvmContractHelpersSponsoringModeT extends Enum {
-    readonly isDisabled: boolean;
-    readonly isAllowlisted: boolean;
-    readonly isGenerous: boolean;
-    readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
-  }
-
-  /** @name PalletEvmContractHelpersError (673) */
-  interface PalletEvmContractHelpersError extends Enum {
-    readonly isNoPermission: boolean;
-    readonly isNoPendingSponsor: boolean;
-    readonly isTooManyMethodsHaveSponsoredLimit: boolean;
-    readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit';
-  }
-
-  /** @name PalletEvmMigrationError (674) */
-  interface PalletEvmMigrationError extends Enum {
-    readonly isAccountNotEmpty: boolean;
-    readonly isAccountIsNotMigrating: boolean;
-    readonly isBadEvent: boolean;
-    readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent';
-  }
-
-  /** @name PalletMaintenanceError (675) */
-  type PalletMaintenanceError = Null;
-
-  /** @name PalletUtilityError (676) */
-  interface PalletUtilityError extends Enum {
-    readonly isTooManyCalls: boolean;
-    readonly type: 'TooManyCalls';
-  }
-
-  /** @name PalletTestUtilsError (677) */
-  interface PalletTestUtilsError extends Enum {
-    readonly isTestPalletDisabled: boolean;
-    readonly isTriggerRollback: boolean;
-    readonly type: 'TestPalletDisabled' | 'TriggerRollback';
-  }
-
-  /** @name SpRuntimeMultiSignature (679) */
-  interface SpRuntimeMultiSignature extends Enum {
-    readonly isEd25519: boolean;
-    readonly asEd25519: SpCoreEd25519Signature;
-    readonly isSr25519: boolean;
-    readonly asSr25519: SpCoreSr25519Signature;
-    readonly isEcdsa: boolean;
-    readonly asEcdsa: SpCoreEcdsaSignature;
-    readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
-  }
-
-  /** @name SpCoreEd25519Signature (680) */
-  interface SpCoreEd25519Signature extends U8aFixed {}
-
-  /** @name SpCoreSr25519Signature (682) */
-  interface SpCoreSr25519Signature extends U8aFixed {}
-
-  /** @name SpCoreEcdsaSignature (683) */
-  interface SpCoreEcdsaSignature extends U8aFixed {}
-
-  /** @name FrameSystemExtensionsCheckSpecVersion (686) */
-  type FrameSystemExtensionsCheckSpecVersion = Null;
-
-  /** @name FrameSystemExtensionsCheckTxVersion (687) */
-  type FrameSystemExtensionsCheckTxVersion = Null;
-
-  /** @name FrameSystemExtensionsCheckGenesis (688) */
-  type FrameSystemExtensionsCheckGenesis = Null;
-
-  /** @name FrameSystemExtensionsCheckNonce (691) */
-  interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
-
-  /** @name FrameSystemExtensionsCheckWeight (692) */
-  type FrameSystemExtensionsCheckWeight = Null;
-
-  /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (693) */
-  type OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance = Null;
-
-  /** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls (694) */
-  type OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls = Null;
-
-  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (695) */
-  interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
-
-  /** @name OpalRuntimeRuntime (696) */
-  type OpalRuntimeRuntime = Null;
-
-  /** @name PalletEthereumFakeTransactionFinalizer (697) */
-  type PalletEthereumFakeTransactionFinalizer = Null;
-
-} // declare module
deletedjs-packages/types/src/types.tsdiffbeforeafterboth
--- a/js-packages/types/src/types.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export * from './appPromotion/types.js';
-export * from './default/types.js';
-export * from './povinfo/types.js';
-export * from './unique/types.js';
deletedjs-packages/types/src/unique/definitions.tsdiffbeforeafterboth
--- a/js-packages/types/src/unique/definitions.ts
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-type RpcParam = {
-  name: string;
-  type: string;
-  isOptional?: true;
-};
-
-const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr';
-
-const collectionParam = {name: 'collection', type: 'u32'};
-const tokenParam = {name: 'tokenId', type: 'u32'};
-const propertyKeysParam = {name: 'propertyKeys', type: 'Option<Vec<String>>', isOptional: true};
-const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE});
-const atParam = {name: 'at', type: 'Hash', isOptional: true};
-
-const fun = (description: string, params: RpcParam[], type: string) => ({
-  description,
-  params: [...params, atParam],
-  type,
-});
-
-export default {
-  types: {},
-  rpc: {
-    accountTokens: fun(
-      'Get tokens owned by an account in a collection',
-      [collectionParam, crossAccountParam()],
-      'Vec<u32>',
-    ),
-    collectionTokens: fun(
-      'Get tokens contained within a collection',
-      [collectionParam],
-      'Vec<u32>',
-    ),
-    tokenExists: fun(
-      'Check if the token exists',
-      [collectionParam, tokenParam],
-      'bool',
-    ),
-
-    tokenOwner: fun(
-      'Get the token owner',
-      [collectionParam, tokenParam],
-      `Option<${CROSS_ACCOUNT_ID_TYPE}>`,
-    ),
-    topmostTokenOwner: fun(
-      'Get the topmost token owner in the hierarchy of a possibly nested token',
-      [collectionParam, tokenParam],
-      `Option<${CROSS_ACCOUNT_ID_TYPE}>`,
-    ),
-    tokenOwners: fun(
-      'Returns 10 tokens owners in no particular order',
-      [collectionParam, tokenParam],
-      `Vec<${CROSS_ACCOUNT_ID_TYPE}>`,
-    ),
-    tokenChildren: fun(
-      'Get tokens nested directly into the token',
-      [collectionParam, tokenParam],
-      'Vec<UpDataStructsTokenChild>',
-    ),
-
-    collectionProperties: fun(
-      'Get collection properties, optionally limited to the provided keys',
-      [collectionParam, propertyKeysParam],
-      'Vec<UpDataStructsProperty>',
-    ),
-    tokenProperties: fun(
-      'Get token properties, optionally limited to the provided keys',
-      [collectionParam, tokenParam, propertyKeysParam],
-      'Vec<UpDataStructsProperty>',
-    ),
-    propertyPermissions: fun(
-      'Get property permissions, optionally limited to the provided keys',
-      [collectionParam, propertyKeysParam],
-      'Vec<UpDataStructsPropertyKeyPermission>',
-    ),
-
-    constMetadata: fun(
-      'Get token constant metadata',
-      [collectionParam, tokenParam],
-      'Vec<u8>',
-    ),
-    variableMetadata: fun(
-      'Get token variable metadata',
-      [collectionParam, tokenParam],
-      'Vec<u8>',
-    ),
-
-    tokenData: fun(
-      'Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT',
-      [collectionParam, tokenParam, propertyKeysParam],
-      'UpDataStructsTokenData',
-    ),
-    totalSupply: fun(
-      'Get the amount of distinctive tokens present in a collection',
-      [collectionParam],
-      'u32',
-    ),
-
-    accountBalance: fun(
-      'Get the amount of any user tokens owned by an account',
-      [collectionParam, crossAccountParam()],
-      'u32',
-    ),
-    balance: fun(
-      'Get the amount of a specific token owned by an account',
-      [collectionParam, crossAccountParam(), tokenParam],
-      'u128',
-    ),
-    allowance: fun(
-      'Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor',
-      [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam],
-      'u128',
-    ),
-
-    adminlist: fun(
-      'Get the list of admin accounts of a collection',
-      [collectionParam],
-      'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
-    ),
-    allowlist: fun(
-      'Get the list of accounts allowed to operate within a collection',
-      [collectionParam],
-      'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
-    ),
-    allowed: fun(
-      'Check if a user is allowed to operate within a collection',
-      [collectionParam, crossAccountParam()],
-      'bool',
-    ),
-
-    lastTokenId: fun(
-      'Get the last token ID created in a collection',
-      [collectionParam],
-      'u32',
-    ),
-    collectionById: fun(
-      'Get a collection by the specified ID',
-      [collectionParam],
-      'Option<UpDataStructsRpcCollection>',
-    ),
-    collectionStats: fun(
-      'Get chain stats about collections',
-      [],
-      'UpDataStructsCollectionStats',
-    ),
-
-    nextSponsored: fun(
-      'Get the number of blocks until sponsoring a transaction is available',
-      [collectionParam, crossAccountParam(), tokenParam],
-      'Option<u64>',
-    ),
-    effectiveCollectionLimits: fun(
-      'Get effective collection limits',
-      [collectionParam],
-      'Option<UpDataStructsCollectionLimits>',
-    ),
-    totalPieces: fun(
-      'Get the total amount of pieces of an RFT',
-      [collectionParam, tokenParam],
-      'Option<u128>',
-    ),
-    allowanceForAll: fun(
-      'Tells whether the given `owner` approves the `operator`.',
-      [collectionParam, crossAccountParam('owner'), crossAccountParam('operator')],
-      'Option<bool>',
-    ),
-  },
-};
deletedjs-packages/types/src/unique/index.tsdiffbeforeafterboth
--- a/js-packages/types/src/unique/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export * from './types.js';
deletedjs-packages/types/src/unique/types.tsdiffbeforeafterboth
--- a/js-packages/types/src/unique/types.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
-/* eslint-disable */
-
-export type PHANTOM_UNIQUE = 'unique';
deletedjs-packages/types/tsconfig.jsondiffbeforeafterboth
--- a/js-packages/types/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "extends": "../tsconfig.packages.json",
-  "compilerOptions": {
-    "rootDir": "./src",
-    "outDir": "dist"
-  }
-}
\ No newline at end of file
addedjs-packages/types/types-lookup.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/types-lookup.ts
@@ -0,0 +1,5540 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+// import type lookup before we augment - in some environments
+// this is required to allow for ambient/previous definitions
+import '@polkadot/types/lookup';
+
+import type { Data } from '@polkadot/types';
+import type { BTreeMap, BTreeSet, Bytes, Compact, Enum, Null, Option, Result, Set, Struct, Text, U256, U8aFixed, Vec, bool, i64, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
+import type { ITuple } from '@polkadot/types-codec/types';
+import type { Vote } from '@polkadot/types/interfaces/elections';
+import type { AccountId32, Call, H160, H256, MultiAddress, Perbill } from '@polkadot/types/interfaces/runtime';
+import type { Event } from '@polkadot/types/interfaces/system';
+
+declare module '@polkadot/types/lookup' {
+  /** @name FrameSystemAccountInfo (3) */
+  interface FrameSystemAccountInfo extends Struct {
+    readonly nonce: u32;
+    readonly consumers: u32;
+    readonly providers: u32;
+    readonly sufficients: u32;
+    readonly data: PalletBalancesAccountData;
+  }
+
+  /** @name PalletBalancesAccountData (5) */
+  interface PalletBalancesAccountData extends Struct {
+    readonly free: u128;
+    readonly reserved: u128;
+    readonly frozen: u128;
+    readonly flags: u128;
+  }
+
+  /** @name FrameSupportDispatchPerDispatchClassWeight (8) */
+  interface FrameSupportDispatchPerDispatchClassWeight extends Struct {
+    readonly normal: SpWeightsWeightV2Weight;
+    readonly operational: SpWeightsWeightV2Weight;
+    readonly mandatory: SpWeightsWeightV2Weight;
+  }
+
+  /** @name SpWeightsWeightV2Weight (9) */
+  interface SpWeightsWeightV2Weight extends Struct {
+    readonly refTime: Compact<u64>;
+    readonly proofSize: Compact<u64>;
+  }
+
+  /** @name SpRuntimeDigest (14) */
+  interface SpRuntimeDigest extends Struct {
+    readonly logs: Vec<SpRuntimeDigestDigestItem>;
+  }
+
+  /** @name SpRuntimeDigestDigestItem (16) */
+  interface SpRuntimeDigestDigestItem extends Enum {
+    readonly isOther: boolean;
+    readonly asOther: Bytes;
+    readonly isConsensus: boolean;
+    readonly asConsensus: ITuple<[U8aFixed, Bytes]>;
+    readonly isSeal: boolean;
+    readonly asSeal: ITuple<[U8aFixed, Bytes]>;
+    readonly isPreRuntime: boolean;
+    readonly asPreRuntime: ITuple<[U8aFixed, Bytes]>;
+    readonly isRuntimeEnvironmentUpdated: boolean;
+    readonly type: 'Other' | 'Consensus' | 'Seal' | 'PreRuntime' | 'RuntimeEnvironmentUpdated';
+  }
+
+  /** @name FrameSystemEventRecord (19) */
+  interface FrameSystemEventRecord extends Struct {
+    readonly phase: FrameSystemPhase;
+    readonly event: Event;
+    readonly topics: Vec<H256>;
+  }
+
+  /** @name FrameSystemEvent (21) */
+  interface FrameSystemEvent extends Enum {
+    readonly isExtrinsicSuccess: boolean;
+    readonly asExtrinsicSuccess: {
+      readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
+    } & Struct;
+    readonly isExtrinsicFailed: boolean;
+    readonly asExtrinsicFailed: {
+      readonly dispatchError: SpRuntimeDispatchError;
+      readonly dispatchInfo: FrameSupportDispatchDispatchInfo;
+    } & Struct;
+    readonly isCodeUpdated: boolean;
+    readonly isNewAccount: boolean;
+    readonly asNewAccount: {
+      readonly account: AccountId32;
+    } & Struct;
+    readonly isKilledAccount: boolean;
+    readonly asKilledAccount: {
+      readonly account: AccountId32;
+    } & Struct;
+    readonly isRemarked: boolean;
+    readonly asRemarked: {
+      readonly sender: AccountId32;
+      readonly hash_: H256;
+    } & Struct;
+    readonly type: 'ExtrinsicSuccess' | 'ExtrinsicFailed' | 'CodeUpdated' | 'NewAccount' | 'KilledAccount' | 'Remarked';
+  }
+
+  /** @name FrameSupportDispatchDispatchInfo (22) */
+  interface FrameSupportDispatchDispatchInfo extends Struct {
+    readonly weight: SpWeightsWeightV2Weight;
+    readonly class: FrameSupportDispatchDispatchClass;
+    readonly paysFee: FrameSupportDispatchPays;
+  }
+
+  /** @name FrameSupportDispatchDispatchClass (23) */
+  interface FrameSupportDispatchDispatchClass extends Enum {
+    readonly isNormal: boolean;
+    readonly isOperational: boolean;
+    readonly isMandatory: boolean;
+    readonly type: 'Normal' | 'Operational' | 'Mandatory';
+  }
+
+  /** @name FrameSupportDispatchPays (24) */
+  interface FrameSupportDispatchPays extends Enum {
+    readonly isYes: boolean;
+    readonly isNo: boolean;
+    readonly type: 'Yes' | 'No';
+  }
+
+  /** @name SpRuntimeDispatchError (25) */
+  interface SpRuntimeDispatchError extends Enum {
+    readonly isOther: boolean;
+    readonly isCannotLookup: boolean;
+    readonly isBadOrigin: boolean;
+    readonly isModule: boolean;
+    readonly asModule: SpRuntimeModuleError;
+    readonly isConsumerRemaining: boolean;
+    readonly isNoProviders: boolean;
+    readonly isTooManyConsumers: boolean;
+    readonly isToken: boolean;
+    readonly asToken: SpRuntimeTokenError;
+    readonly isArithmetic: boolean;
+    readonly asArithmetic: SpArithmeticArithmeticError;
+    readonly isTransactional: boolean;
+    readonly asTransactional: SpRuntimeTransactionalError;
+    readonly isExhausted: boolean;
+    readonly isCorruption: boolean;
+    readonly isUnavailable: boolean;
+    readonly isRootNotAllowed: boolean;
+    readonly type: 'Other' | 'CannotLookup' | 'BadOrigin' | 'Module' | 'ConsumerRemaining' | 'NoProviders' | 'TooManyConsumers' | 'Token' | 'Arithmetic' | 'Transactional' | 'Exhausted' | 'Corruption' | 'Unavailable' | 'RootNotAllowed';
+  }
+
+  /** @name SpRuntimeModuleError (26) */
+  interface SpRuntimeModuleError extends Struct {
+    readonly index: u8;
+    readonly error: U8aFixed;
+  }
+
+  /** @name SpRuntimeTokenError (27) */
+  interface SpRuntimeTokenError extends Enum {
+    readonly isFundsUnavailable: boolean;
+    readonly isOnlyProvider: boolean;
+    readonly isBelowMinimum: boolean;
+    readonly isCannotCreate: boolean;
+    readonly isUnknownAsset: boolean;
+    readonly isFrozen: boolean;
+    readonly isUnsupported: boolean;
+    readonly isCannotCreateHold: boolean;
+    readonly isNotExpendable: boolean;
+    readonly isBlocked: boolean;
+    readonly type: 'FundsUnavailable' | 'OnlyProvider' | 'BelowMinimum' | 'CannotCreate' | 'UnknownAsset' | 'Frozen' | 'Unsupported' | 'CannotCreateHold' | 'NotExpendable' | 'Blocked';
+  }
+
+  /** @name SpArithmeticArithmeticError (28) */
+  interface SpArithmeticArithmeticError extends Enum {
+    readonly isUnderflow: boolean;
+    readonly isOverflow: boolean;
+    readonly isDivisionByZero: boolean;
+    readonly type: 'Underflow' | 'Overflow' | 'DivisionByZero';
+  }
+
+  /** @name SpRuntimeTransactionalError (29) */
+  interface SpRuntimeTransactionalError extends Enum {
+    readonly isLimitReached: boolean;
+    readonly isNoLayer: boolean;
+    readonly type: 'LimitReached' | 'NoLayer';
+  }
+
+  /** @name PalletStateTrieMigrationEvent (30) */
+  interface PalletStateTrieMigrationEvent extends Enum {
+    readonly isMigrated: boolean;
+    readonly asMigrated: {
+      readonly top: u32;
+      readonly child: u32;
+      readonly compute: PalletStateTrieMigrationMigrationCompute;
+    } & Struct;
+    readonly isSlashed: boolean;
+    readonly asSlashed: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isAutoMigrationFinished: boolean;
+    readonly isHalted: boolean;
+    readonly asHalted: {
+      readonly error: PalletStateTrieMigrationError;
+    } & Struct;
+    readonly type: 'Migrated' | 'Slashed' | 'AutoMigrationFinished' | 'Halted';
+  }
+
+  /** @name PalletStateTrieMigrationMigrationCompute (31) */
+  interface PalletStateTrieMigrationMigrationCompute extends Enum {
+    readonly isSigned: boolean;
+    readonly isAuto: boolean;
+    readonly type: 'Signed' | 'Auto';
+  }
+
+  /** @name PalletStateTrieMigrationError (32) */
+  interface PalletStateTrieMigrationError extends Enum {
+    readonly isMaxSignedLimits: boolean;
+    readonly isKeyTooLong: boolean;
+    readonly isNotEnoughFunds: boolean;
+    readonly isBadWitness: boolean;
+    readonly isSignedMigrationNotAllowed: boolean;
+    readonly isBadChildRoot: boolean;
+    readonly type: 'MaxSignedLimits' | 'KeyTooLong' | 'NotEnoughFunds' | 'BadWitness' | 'SignedMigrationNotAllowed' | 'BadChildRoot';
+  }
+
+  /** @name CumulusPalletParachainSystemEvent (33) */
+  interface CumulusPalletParachainSystemEvent extends Enum {
+    readonly isValidationFunctionStored: boolean;
+    readonly isValidationFunctionApplied: boolean;
+    readonly asValidationFunctionApplied: {
+      readonly relayChainBlockNum: u32;
+    } & Struct;
+    readonly isValidationFunctionDiscarded: boolean;
+    readonly isUpgradeAuthorized: boolean;
+    readonly asUpgradeAuthorized: {
+      readonly codeHash: H256;
+    } & Struct;
+    readonly isDownwardMessagesReceived: boolean;
+    readonly asDownwardMessagesReceived: {
+      readonly count: u32;
+    } & Struct;
+    readonly isDownwardMessagesProcessed: boolean;
+    readonly asDownwardMessagesProcessed: {
+      readonly weightUsed: SpWeightsWeightV2Weight;
+      readonly dmqHead: H256;
+    } & Struct;
+    readonly isUpwardMessageSent: boolean;
+    readonly asUpwardMessageSent: {
+      readonly messageHash: Option<U8aFixed>;
+    } & Struct;
+    readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed' | 'UpwardMessageSent';
+  }
+
+  /** @name PalletCollatorSelectionEvent (35) */
+  interface PalletCollatorSelectionEvent extends Enum {
+    readonly isInvulnerableAdded: boolean;
+    readonly asInvulnerableAdded: {
+      readonly invulnerable: AccountId32;
+    } & Struct;
+    readonly isInvulnerableRemoved: boolean;
+    readonly asInvulnerableRemoved: {
+      readonly invulnerable: AccountId32;
+    } & Struct;
+    readonly isLicenseObtained: boolean;
+    readonly asLicenseObtained: {
+      readonly accountId: AccountId32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isLicenseReleased: boolean;
+    readonly asLicenseReleased: {
+      readonly accountId: AccountId32;
+      readonly depositReturned: u128;
+    } & Struct;
+    readonly isCandidateAdded: boolean;
+    readonly asCandidateAdded: {
+      readonly accountId: AccountId32;
+    } & Struct;
+    readonly isCandidateRemoved: boolean;
+    readonly asCandidateRemoved: {
+      readonly accountId: AccountId32;
+    } & Struct;
+    readonly type: 'InvulnerableAdded' | 'InvulnerableRemoved' | 'LicenseObtained' | 'LicenseReleased' | 'CandidateAdded' | 'CandidateRemoved';
+  }
+
+  /** @name PalletSessionEvent (36) */
+  interface PalletSessionEvent extends Enum {
+    readonly isNewSession: boolean;
+    readonly asNewSession: {
+      readonly sessionIndex: u32;
+    } & Struct;
+    readonly type: 'NewSession';
+  }
+
+  /** @name PalletBalancesEvent (37) */
+  interface PalletBalancesEvent extends Enum {
+    readonly isEndowed: boolean;
+    readonly asEndowed: {
+      readonly account: AccountId32;
+      readonly freeBalance: u128;
+    } & Struct;
+    readonly isDustLost: boolean;
+    readonly asDustLost: {
+      readonly account: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isTransfer: boolean;
+    readonly asTransfer: {
+      readonly from: AccountId32;
+      readonly to: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isBalanceSet: boolean;
+    readonly asBalanceSet: {
+      readonly who: AccountId32;
+      readonly free: u128;
+    } & Struct;
+    readonly isReserved: boolean;
+    readonly asReserved: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isUnreserved: boolean;
+    readonly asUnreserved: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isReserveRepatriated: boolean;
+    readonly asReserveRepatriated: {
+      readonly from: AccountId32;
+      readonly to: AccountId32;
+      readonly amount: u128;
+      readonly destinationStatus: FrameSupportTokensMiscBalanceStatus;
+    } & Struct;
+    readonly isDeposit: boolean;
+    readonly asDeposit: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isWithdraw: boolean;
+    readonly asWithdraw: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isSlashed: boolean;
+    readonly asSlashed: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isMinted: boolean;
+    readonly asMinted: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isBurned: boolean;
+    readonly asBurned: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isSuspended: boolean;
+    readonly asSuspended: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isRestored: boolean;
+    readonly asRestored: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isUpgraded: boolean;
+    readonly asUpgraded: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly isIssued: boolean;
+    readonly asIssued: {
+      readonly amount: u128;
+    } & Struct;
+    readonly isRescinded: boolean;
+    readonly asRescinded: {
+      readonly amount: u128;
+    } & Struct;
+    readonly isLocked: boolean;
+    readonly asLocked: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isUnlocked: boolean;
+    readonly asUnlocked: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isFrozen: boolean;
+    readonly asFrozen: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isThawed: boolean;
+    readonly asThawed: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'BalanceSet' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'Deposit' | 'Withdraw' | 'Slashed' | 'Minted' | 'Burned' | 'Suspended' | 'Restored' | 'Upgraded' | 'Issued' | 'Rescinded' | 'Locked' | 'Unlocked' | 'Frozen' | 'Thawed';
+  }
+
+  /** @name FrameSupportTokensMiscBalanceStatus (38) */
+  interface FrameSupportTokensMiscBalanceStatus extends Enum {
+    readonly isFree: boolean;
+    readonly isReserved: boolean;
+    readonly type: 'Free' | 'Reserved';
+  }
+
+  /** @name PalletTransactionPaymentEvent (39) */
+  interface PalletTransactionPaymentEvent extends Enum {
+    readonly isTransactionFeePaid: boolean;
+    readonly asTransactionFeePaid: {
+      readonly who: AccountId32;
+      readonly actualFee: u128;
+      readonly tip: u128;
+    } & Struct;
+    readonly type: 'TransactionFeePaid';
+  }
+
+  /** @name PalletTreasuryEvent (40) */
+  interface PalletTreasuryEvent extends Enum {
+    readonly isProposed: boolean;
+    readonly asProposed: {
+      readonly proposalIndex: u32;
+    } & Struct;
+    readonly isSpending: boolean;
+    readonly asSpending: {
+      readonly budgetRemaining: u128;
+    } & Struct;
+    readonly isAwarded: boolean;
+    readonly asAwarded: {
+      readonly proposalIndex: u32;
+      readonly award: u128;
+      readonly account: AccountId32;
+    } & Struct;
+    readonly isRejected: boolean;
+    readonly asRejected: {
+      readonly proposalIndex: u32;
+      readonly slashed: u128;
+    } & Struct;
+    readonly isBurnt: boolean;
+    readonly asBurnt: {
+      readonly burntFunds: u128;
+    } & Struct;
+    readonly isRollover: boolean;
+    readonly asRollover: {
+      readonly rolloverBalance: u128;
+    } & Struct;
+    readonly isDeposit: boolean;
+    readonly asDeposit: {
+      readonly value: u128;
+    } & Struct;
+    readonly isSpendApproved: boolean;
+    readonly asSpendApproved: {
+      readonly proposalIndex: u32;
+      readonly amount: u128;
+      readonly beneficiary: AccountId32;
+    } & Struct;
+    readonly isUpdatedInactive: boolean;
+    readonly asUpdatedInactive: {
+      readonly reactivated: u128;
+      readonly deactivated: u128;
+    } & Struct;
+    readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit' | 'SpendApproved' | 'UpdatedInactive';
+  }
+
+  /** @name PalletSudoEvent (41) */
+  interface PalletSudoEvent extends Enum {
+    readonly isSudid: boolean;
+    readonly asSudid: {
+      readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
+    } & Struct;
+    readonly isKeyChanged: boolean;
+    readonly asKeyChanged: {
+      readonly oldSudoer: Option<AccountId32>;
+    } & Struct;
+    readonly isSudoAsDone: boolean;
+    readonly asSudoAsDone: {
+      readonly sudoResult: Result<Null, SpRuntimeDispatchError>;
+    } & Struct;
+    readonly type: 'Sudid' | 'KeyChanged' | 'SudoAsDone';
+  }
+
+  /** @name OrmlVestingModuleEvent (45) */
+  interface OrmlVestingModuleEvent extends Enum {
+    readonly isVestingScheduleAdded: boolean;
+    readonly asVestingScheduleAdded: {
+      readonly from: AccountId32;
+      readonly to: AccountId32;
+      readonly vestingSchedule: OrmlVestingVestingSchedule;
+    } & Struct;
+    readonly isClaimed: boolean;
+    readonly asClaimed: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isVestingSchedulesUpdated: boolean;
+    readonly asVestingSchedulesUpdated: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly type: 'VestingScheduleAdded' | 'Claimed' | 'VestingSchedulesUpdated';
+  }
+
+  /** @name OrmlVestingVestingSchedule (46) */
+  interface OrmlVestingVestingSchedule extends Struct {
+    readonly start: u32;
+    readonly period: u32;
+    readonly periodCount: u32;
+    readonly perPeriod: Compact<u128>;
+  }
+
+  /** @name OrmlXtokensModuleEvent (48) */
+  interface OrmlXtokensModuleEvent extends Enum {
+    readonly isTransferredMultiAssets: boolean;
+    readonly asTransferredMultiAssets: {
+      readonly sender: AccountId32;
+      readonly assets: StagingXcmV3MultiassetMultiAssets;
+      readonly fee: StagingXcmV3MultiAsset;
+      readonly dest: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly type: 'TransferredMultiAssets';
+  }
+
+  /** @name StagingXcmV3MultiassetMultiAssets (49) */
+  interface StagingXcmV3MultiassetMultiAssets extends Vec<StagingXcmV3MultiAsset> {}
+
+  /** @name StagingXcmV3MultiAsset (51) */
+  interface StagingXcmV3MultiAsset extends Struct {
+    readonly id: StagingXcmV3MultiassetAssetId;
+    readonly fun: StagingXcmV3MultiassetFungibility;
+  }
+
+  /** @name StagingXcmV3MultiassetAssetId (52) */
+  interface StagingXcmV3MultiassetAssetId extends Enum {
+    readonly isConcrete: boolean;
+    readonly asConcrete: StagingXcmV3MultiLocation;
+    readonly isAbstract: boolean;
+    readonly asAbstract: U8aFixed;
+    readonly type: 'Concrete' | 'Abstract';
+  }
+
+  /** @name StagingXcmV3MultiLocation (53) */
+  interface StagingXcmV3MultiLocation extends Struct {
+    readonly parents: u8;
+    readonly interior: StagingXcmV3Junctions;
+  }
+
+  /** @name StagingXcmV3Junctions (54) */
+  interface StagingXcmV3Junctions extends Enum {
+    readonly isHere: boolean;
+    readonly isX1: boolean;
+    readonly asX1: StagingXcmV3Junction;
+    readonly isX2: boolean;
+    readonly asX2: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction]>;
+    readonly isX3: boolean;
+    readonly asX3: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+    readonly isX4: boolean;
+    readonly asX4: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+    readonly isX5: boolean;
+    readonly asX5: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+    readonly isX6: boolean;
+    readonly asX6: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+    readonly isX7: boolean;
+    readonly asX7: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+    readonly isX8: boolean;
+    readonly asX8: ITuple<[StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction, StagingXcmV3Junction]>;
+    readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
+  }
+
+  /** @name StagingXcmV3Junction (55) */
+  interface StagingXcmV3Junction extends Enum {
+    readonly isParachain: boolean;
+    readonly asParachain: Compact<u32>;
+    readonly isAccountId32: boolean;
+    readonly asAccountId32: {
+      readonly network: Option<StagingXcmV3JunctionNetworkId>;
+      readonly id: U8aFixed;
+    } & Struct;
+    readonly isAccountIndex64: boolean;
+    readonly asAccountIndex64: {
+      readonly network: Option<StagingXcmV3JunctionNetworkId>;
+      readonly index: Compact<u64>;
+    } & Struct;
+    readonly isAccountKey20: boolean;
+    readonly asAccountKey20: {
+      readonly network: Option<StagingXcmV3JunctionNetworkId>;
+      readonly key: U8aFixed;
+    } & Struct;
+    readonly isPalletInstance: boolean;
+    readonly asPalletInstance: u8;
+    readonly isGeneralIndex: boolean;
+    readonly asGeneralIndex: Compact<u128>;
+    readonly isGeneralKey: boolean;
+    readonly asGeneralKey: {
+      readonly length: u8;
+      readonly data: U8aFixed;
+    } & Struct;
+    readonly isOnlyChild: boolean;
+    readonly isPlurality: boolean;
+    readonly asPlurality: {
+      readonly id: StagingXcmV3JunctionBodyId;
+      readonly part: StagingXcmV3JunctionBodyPart;
+    } & Struct;
+    readonly isGlobalConsensus: boolean;
+    readonly asGlobalConsensus: StagingXcmV3JunctionNetworkId;
+    readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality' | 'GlobalConsensus';
+  }
+
+  /** @name StagingXcmV3JunctionNetworkId (58) */
+  interface StagingXcmV3JunctionNetworkId extends Enum {
+    readonly isByGenesis: boolean;
+    readonly asByGenesis: U8aFixed;
+    readonly isByFork: boolean;
+    readonly asByFork: {
+      readonly blockNumber: u64;
+      readonly blockHash: U8aFixed;
+    } & Struct;
+    readonly isPolkadot: boolean;
+    readonly isKusama: boolean;
+    readonly isWestend: boolean;
+    readonly isRococo: boolean;
+    readonly isWococo: boolean;
+    readonly isEthereum: boolean;
+    readonly asEthereum: {
+      readonly chainId: Compact<u64>;
+    } & Struct;
+    readonly isBitcoinCore: boolean;
+    readonly isBitcoinCash: boolean;
+    readonly type: 'ByGenesis' | 'ByFork' | 'Polkadot' | 'Kusama' | 'Westend' | 'Rococo' | 'Wococo' | 'Ethereum' | 'BitcoinCore' | 'BitcoinCash';
+  }
+
+  /** @name StagingXcmV3JunctionBodyId (60) */
+  interface StagingXcmV3JunctionBodyId extends Enum {
+    readonly isUnit: boolean;
+    readonly isMoniker: boolean;
+    readonly asMoniker: U8aFixed;
+    readonly isIndex: boolean;
+    readonly asIndex: Compact<u32>;
+    readonly isExecutive: boolean;
+    readonly isTechnical: boolean;
+    readonly isLegislative: boolean;
+    readonly isJudicial: boolean;
+    readonly isDefense: boolean;
+    readonly isAdministration: boolean;
+    readonly isTreasury: boolean;
+    readonly type: 'Unit' | 'Moniker' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
+  }
+
+  /** @name StagingXcmV3JunctionBodyPart (61) */
+  interface StagingXcmV3JunctionBodyPart extends Enum {
+    readonly isVoice: boolean;
+    readonly isMembers: boolean;
+    readonly asMembers: {
+      readonly count: Compact<u32>;
+    } & Struct;
+    readonly isFraction: boolean;
+    readonly asFraction: {
+      readonly nom: Compact<u32>;
+      readonly denom: Compact<u32>;
+    } & Struct;
+    readonly isAtLeastProportion: boolean;
+    readonly asAtLeastProportion: {
+      readonly nom: Compact<u32>;
+      readonly denom: Compact<u32>;
+    } & Struct;
+    readonly isMoreThanProportion: boolean;
+    readonly asMoreThanProportion: {
+      readonly nom: Compact<u32>;
+      readonly denom: Compact<u32>;
+    } & Struct;
+    readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
+  }
+
+  /** @name StagingXcmV3MultiassetFungibility (62) */
+  interface StagingXcmV3MultiassetFungibility extends Enum {
+    readonly isFungible: boolean;
+    readonly asFungible: Compact<u128>;
+    readonly isNonFungible: boolean;
+    readonly asNonFungible: StagingXcmV3MultiassetAssetInstance;
+    readonly type: 'Fungible' | 'NonFungible';
+  }
+
+  /** @name StagingXcmV3MultiassetAssetInstance (63) */
+  interface StagingXcmV3MultiassetAssetInstance extends Enum {
+    readonly isUndefined: boolean;
+    readonly isIndex: boolean;
+    readonly asIndex: Compact<u128>;
+    readonly isArray4: boolean;
+    readonly asArray4: U8aFixed;
+    readonly isArray8: boolean;
+    readonly asArray8: U8aFixed;
+    readonly isArray16: boolean;
+    readonly asArray16: U8aFixed;
+    readonly isArray32: boolean;
+    readonly asArray32: U8aFixed;
+    readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32';
+  }
+
+  /** @name OrmlTokensModuleEvent (66) */
+  interface OrmlTokensModuleEvent extends Enum {
+    readonly isEndowed: boolean;
+    readonly asEndowed: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isDustLost: boolean;
+    readonly asDustLost: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isTransfer: boolean;
+    readonly asTransfer: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly from: AccountId32;
+      readonly to: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isReserved: boolean;
+    readonly asReserved: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isUnreserved: boolean;
+    readonly asUnreserved: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isReserveRepatriated: boolean;
+    readonly asReserveRepatriated: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly from: AccountId32;
+      readonly to: AccountId32;
+      readonly amount: u128;
+      readonly status: FrameSupportTokensMiscBalanceStatus;
+    } & Struct;
+    readonly isBalanceSet: boolean;
+    readonly asBalanceSet: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly free: u128;
+      readonly reserved: u128;
+    } & Struct;
+    readonly isTotalIssuanceSet: boolean;
+    readonly asTotalIssuanceSet: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: u128;
+    } & Struct;
+    readonly isWithdrawn: boolean;
+    readonly asWithdrawn: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isSlashed: boolean;
+    readonly asSlashed: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly freeAmount: u128;
+      readonly reservedAmount: u128;
+    } & Struct;
+    readonly isDeposited: boolean;
+    readonly asDeposited: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isLockSet: boolean;
+    readonly asLockSet: {
+      readonly lockId: U8aFixed;
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isLockRemoved: boolean;
+    readonly asLockRemoved: {
+      readonly lockId: U8aFixed;
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+    } & Struct;
+    readonly isLocked: boolean;
+    readonly asLocked: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isUnlocked: boolean;
+    readonly asUnlocked: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isIssued: boolean;
+    readonly asIssued: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: u128;
+    } & Struct;
+    readonly isRescinded: boolean;
+    readonly asRescinded: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: u128;
+    } & Struct;
+    readonly type: 'Endowed' | 'DustLost' | 'Transfer' | 'Reserved' | 'Unreserved' | 'ReserveRepatriated' | 'BalanceSet' | 'TotalIssuanceSet' | 'Withdrawn' | 'Slashed' | 'Deposited' | 'LockSet' | 'LockRemoved' | 'Locked' | 'Unlocked' | 'Issued' | 'Rescinded';
+  }
+
+  /** @name PalletForeignAssetsAssetId (67) */
+  interface PalletForeignAssetsAssetId extends Enum {
+    readonly isForeignAssetId: boolean;
+    readonly asForeignAssetId: u32;
+    readonly isNativeAssetId: boolean;
+    readonly asNativeAssetId: PalletForeignAssetsNativeCurrency;
+    readonly type: 'ForeignAssetId' | 'NativeAssetId';
+  }
+
+  /** @name PalletForeignAssetsNativeCurrency (68) */
+  interface PalletForeignAssetsNativeCurrency extends Enum {
+    readonly isHere: boolean;
+    readonly isParent: boolean;
+    readonly type: 'Here' | 'Parent';
+  }
+
+  /** @name PalletIdentityEvent (69) */
+  interface PalletIdentityEvent extends Enum {
+    readonly isIdentitySet: boolean;
+    readonly asIdentitySet: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly isIdentityCleared: boolean;
+    readonly asIdentityCleared: {
+      readonly who: AccountId32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isIdentityKilled: boolean;
+    readonly asIdentityKilled: {
+      readonly who: AccountId32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isIdentitiesInserted: boolean;
+    readonly asIdentitiesInserted: {
+      readonly amount: u32;
+    } & Struct;
+    readonly isIdentitiesRemoved: boolean;
+    readonly asIdentitiesRemoved: {
+      readonly amount: u32;
+    } & Struct;
+    readonly isJudgementRequested: boolean;
+    readonly asJudgementRequested: {
+      readonly who: AccountId32;
+      readonly registrarIndex: u32;
+    } & Struct;
+    readonly isJudgementUnrequested: boolean;
+    readonly asJudgementUnrequested: {
+      readonly who: AccountId32;
+      readonly registrarIndex: u32;
+    } & Struct;
+    readonly isJudgementGiven: boolean;
+    readonly asJudgementGiven: {
+      readonly target: AccountId32;
+      readonly registrarIndex: u32;
+    } & Struct;
+    readonly isRegistrarAdded: boolean;
+    readonly asRegistrarAdded: {
+      readonly registrarIndex: u32;
+    } & Struct;
+    readonly isSubIdentityAdded: boolean;
+    readonly asSubIdentityAdded: {
+      readonly sub: AccountId32;
+      readonly main: AccountId32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isSubIdentityRemoved: boolean;
+    readonly asSubIdentityRemoved: {
+      readonly sub: AccountId32;
+      readonly main: AccountId32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isSubIdentityRevoked: boolean;
+    readonly asSubIdentityRevoked: {
+      readonly sub: AccountId32;
+      readonly main: AccountId32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isSubIdentitiesInserted: boolean;
+    readonly asSubIdentitiesInserted: {
+      readonly amount: u32;
+    } & Struct;
+    readonly type: 'IdentitySet' | 'IdentityCleared' | 'IdentityKilled' | 'IdentitiesInserted' | 'IdentitiesRemoved' | 'JudgementRequested' | 'JudgementUnrequested' | 'JudgementGiven' | 'RegistrarAdded' | 'SubIdentityAdded' | 'SubIdentityRemoved' | 'SubIdentityRevoked' | 'SubIdentitiesInserted';
+  }
+
+  /** @name PalletPreimageEvent (70) */
+  interface PalletPreimageEvent extends Enum {
+    readonly isNoted: boolean;
+    readonly asNoted: {
+      readonly hash_: H256;
+    } & Struct;
+    readonly isRequested: boolean;
+    readonly asRequested: {
+      readonly hash_: H256;
+    } & Struct;
+    readonly isCleared: boolean;
+    readonly asCleared: {
+      readonly hash_: H256;
+    } & Struct;
+    readonly type: 'Noted' | 'Requested' | 'Cleared';
+  }
+
+  /** @name PalletDemocracyEvent (71) */
+  interface PalletDemocracyEvent extends Enum {
+    readonly isProposed: boolean;
+    readonly asProposed: {
+      readonly proposalIndex: u32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isTabled: boolean;
+    readonly asTabled: {
+      readonly proposalIndex: u32;
+      readonly deposit: u128;
+    } & Struct;
+    readonly isExternalTabled: boolean;
+    readonly isStarted: boolean;
+    readonly asStarted: {
+      readonly refIndex: u32;
+      readonly threshold: PalletDemocracyVoteThreshold;
+    } & Struct;
+    readonly isPassed: boolean;
+    readonly asPassed: {
+      readonly refIndex: u32;
+    } & Struct;
+    readonly isNotPassed: boolean;
+    readonly asNotPassed: {
+      readonly refIndex: u32;
+    } & Struct;
+    readonly isCancelled: boolean;
+    readonly asCancelled: {
+      readonly refIndex: u32;
+    } & Struct;
+    readonly isDelegated: boolean;
+    readonly asDelegated: {
+      readonly who: AccountId32;
+      readonly target: AccountId32;
+    } & Struct;
+    readonly isUndelegated: boolean;
+    readonly asUndelegated: {
+      readonly account: AccountId32;
+    } & Struct;
+    readonly isVetoed: boolean;
+    readonly asVetoed: {
+      readonly who: AccountId32;
+      readonly proposalHash: H256;
+      readonly until: u32;
+    } & Struct;
+    readonly isBlacklisted: boolean;
+    readonly asBlacklisted: {
+      readonly proposalHash: H256;
+    } & Struct;
+    readonly isVoted: boolean;
+    readonly asVoted: {
+      readonly voter: AccountId32;
+      readonly refIndex: u32;
+      readonly vote: PalletDemocracyVoteAccountVote;
+    } & Struct;
+    readonly isSeconded: boolean;
+    readonly asSeconded: {
+      readonly seconder: AccountId32;
+      readonly propIndex: u32;
+    } & Struct;
+    readonly isProposalCanceled: boolean;
+    readonly asProposalCanceled: {
+      readonly propIndex: u32;
+    } & Struct;
+    readonly isMetadataSet: boolean;
+    readonly asMetadataSet: {
+      readonly owner: PalletDemocracyMetadataOwner;
+      readonly hash_: H256;
+    } & Struct;
+    readonly isMetadataCleared: boolean;
+    readonly asMetadataCleared: {
+      readonly owner: PalletDemocracyMetadataOwner;
+      readonly hash_: H256;
+    } & Struct;
+    readonly isMetadataTransferred: boolean;
+    readonly asMetadataTransferred: {
+      readonly prevOwner: PalletDemocracyMetadataOwner;
+      readonly owner: PalletDemocracyMetadataOwner;
+      readonly hash_: H256;
+    } & Struct;
+    readonly type: 'Proposed' | 'Tabled' | 'ExternalTabled' | 'Started' | 'Passed' | 'NotPassed' | 'Cancelled' | 'Delegated' | 'Undelegated' | 'Vetoed' | 'Blacklisted' | 'Voted' | 'Seconded' | 'ProposalCanceled' | 'MetadataSet' | 'MetadataCleared' | 'MetadataTransferred';
+  }
+
+  /** @name PalletDemocracyVoteThreshold (72) */
+  interface PalletDemocracyVoteThreshold extends Enum {
+    readonly isSuperMajorityApprove: boolean;
+    readonly isSuperMajorityAgainst: boolean;
+    readonly isSimpleMajority: boolean;
+    readonly type: 'SuperMajorityApprove' | 'SuperMajorityAgainst' | 'SimpleMajority';
+  }
+
+  /** @name PalletDemocracyVoteAccountVote (73) */
+  interface PalletDemocracyVoteAccountVote extends Enum {
+    readonly isStandard: boolean;
+    readonly asStandard: {
+      readonly vote: Vote;
+      readonly balance: u128;
+    } & Struct;
+    readonly isSplit: boolean;
+    readonly asSplit: {
+      readonly aye: u128;
+      readonly nay: u128;
+    } & Struct;
+    readonly type: 'Standard' | 'Split';
+  }
+
+  /** @name PalletDemocracyMetadataOwner (75) */
+  interface PalletDemocracyMetadataOwner extends Enum {
+    readonly isExternal: boolean;
+    readonly isProposal: boolean;
+    readonly asProposal: u32;
+    readonly isReferendum: boolean;
+    readonly asReferendum: u32;
+    readonly type: 'External' | 'Proposal' | 'Referendum';
+  }
+
+  /** @name PalletCollectiveEvent (76) */
+  interface PalletCollectiveEvent extends Enum {
+    readonly isProposed: boolean;
+    readonly asProposed: {
+      readonly account: AccountId32;
+      readonly proposalIndex: u32;
+      readonly proposalHash: H256;
+      readonly threshold: u32;
+    } & Struct;
+    readonly isVoted: boolean;
+    readonly asVoted: {
+      readonly account: AccountId32;
+      readonly proposalHash: H256;
+      readonly voted: bool;
+      readonly yes: u32;
+      readonly no: u32;
+    } & Struct;
+    readonly isApproved: boolean;
+    readonly asApproved: {
+      readonly proposalHash: H256;
+    } & Struct;
+    readonly isDisapproved: boolean;
+    readonly asDisapproved: {
+      readonly proposalHash: H256;
+    } & Struct;
+    readonly isExecuted: boolean;
+    readonly asExecuted: {
+      readonly proposalHash: H256;
+      readonly result: Result<Null, SpRuntimeDispatchError>;
+    } & Struct;
+    readonly isMemberExecuted: boolean;
+    readonly asMemberExecuted: {
+      readonly proposalHash: H256;
+      readonly result: Result<Null, SpRuntimeDispatchError>;
+    } & Struct;
+    readonly isClosed: boolean;
+    readonly asClosed: {
+      readonly proposalHash: H256;
+      readonly yes: u32;
+      readonly no: u32;
+    } & Struct;
+    readonly type: 'Proposed' | 'Voted' | 'Approved' | 'Disapproved' | 'Executed' | 'MemberExecuted' | 'Closed';
+  }
+
+  /** @name PalletMembershipEvent (79) */
+  interface PalletMembershipEvent extends Enum {
+    readonly isMemberAdded: boolean;
+    readonly isMemberRemoved: boolean;
+    readonly isMembersSwapped: boolean;
+    readonly isMembersReset: boolean;
+    readonly isKeyChanged: boolean;
+    readonly isDummy: boolean;
+    readonly type: 'MemberAdded' | 'MemberRemoved' | 'MembersSwapped' | 'MembersReset' | 'KeyChanged' | 'Dummy';
+  }
+
+  /** @name PalletRankedCollectiveEvent (81) */
+  interface PalletRankedCollectiveEvent extends Enum {
+    readonly isMemberAdded: boolean;
+    readonly asMemberAdded: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly isRankChanged: boolean;
+    readonly asRankChanged: {
+      readonly who: AccountId32;
+      readonly rank: u16;
+    } & Struct;
+    readonly isMemberRemoved: boolean;
+    readonly asMemberRemoved: {
+      readonly who: AccountId32;
+      readonly rank: u16;
+    } & Struct;
+    readonly isVoted: boolean;
+    readonly asVoted: {
+      readonly who: AccountId32;
+      readonly poll: u32;
+      readonly vote: PalletRankedCollectiveVoteRecord;
+      readonly tally: PalletRankedCollectiveTally;
+    } & Struct;
+    readonly type: 'MemberAdded' | 'RankChanged' | 'MemberRemoved' | 'Voted';
+  }
+
+  /** @name PalletRankedCollectiveVoteRecord (83) */
+  interface PalletRankedCollectiveVoteRecord extends Enum {
+    readonly isAye: boolean;
+    readonly asAye: u32;
+    readonly isNay: boolean;
+    readonly asNay: u32;
+    readonly type: 'Aye' | 'Nay';
+  }
+
+  /** @name PalletRankedCollectiveTally (84) */
+  interface PalletRankedCollectiveTally extends Struct {
+    readonly bareAyes: u32;
+    readonly ayes: u32;
+    readonly nays: u32;
+  }
+
+  /** @name PalletReferendaEvent (85) */
+  interface PalletReferendaEvent extends Enum {
+    readonly isSubmitted: boolean;
+    readonly asSubmitted: {
+      readonly index: u32;
+      readonly track: u16;
+      readonly proposal: FrameSupportPreimagesBounded;
+    } & Struct;
+    readonly isDecisionDepositPlaced: boolean;
+    readonly asDecisionDepositPlaced: {
+      readonly index: u32;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isDecisionDepositRefunded: boolean;
+    readonly asDecisionDepositRefunded: {
+      readonly index: u32;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isDepositSlashed: boolean;
+    readonly asDepositSlashed: {
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isDecisionStarted: boolean;
+    readonly asDecisionStarted: {
+      readonly index: u32;
+      readonly track: u16;
+      readonly proposal: FrameSupportPreimagesBounded;
+      readonly tally: PalletRankedCollectiveTally;
+    } & Struct;
+    readonly isConfirmStarted: boolean;
+    readonly asConfirmStarted: {
+      readonly index: u32;
+    } & Struct;
+    readonly isConfirmAborted: boolean;
+    readonly asConfirmAborted: {
+      readonly index: u32;
+    } & Struct;
+    readonly isConfirmed: boolean;
+    readonly asConfirmed: {
+      readonly index: u32;
+      readonly tally: PalletRankedCollectiveTally;
+    } & Struct;
+    readonly isApproved: boolean;
+    readonly asApproved: {
+      readonly index: u32;
+    } & Struct;
+    readonly isRejected: boolean;
+    readonly asRejected: {
+      readonly index: u32;
+      readonly tally: PalletRankedCollectiveTally;
+    } & Struct;
+    readonly isTimedOut: boolean;
+    readonly asTimedOut: {
+      readonly index: u32;
+      readonly tally: PalletRankedCollectiveTally;
+    } & Struct;
+    readonly isCancelled: boolean;
+    readonly asCancelled: {
+      readonly index: u32;
+      readonly tally: PalletRankedCollectiveTally;
+    } & Struct;
+    readonly isKilled: boolean;
+    readonly asKilled: {
+      readonly index: u32;
+      readonly tally: PalletRankedCollectiveTally;
+    } & Struct;
+    readonly isSubmissionDepositRefunded: boolean;
+    readonly asSubmissionDepositRefunded: {
+      readonly index: u32;
+      readonly who: AccountId32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isMetadataSet: boolean;
+    readonly asMetadataSet: {
+      readonly index: u32;
+      readonly hash_: H256;
+    } & Struct;
+    readonly isMetadataCleared: boolean;
+    readonly asMetadataCleared: {
+      readonly index: u32;
+      readonly hash_: H256;
+    } & Struct;
+    readonly type: 'Submitted' | 'DecisionDepositPlaced' | 'DecisionDepositRefunded' | 'DepositSlashed' | 'DecisionStarted' | 'ConfirmStarted' | 'ConfirmAborted' | 'Confirmed' | 'Approved' | 'Rejected' | 'TimedOut' | 'Cancelled' | 'Killed' | 'SubmissionDepositRefunded' | 'MetadataSet' | 'MetadataCleared';
+  }
+
+  /** @name FrameSupportPreimagesBounded (86) */
+  interface FrameSupportPreimagesBounded extends Enum {
+    readonly isLegacy: boolean;
+    readonly asLegacy: {
+      readonly hash_: H256;
+    } & Struct;
+    readonly isInline: boolean;
+    readonly asInline: Bytes;
+    readonly isLookup: boolean;
+    readonly asLookup: {
+      readonly hash_: H256;
+      readonly len: u32;
+    } & Struct;
+    readonly type: 'Legacy' | 'Inline' | 'Lookup';
+  }
+
+  /** @name FrameSystemCall (88) */
+  interface FrameSystemCall extends Enum {
+    readonly isRemark: boolean;
+    readonly asRemark: {
+      readonly remark: Bytes;
+    } & Struct;
+    readonly isSetHeapPages: boolean;
+    readonly asSetHeapPages: {
+      readonly pages: u64;
+    } & Struct;
+    readonly isSetCode: boolean;
+    readonly asSetCode: {
+      readonly code: Bytes;
+    } & Struct;
+    readonly isSetCodeWithoutChecks: boolean;
+    readonly asSetCodeWithoutChecks: {
+      readonly code: Bytes;
+    } & Struct;
+    readonly isSetStorage: boolean;
+    readonly asSetStorage: {
+      readonly items: Vec<ITuple<[Bytes, Bytes]>>;
+    } & Struct;
+    readonly isKillStorage: boolean;
+    readonly asKillStorage: {
+      readonly keys_: Vec<Bytes>;
+    } & Struct;
+    readonly isKillPrefix: boolean;
+    readonly asKillPrefix: {
+      readonly prefix: Bytes;
+      readonly subkeys: u32;
+    } & Struct;
+    readonly isRemarkWithEvent: boolean;
+    readonly asRemarkWithEvent: {
+      readonly remark: Bytes;
+    } & Struct;
+    readonly type: 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent';
+  }
+
+  /** @name PalletStateTrieMigrationCall (92) */
+  interface PalletStateTrieMigrationCall extends Enum {
+    readonly isControlAutoMigration: boolean;
+    readonly asControlAutoMigration: {
+      readonly maybeConfig: Option<PalletStateTrieMigrationMigrationLimits>;
+    } & Struct;
+    readonly isContinueMigrate: boolean;
+    readonly asContinueMigrate: {
+      readonly limits: PalletStateTrieMigrationMigrationLimits;
+      readonly realSizeUpper: u32;
+      readonly witnessTask: PalletStateTrieMigrationMigrationTask;
+    } & Struct;
+    readonly isMigrateCustomTop: boolean;
+    readonly asMigrateCustomTop: {
+      readonly keys_: Vec<Bytes>;
+      readonly witnessSize: u32;
+    } & Struct;
+    readonly isMigrateCustomChild: boolean;
+    readonly asMigrateCustomChild: {
+      readonly root: Bytes;
+      readonly childKeys: Vec<Bytes>;
+      readonly totalSize: u32;
+    } & Struct;
+    readonly isSetSignedMaxLimits: boolean;
+    readonly asSetSignedMaxLimits: {
+      readonly limits: PalletStateTrieMigrationMigrationLimits;
+    } & Struct;
+    readonly isForceSetProgress: boolean;
+    readonly asForceSetProgress: {
+      readonly progressTop: PalletStateTrieMigrationProgress;
+      readonly progressChild: PalletStateTrieMigrationProgress;
+    } & Struct;
+    readonly type: 'ControlAutoMigration' | 'ContinueMigrate' | 'MigrateCustomTop' | 'MigrateCustomChild' | 'SetSignedMaxLimits' | 'ForceSetProgress';
+  }
+
+  /** @name PalletStateTrieMigrationMigrationLimits (94) */
+  interface PalletStateTrieMigrationMigrationLimits extends Struct {
+    readonly size_: u32;
+    readonly item: u32;
+  }
+
+  /** @name PalletStateTrieMigrationMigrationTask (95) */
+  interface PalletStateTrieMigrationMigrationTask extends Struct {
+    readonly progressTop: PalletStateTrieMigrationProgress;
+    readonly progressChild: PalletStateTrieMigrationProgress;
+    readonly size_: u32;
+    readonly topItems: u32;
+    readonly childItems: u32;
+  }
+
+  /** @name PalletStateTrieMigrationProgress (96) */
+  interface PalletStateTrieMigrationProgress extends Enum {
+    readonly isToStart: boolean;
+    readonly isLastKey: boolean;
+    readonly asLastKey: Bytes;
+    readonly isComplete: boolean;
+    readonly type: 'ToStart' | 'LastKey' | 'Complete';
+  }
+
+  /** @name CumulusPalletParachainSystemCall (98) */
+  interface CumulusPalletParachainSystemCall extends Enum {
+    readonly isSetValidationData: boolean;
+    readonly asSetValidationData: {
+      readonly data: CumulusPrimitivesParachainInherentParachainInherentData;
+    } & Struct;
+    readonly isSudoSendUpwardMessage: boolean;
+    readonly asSudoSendUpwardMessage: {
+      readonly message: Bytes;
+    } & Struct;
+    readonly isAuthorizeUpgrade: boolean;
+    readonly asAuthorizeUpgrade: {
+      readonly codeHash: H256;
+      readonly checkVersion: bool;
+    } & Struct;
+    readonly isEnactAuthorizedUpgrade: boolean;
+    readonly asEnactAuthorizedUpgrade: {
+      readonly code: Bytes;
+    } & Struct;
+    readonly type: 'SetValidationData' | 'SudoSendUpwardMessage' | 'AuthorizeUpgrade' | 'EnactAuthorizedUpgrade';
+  }
+
+  /** @name CumulusPrimitivesParachainInherentParachainInherentData (99) */
+  interface CumulusPrimitivesParachainInherentParachainInherentData extends Struct {
+    readonly validationData: PolkadotPrimitivesV5PersistedValidationData;
+    readonly relayChainState: SpTrieStorageProof;
+    readonly downwardMessages: Vec<PolkadotCorePrimitivesInboundDownwardMessage>;
+    readonly horizontalMessages: BTreeMap<u32, Vec<PolkadotCorePrimitivesInboundHrmpMessage>>;
+  }
+
+  /** @name PolkadotPrimitivesV5PersistedValidationData (100) */
+  interface PolkadotPrimitivesV5PersistedValidationData extends Struct {
+    readonly parentHead: Bytes;
+    readonly relayParentNumber: u32;
+    readonly relayParentStorageRoot: H256;
+    readonly maxPovSize: u32;
+  }
+
+  /** @name SpTrieStorageProof (102) */
+  interface SpTrieStorageProof extends Struct {
+    readonly trieNodes: BTreeSet<Bytes>;
+  }
+
+  /** @name PolkadotCorePrimitivesInboundDownwardMessage (105) */
+  interface PolkadotCorePrimitivesInboundDownwardMessage extends Struct {
+    readonly sentAt: u32;
+    readonly msg: Bytes;
+  }
+
+  /** @name PolkadotCorePrimitivesInboundHrmpMessage (109) */
+  interface PolkadotCorePrimitivesInboundHrmpMessage extends Struct {
+    readonly sentAt: u32;
+    readonly data: Bytes;
+  }
+
+  /** @name ParachainInfoCall (112) */
+  type ParachainInfoCall = Null;
+
+  /** @name PalletCollatorSelectionCall (113) */
+  interface PalletCollatorSelectionCall extends Enum {
+    readonly isAddInvulnerable: boolean;
+    readonly asAddInvulnerable: {
+      readonly new_: AccountId32;
+    } & Struct;
+    readonly isRemoveInvulnerable: boolean;
+    readonly asRemoveInvulnerable: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly isGetLicense: boolean;
+    readonly isOnboard: boolean;
+    readonly isOffboard: boolean;
+    readonly isReleaseLicense: boolean;
+    readonly isForceReleaseLicense: boolean;
+    readonly asForceReleaseLicense: {
+      readonly who: AccountId32;
+    } & Struct;
+    readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense';
+  }
+
+  /** @name PalletSessionCall (114) */
+  interface PalletSessionCall extends Enum {
+    readonly isSetKeys: boolean;
+    readonly asSetKeys: {
+      readonly keys_: OpalRuntimeRuntimeCommonSessionKeys;
+      readonly proof: Bytes;
+    } & Struct;
+    readonly isPurgeKeys: boolean;
+    readonly type: 'SetKeys' | 'PurgeKeys';
+  }
+
+  /** @name OpalRuntimeRuntimeCommonSessionKeys (115) */
+  interface OpalRuntimeRuntimeCommonSessionKeys extends Struct {
+    readonly aura: SpConsensusAuraSr25519AppSr25519Public;
+  }
+
+  /** @name SpConsensusAuraSr25519AppSr25519Public (116) */
+  interface SpConsensusAuraSr25519AppSr25519Public extends SpCoreSr25519Public {}
+
+  /** @name SpCoreSr25519Public (117) */
+  interface SpCoreSr25519Public extends U8aFixed {}
+
+  /** @name PalletBalancesCall (118) */
+  interface PalletBalancesCall extends Enum {
+    readonly isTransferAllowDeath: boolean;
+    readonly asTransferAllowDeath: {
+      readonly dest: MultiAddress;
+      readonly value: Compact<u128>;
+    } & Struct;
+    readonly isSetBalanceDeprecated: boolean;
+    readonly asSetBalanceDeprecated: {
+      readonly who: MultiAddress;
+      readonly newFree: Compact<u128>;
+      readonly oldReserved: Compact<u128>;
+    } & Struct;
+    readonly isForceTransfer: boolean;
+    readonly asForceTransfer: {
+      readonly source: MultiAddress;
+      readonly dest: MultiAddress;
+      readonly value: Compact<u128>;
+    } & Struct;
+    readonly isTransferKeepAlive: boolean;
+    readonly asTransferKeepAlive: {
+      readonly dest: MultiAddress;
+      readonly value: Compact<u128>;
+    } & Struct;
+    readonly isTransferAll: boolean;
+    readonly asTransferAll: {
+      readonly dest: MultiAddress;
+      readonly keepAlive: bool;
+    } & Struct;
+    readonly isForceUnreserve: boolean;
+    readonly asForceUnreserve: {
+      readonly who: MultiAddress;
+      readonly amount: u128;
+    } & Struct;
+    readonly isUpgradeAccounts: boolean;
+    readonly asUpgradeAccounts: {
+      readonly who: Vec<AccountId32>;
+    } & Struct;
+    readonly isTransfer: boolean;
+    readonly asTransfer: {
+      readonly dest: MultiAddress;
+      readonly value: Compact<u128>;
+    } & Struct;
+    readonly isForceSetBalance: boolean;
+    readonly asForceSetBalance: {
+      readonly who: MultiAddress;
+      readonly newFree: Compact<u128>;
+    } & Struct;
+    readonly type: 'TransferAllowDeath' | 'SetBalanceDeprecated' | 'ForceTransfer' | 'TransferKeepAlive' | 'TransferAll' | 'ForceUnreserve' | 'UpgradeAccounts' | 'Transfer' | 'ForceSetBalance';
+  }
+
+  /** @name PalletTimestampCall (122) */
+  interface PalletTimestampCall extends Enum {
+    readonly isSet: boolean;
+    readonly asSet: {
+      readonly now: Compact<u64>;
+    } & Struct;
+    readonly type: 'Set';
+  }
+
+  /** @name PalletTreasuryCall (123) */
+  interface PalletTreasuryCall extends Enum {
+    readonly isProposeSpend: boolean;
+    readonly asProposeSpend: {
+      readonly value: Compact<u128>;
+      readonly beneficiary: MultiAddress;
+    } & Struct;
+    readonly isRejectProposal: boolean;
+    readonly asRejectProposal: {
+      readonly proposalId: Compact<u32>;
+    } & Struct;
+    readonly isApproveProposal: boolean;
+    readonly asApproveProposal: {
+      readonly proposalId: Compact<u32>;
+    } & Struct;
+    readonly isSpend: boolean;
+    readonly asSpend: {
+      readonly amount: Compact<u128>;
+      readonly beneficiary: MultiAddress;
+    } & Struct;
+    readonly isRemoveApproval: boolean;
+    readonly asRemoveApproval: {
+      readonly proposalId: Compact<u32>;
+    } & Struct;
+    readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'Spend' | 'RemoveApproval';
+  }
+
+  /** @name PalletSudoCall (124) */
+  interface PalletSudoCall extends Enum {
+    readonly isSudo: boolean;
+    readonly asSudo: {
+      readonly call: Call;
+    } & Struct;
+    readonly isSudoUncheckedWeight: boolean;
+    readonly asSudoUncheckedWeight: {
+      readonly call: Call;
+      readonly weight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isSetKey: boolean;
+    readonly asSetKey: {
+      readonly new_: MultiAddress;
+    } & Struct;
+    readonly isSudoAs: boolean;
+    readonly asSudoAs: {
+      readonly who: MultiAddress;
+      readonly call: Call;
+    } & Struct;
+    readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs';
+  }
+
+  /** @name OrmlVestingModuleCall (125) */
+  interface OrmlVestingModuleCall extends Enum {
+    readonly isClaim: boolean;
+    readonly isVestedTransfer: boolean;
+    readonly asVestedTransfer: {
+      readonly dest: MultiAddress;
+      readonly schedule: OrmlVestingVestingSchedule;
+    } & Struct;
+    readonly isUpdateVestingSchedules: boolean;
+    readonly asUpdateVestingSchedules: {
+      readonly who: MultiAddress;
+      readonly vestingSchedules: Vec<OrmlVestingVestingSchedule>;
+    } & Struct;
+    readonly isClaimFor: boolean;
+    readonly asClaimFor: {
+      readonly dest: MultiAddress;
+    } & Struct;
+    readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor';
+  }
+
+  /** @name OrmlXtokensModuleCall (127) */
+  interface OrmlXtokensModuleCall extends Enum {
+    readonly isTransfer: boolean;
+    readonly asTransfer: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: u128;
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly destWeightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isTransferMultiasset: boolean;
+    readonly asTransferMultiasset: {
+      readonly asset: StagingXcmVersionedMultiAsset;
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly destWeightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isTransferWithFee: boolean;
+    readonly asTransferWithFee: {
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: u128;
+      readonly fee: u128;
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly destWeightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isTransferMultiassetWithFee: boolean;
+    readonly asTransferMultiassetWithFee: {
+      readonly asset: StagingXcmVersionedMultiAsset;
+      readonly fee: StagingXcmVersionedMultiAsset;
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly destWeightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isTransferMulticurrencies: boolean;
+    readonly asTransferMulticurrencies: {
+      readonly currencies: Vec<ITuple<[PalletForeignAssetsAssetId, u128]>>;
+      readonly feeItem: u32;
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly destWeightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isTransferMultiassets: boolean;
+    readonly asTransferMultiassets: {
+      readonly assets: StagingXcmVersionedMultiAssets;
+      readonly feeItem: u32;
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly destWeightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly type: 'Transfer' | 'TransferMultiasset' | 'TransferWithFee' | 'TransferMultiassetWithFee' | 'TransferMulticurrencies' | 'TransferMultiassets';
+  }
+
+  /** @name StagingXcmVersionedMultiLocation (128) */
+  interface StagingXcmVersionedMultiLocation extends Enum {
+    readonly isV2: boolean;
+    readonly asV2: StagingXcmV2MultiLocation;
+    readonly isV3: boolean;
+    readonly asV3: StagingXcmV3MultiLocation;
+    readonly type: 'V2' | 'V3';
+  }
+
+  /** @name StagingXcmV2MultiLocation (129) */
+  interface StagingXcmV2MultiLocation extends Struct {
+    readonly parents: u8;
+    readonly interior: StagingXcmV2MultilocationJunctions;
+  }
+
+  /** @name StagingXcmV2MultilocationJunctions (130) */
+  interface StagingXcmV2MultilocationJunctions extends Enum {
+    readonly isHere: boolean;
+    readonly isX1: boolean;
+    readonly asX1: StagingXcmV2Junction;
+    readonly isX2: boolean;
+    readonly asX2: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction]>;
+    readonly isX3: boolean;
+    readonly asX3: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+    readonly isX4: boolean;
+    readonly asX4: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+    readonly isX5: boolean;
+    readonly asX5: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+    readonly isX6: boolean;
+    readonly asX6: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+    readonly isX7: boolean;
+    readonly asX7: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+    readonly isX8: boolean;
+    readonly asX8: ITuple<[StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction, StagingXcmV2Junction]>;
+    readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8';
+  }
+
+  /** @name StagingXcmV2Junction (131) */
+  interface StagingXcmV2Junction extends Enum {
+    readonly isParachain: boolean;
+    readonly asParachain: Compact<u32>;
+    readonly isAccountId32: boolean;
+    readonly asAccountId32: {
+      readonly network: StagingXcmV2NetworkId;
+      readonly id: U8aFixed;
+    } & Struct;
+    readonly isAccountIndex64: boolean;
+    readonly asAccountIndex64: {
+      readonly network: StagingXcmV2NetworkId;
+      readonly index: Compact<u64>;
+    } & Struct;
+    readonly isAccountKey20: boolean;
+    readonly asAccountKey20: {
+      readonly network: StagingXcmV2NetworkId;
+      readonly key: U8aFixed;
+    } & Struct;
+    readonly isPalletInstance: boolean;
+    readonly asPalletInstance: u8;
+    readonly isGeneralIndex: boolean;
+    readonly asGeneralIndex: Compact<u128>;
+    readonly isGeneralKey: boolean;
+    readonly asGeneralKey: Bytes;
+    readonly isOnlyChild: boolean;
+    readonly isPlurality: boolean;
+    readonly asPlurality: {
+      readonly id: StagingXcmV2BodyId;
+      readonly part: StagingXcmV2BodyPart;
+    } & Struct;
+    readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality';
+  }
+
+  /** @name StagingXcmV2NetworkId (132) */
+  interface StagingXcmV2NetworkId extends Enum {
+    readonly isAny: boolean;
+    readonly isNamed: boolean;
+    readonly asNamed: Bytes;
+    readonly isPolkadot: boolean;
+    readonly isKusama: boolean;
+    readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama';
+  }
+
+  /** @name StagingXcmV2BodyId (134) */
+  interface StagingXcmV2BodyId extends Enum {
+    readonly isUnit: boolean;
+    readonly isNamed: boolean;
+    readonly asNamed: Bytes;
+    readonly isIndex: boolean;
+    readonly asIndex: Compact<u32>;
+    readonly isExecutive: boolean;
+    readonly isTechnical: boolean;
+    readonly isLegislative: boolean;
+    readonly isJudicial: boolean;
+    readonly isDefense: boolean;
+    readonly isAdministration: boolean;
+    readonly isTreasury: boolean;
+    readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial' | 'Defense' | 'Administration' | 'Treasury';
+  }
+
+  /** @name StagingXcmV2BodyPart (135) */
+  interface StagingXcmV2BodyPart extends Enum {
+    readonly isVoice: boolean;
+    readonly isMembers: boolean;
+    readonly asMembers: {
+      readonly count: Compact<u32>;
+    } & Struct;
+    readonly isFraction: boolean;
+    readonly asFraction: {
+      readonly nom: Compact<u32>;
+      readonly denom: Compact<u32>;
+    } & Struct;
+    readonly isAtLeastProportion: boolean;
+    readonly asAtLeastProportion: {
+      readonly nom: Compact<u32>;
+      readonly denom: Compact<u32>;
+    } & Struct;
+    readonly isMoreThanProportion: boolean;
+    readonly asMoreThanProportion: {
+      readonly nom: Compact<u32>;
+      readonly denom: Compact<u32>;
+    } & Struct;
+    readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion';
+  }
+
+  /** @name StagingXcmV3WeightLimit (136) */
+  interface StagingXcmV3WeightLimit extends Enum {
+    readonly isUnlimited: boolean;
+    readonly isLimited: boolean;
+    readonly asLimited: SpWeightsWeightV2Weight;
+    readonly type: 'Unlimited' | 'Limited';
+  }
+
+  /** @name StagingXcmVersionedMultiAsset (137) */
+  interface StagingXcmVersionedMultiAsset extends Enum {
+    readonly isV2: boolean;
+    readonly asV2: StagingXcmV2MultiAsset;
+    readonly isV3: boolean;
+    readonly asV3: StagingXcmV3MultiAsset;
+    readonly type: 'V2' | 'V3';
+  }
+
+  /** @name StagingXcmV2MultiAsset (138) */
+  interface StagingXcmV2MultiAsset extends Struct {
+    readonly id: StagingXcmV2MultiassetAssetId;
+    readonly fun: StagingXcmV2MultiassetFungibility;
+  }
+
+  /** @name StagingXcmV2MultiassetAssetId (139) */
+  interface StagingXcmV2MultiassetAssetId extends Enum {
+    readonly isConcrete: boolean;
+    readonly asConcrete: StagingXcmV2MultiLocation;
+    readonly isAbstract: boolean;
+    readonly asAbstract: Bytes;
+    readonly type: 'Concrete' | 'Abstract';
+  }
+
+  /** @name StagingXcmV2MultiassetFungibility (140) */
+  interface StagingXcmV2MultiassetFungibility extends Enum {
+    readonly isFungible: boolean;
+    readonly asFungible: Compact<u128>;
+    readonly isNonFungible: boolean;
+    readonly asNonFungible: StagingXcmV2MultiassetAssetInstance;
+    readonly type: 'Fungible' | 'NonFungible';
+  }
+
+  /** @name StagingXcmV2MultiassetAssetInstance (141) */
+  interface StagingXcmV2MultiassetAssetInstance extends Enum {
+    readonly isUndefined: boolean;
+    readonly isIndex: boolean;
+    readonly asIndex: Compact<u128>;
+    readonly isArray4: boolean;
+    readonly asArray4: U8aFixed;
+    readonly isArray8: boolean;
+    readonly asArray8: U8aFixed;
+    readonly isArray16: boolean;
+    readonly asArray16: U8aFixed;
+    readonly isArray32: boolean;
+    readonly asArray32: U8aFixed;
+    readonly isBlob: boolean;
+    readonly asBlob: Bytes;
+    readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob';
+  }
+
+  /** @name StagingXcmVersionedMultiAssets (144) */
+  interface StagingXcmVersionedMultiAssets extends Enum {
+    readonly isV2: boolean;
+    readonly asV2: StagingXcmV2MultiassetMultiAssets;
+    readonly isV3: boolean;
+    readonly asV3: StagingXcmV3MultiassetMultiAssets;
+    readonly type: 'V2' | 'V3';
+  }
+
+  /** @name StagingXcmV2MultiassetMultiAssets (145) */
+  interface StagingXcmV2MultiassetMultiAssets extends Vec<StagingXcmV2MultiAsset> {}
+
+  /** @name OrmlTokensModuleCall (147) */
+  interface OrmlTokensModuleCall extends Enum {
+    readonly isTransfer: boolean;
+    readonly asTransfer: {
+      readonly dest: MultiAddress;
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: Compact<u128>;
+    } & Struct;
+    readonly isTransferAll: boolean;
+    readonly asTransferAll: {
+      readonly dest: MultiAddress;
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly keepAlive: bool;
+    } & Struct;
+    readonly isTransferKeepAlive: boolean;
+    readonly asTransferKeepAlive: {
+      readonly dest: MultiAddress;
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: Compact<u128>;
+    } & Struct;
+    readonly isForceTransfer: boolean;
+    readonly asForceTransfer: {
+      readonly source: MultiAddress;
+      readonly dest: MultiAddress;
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly amount: Compact<u128>;
+    } & Struct;
+    readonly isSetBalance: boolean;
+    readonly asSetBalance: {
+      readonly who: MultiAddress;
+      readonly currencyId: PalletForeignAssetsAssetId;
+      readonly newFree: Compact<u128>;
+      readonly newReserved: Compact<u128>;
+    } & Struct;
+    readonly type: 'Transfer' | 'TransferAll' | 'TransferKeepAlive' | 'ForceTransfer' | 'SetBalance';
+  }
+
+  /** @name PalletIdentityCall (148) */
+  interface PalletIdentityCall extends Enum {
+    readonly isAddRegistrar: boolean;
+    readonly asAddRegistrar: {
+      readonly account: MultiAddress;
+    } & Struct;
+    readonly isSetIdentity: boolean;
+    readonly asSetIdentity: {
+      readonly info: PalletIdentityIdentityInfo;
+    } & Struct;
+    readonly isSetSubs: boolean;
+    readonly asSetSubs: {
+      readonly subs: Vec<ITuple<[AccountId32, Data]>>;
+    } & Struct;
+    readonly isClearIdentity: boolean;
+    readonly isRequestJudgement: boolean;
+    readonly asRequestJudgement: {
+      readonly regIndex: Compact<u32>;
+      readonly maxFee: Compact<u128>;
+    } & Struct;
+    readonly isCancelRequest: boolean;
+    readonly asCancelRequest: {
+      readonly regIndex: u32;
+    } & Struct;
+    readonly isSetFee: boolean;
+    readonly asSetFee: {
+      readonly index: Compact<u32>;
+      readonly fee: Compact<u128>;
+    } & Struct;
+    readonly isSetAccountId: boolean;
+    readonly asSetAccountId: {
+      readonly index: Compact<u32>;
+      readonly new_: MultiAddress;
+    } & Struct;
+    readonly isSetFields: boolean;
+    readonly asSetFields: {
+      readonly index: Compact<u32>;
+      readonly fields: PalletIdentityBitFlags;
+    } & Struct;
+    readonly isProvideJudgement: boolean;
+    readonly asProvideJudgement: {
+      readonly regIndex: Compact<u32>;
+      readonly target: MultiAddress;
+      readonly judgement: PalletIdentityJudgement;
+      readonly identity: H256;
+    } & Struct;
+    readonly isKillIdentity: boolean;
+    readonly asKillIdentity: {
+      readonly target: MultiAddress;
+    } & Struct;
+    readonly isAddSub: boolean;
+    readonly asAddSub: {
+      readonly sub: MultiAddress;
+      readonly data: Data;
+    } & Struct;
+    readonly isRenameSub: boolean;
+    readonly asRenameSub: {
+      readonly sub: MultiAddress;
+      readonly data: Data;
+    } & Struct;
+    readonly isRemoveSub: boolean;
+    readonly asRemoveSub: {
+      readonly sub: MultiAddress;
+    } & Struct;
+    readonly isQuitSub: boolean;
+    readonly isForceInsertIdentities: boolean;
+    readonly asForceInsertIdentities: {
+      readonly identities: Vec<ITuple<[AccountId32, PalletIdentityRegistration]>>;
+    } & Struct;
+    readonly isForceRemoveIdentities: boolean;
+    readonly asForceRemoveIdentities: {
+      readonly identities: Vec<AccountId32>;
+    } & Struct;
+    readonly isForceSetSubs: boolean;
+    readonly asForceSetSubs: {
+      readonly subs: Vec<ITuple<[AccountId32, ITuple<[u128, Vec<ITuple<[AccountId32, Data]>>]>]>>;
+    } & Struct;
+    readonly type: 'AddRegistrar' | 'SetIdentity' | 'SetSubs' | 'ClearIdentity' | 'RequestJudgement' | 'CancelRequest' | 'SetFee' | 'SetAccountId' | 'SetFields' | 'ProvideJudgement' | 'KillIdentity' | 'AddSub' | 'RenameSub' | 'RemoveSub' | 'QuitSub' | 'ForceInsertIdentities' | 'ForceRemoveIdentities' | 'ForceSetSubs';
+  }
+
+  /** @name PalletIdentityIdentityInfo (149) */
+  interface PalletIdentityIdentityInfo extends Struct {
+    readonly additional: Vec<ITuple<[Data, Data]>>;
+    readonly display: Data;
+    readonly legal: Data;
+    readonly web: Data;
+    readonly riot: Data;
+    readonly email: Data;
+    readonly pgpFingerprint: Option<U8aFixed>;
+    readonly image: Data;
+    readonly twitter: Data;
+  }
+
+  /** @name PalletIdentityBitFlags (185) */
+  interface PalletIdentityBitFlags extends Set {
+    readonly isDisplay: boolean;
+    readonly isLegal: boolean;
+    readonly isWeb: boolean;
+    readonly isRiot: boolean;
+    readonly isEmail: boolean;
+    readonly isPgpFingerprint: boolean;
+    readonly isImage: boolean;
+    readonly isTwitter: boolean;
+  }
+
+  /** @name PalletIdentityIdentityField (186) */
+  interface PalletIdentityIdentityField extends Enum {
+    readonly isDisplay: boolean;
+    readonly isLegal: boolean;
+    readonly isWeb: boolean;
+    readonly isRiot: boolean;
+    readonly isEmail: boolean;
+    readonly isPgpFingerprint: boolean;
+    readonly isImage: boolean;
+    readonly isTwitter: boolean;
+    readonly type: 'Display' | 'Legal' | 'Web' | 'Riot' | 'Email' | 'PgpFingerprint' | 'Image' | 'Twitter';
+  }
+
+  /** @name PalletIdentityJudgement (187) */
+  interface PalletIdentityJudgement extends Enum {
+    readonly isUnknown: boolean;
+    readonly isFeePaid: boolean;
+    readonly asFeePaid: u128;
+    readonly isReasonable: boolean;
+    readonly isKnownGood: boolean;
+    readonly isOutOfDate: boolean;
+    readonly isLowQuality: boolean;
+    readonly isErroneous: boolean;
+    readonly type: 'Unknown' | 'FeePaid' | 'Reasonable' | 'KnownGood' | 'OutOfDate' | 'LowQuality' | 'Erroneous';
+  }
+
+  /** @name PalletIdentityRegistration (190) */
+  interface PalletIdentityRegistration extends Struct {
+    readonly judgements: Vec<ITuple<[u32, PalletIdentityJudgement]>>;
+    readonly deposit: u128;
+    readonly info: PalletIdentityIdentityInfo;
+  }
+
+  /** @name PalletPreimageCall (198) */
+  interface PalletPreimageCall extends Enum {
+    readonly isNotePreimage: boolean;
+    readonly asNotePreimage: {
+      readonly bytes: Bytes;
+    } & Struct;
+    readonly isUnnotePreimage: boolean;
+    readonly asUnnotePreimage: {
+      readonly hash_: H256;
+    } & Struct;
+    readonly isRequestPreimage: boolean;
+    readonly asRequestPreimage: {
+      readonly hash_: H256;
+    } & Struct;
+    readonly isUnrequestPreimage: boolean;
+    readonly asUnrequestPreimage: {
+      readonly hash_: H256;
+    } & Struct;
+    readonly type: 'NotePreimage' | 'UnnotePreimage' | 'RequestPreimage' | 'UnrequestPreimage';
+  }
+
+  /** @name PalletDemocracyCall (199) */
+  interface PalletDemocracyCall extends Enum {
+    readonly isPropose: boolean;
+    readonly asPropose: {
+      readonly proposal: FrameSupportPreimagesBounded;
+      readonly value: Compact<u128>;
+    } & Struct;
+    readonly isSecond: boolean;
+    readonly asSecond: {
+      readonly proposal: Compact<u32>;
+    } & Struct;
+    readonly isVote: boolean;
+    readonly asVote: {
+      readonly refIndex: Compact<u32>;
+      readonly vote: PalletDemocracyVoteAccountVote;
+    } & Struct;
+    readonly isEmergencyCancel: boolean;
+    readonly asEmergencyCancel: {
+      readonly refIndex: u32;
+    } & Struct;
+    readonly isExternalPropose: boolean;
+    readonly asExternalPropose: {
+      readonly proposal: FrameSupportPreimagesBounded;
+    } & Struct;
+    readonly isExternalProposeMajority: boolean;
+    readonly asExternalProposeMajority: {
+      readonly proposal: FrameSupportPreimagesBounded;
+    } & Struct;
+    readonly isExternalProposeDefault: boolean;
+    readonly asExternalProposeDefault: {
+      readonly proposal: FrameSupportPreimagesBounded;
+    } & Struct;
+    readonly isFastTrack: boolean;
+    readonly asFastTrack: {
+      readonly proposalHash: H256;
+      readonly votingPeriod: u32;
+      readonly delay: u32;
+    } & Struct;
+    readonly isVetoExternal: boolean;
+    readonly asVetoExternal: {
+      readonly proposalHash: H256;
+    } & Struct;
+    readonly isCancelReferendum: boolean;
+    readonly asCancelReferendum: {
+      readonly refIndex: Compact<u32>;
+    } & Struct;
+    readonly isDelegate: boolean;
+    readonly asDelegate: {
+      readonly to: MultiAddress;
+      readonly conviction: PalletDemocracyConviction;
+      readonly balance: u128;
+    } & Struct;
+    readonly isUndelegate: boolean;
+    readonly isClearPublicProposals: boolean;
+    readonly isUnlock: boolean;
+    readonly asUnlock: {
+      readonly target: MultiAddress;
+    } & Struct;
+    readonly isRemoveVote: boolean;
+    readonly asRemoveVote: {
+      readonly index: u32;
+    } & Struct;
+    readonly isRemoveOtherVote: boolean;
+    readonly asRemoveOtherVote: {
+      readonly target: MultiAddress;
+      readonly index: u32;
+    } & Struct;
+    readonly isBlacklist: boolean;
+    readonly asBlacklist: {
+      readonly proposalHash: H256;
+      readonly maybeRefIndex: Option<u32>;
+    } & Struct;
+    readonly isCancelProposal: boolean;
+    readonly asCancelProposal: {
+      readonly propIndex: Compact<u32>;
+    } & Struct;
+    readonly isSetMetadata: boolean;
+    readonly asSetMetadata: {
+      readonly owner: PalletDemocracyMetadataOwner;
+      readonly maybeHash: Option<H256>;
+    } & Struct;
+    readonly type: 'Propose' | 'Second' | 'Vote' | 'EmergencyCancel' | 'ExternalPropose' | 'ExternalProposeMajority' | 'ExternalProposeDefault' | 'FastTrack' | 'VetoExternal' | 'CancelReferendum' | 'Delegate' | 'Undelegate' | 'ClearPublicProposals' | 'Unlock' | 'RemoveVote' | 'RemoveOtherVote' | 'Blacklist' | 'CancelProposal' | 'SetMetadata';
+  }
+
+  /** @name PalletDemocracyConviction (200) */
+  interface PalletDemocracyConviction extends Enum {
+    readonly isNone: boolean;
+    readonly isLocked1x: boolean;
+    readonly isLocked2x: boolean;
+    readonly isLocked3x: boolean;
+    readonly isLocked4x: boolean;
+    readonly isLocked5x: boolean;
+    readonly isLocked6x: boolean;
+    readonly type: 'None' | 'Locked1x' | 'Locked2x' | 'Locked3x' | 'Locked4x' | 'Locked5x' | 'Locked6x';
+  }
+
+  /** @name PalletCollectiveCall (203) */
+  interface PalletCollectiveCall extends Enum {
+    readonly isSetMembers: boolean;
+    readonly asSetMembers: {
+      readonly newMembers: Vec<AccountId32>;
+      readonly prime: Option<AccountId32>;
+      readonly oldCount: u32;
+    } & Struct;
+    readonly isExecute: boolean;
+    readonly asExecute: {
+      readonly proposal: Call;
+      readonly lengthBound: Compact<u32>;
+    } & Struct;
+    readonly isPropose: boolean;
+    readonly asPropose: {
+      readonly threshold: Compact<u32>;
+      readonly proposal: Call;
+      readonly lengthBound: Compact<u32>;
+    } & Struct;
+    readonly isVote: boolean;
+    readonly asVote: {
+      readonly proposal: H256;
+      readonly index: Compact<u32>;
+      readonly approve: bool;
+    } & Struct;
+    readonly isDisapproveProposal: boolean;
+    readonly asDisapproveProposal: {
+      readonly proposalHash: H256;
+    } & Struct;
+    readonly isClose: boolean;
+    readonly asClose: {
+      readonly proposalHash: H256;
+      readonly index: Compact<u32>;
+      readonly proposalWeightBound: SpWeightsWeightV2Weight;
+      readonly lengthBound: Compact<u32>;
+    } & Struct;
+    readonly type: 'SetMembers' | 'Execute' | 'Propose' | 'Vote' | 'DisapproveProposal' | 'Close';
+  }
+
+  /** @name PalletMembershipCall (205) */
+  interface PalletMembershipCall extends Enum {
+    readonly isAddMember: boolean;
+    readonly asAddMember: {
+      readonly who: MultiAddress;
+    } & Struct;
+    readonly isRemoveMember: boolean;
+    readonly asRemoveMember: {
+      readonly who: MultiAddress;
+    } & Struct;
+    readonly isSwapMember: boolean;
+    readonly asSwapMember: {
+      readonly remove: MultiAddress;
+      readonly add: MultiAddress;
+    } & Struct;
+    readonly isResetMembers: boolean;
+    readonly asResetMembers: {
+      readonly members: Vec<AccountId32>;
+    } & Struct;
+    readonly isChangeKey: boolean;
+    readonly asChangeKey: {
+      readonly new_: MultiAddress;
+    } & Struct;
+    readonly isSetPrime: boolean;
+    readonly asSetPrime: {
+      readonly who: MultiAddress;
+    } & Struct;
+    readonly isClearPrime: boolean;
+    readonly type: 'AddMember' | 'RemoveMember' | 'SwapMember' | 'ResetMembers' | 'ChangeKey' | 'SetPrime' | 'ClearPrime';
+  }
+
+  /** @name PalletRankedCollectiveCall (207) */
+  interface PalletRankedCollectiveCall extends Enum {
+    readonly isAddMember: boolean;
+    readonly asAddMember: {
+      readonly who: MultiAddress;
+    } & Struct;
+    readonly isPromoteMember: boolean;
+    readonly asPromoteMember: {
+      readonly who: MultiAddress;
+    } & Struct;
+    readonly isDemoteMember: boolean;
+    readonly asDemoteMember: {
+      readonly who: MultiAddress;
+    } & Struct;
+    readonly isRemoveMember: boolean;
+    readonly asRemoveMember: {
+      readonly who: MultiAddress;
+      readonly minRank: u16;
+    } & Struct;
+    readonly isVote: boolean;
+    readonly asVote: {
+      readonly poll: u32;
+      readonly aye: bool;
+    } & Struct;
+    readonly isCleanupPoll: boolean;
+    readonly asCleanupPoll: {
+      readonly pollIndex: u32;
+      readonly max: u32;
+    } & Struct;
+    readonly type: 'AddMember' | 'PromoteMember' | 'DemoteMember' | 'RemoveMember' | 'Vote' | 'CleanupPoll';
+  }
+
+  /** @name PalletReferendaCall (208) */
+  interface PalletReferendaCall extends Enum {
+    readonly isSubmit: boolean;
+    readonly asSubmit: {
+      readonly proposalOrigin: OpalRuntimeOriginCaller;
+      readonly proposal: FrameSupportPreimagesBounded;
+      readonly enactmentMoment: FrameSupportScheduleDispatchTime;
+    } & Struct;
+    readonly isPlaceDecisionDeposit: boolean;
+    readonly asPlaceDecisionDeposit: {
+      readonly index: u32;
+    } & Struct;
+    readonly isRefundDecisionDeposit: boolean;
+    readonly asRefundDecisionDeposit: {
+      readonly index: u32;
+    } & Struct;
+    readonly isCancel: boolean;
+    readonly asCancel: {
+      readonly index: u32;
+    } & Struct;
+    readonly isKill: boolean;
+    readonly asKill: {
+      readonly index: u32;
+    } & Struct;
+    readonly isNudgeReferendum: boolean;
+    readonly asNudgeReferendum: {
+      readonly index: u32;
+    } & Struct;
+    readonly isOneFewerDeciding: boolean;
+    readonly asOneFewerDeciding: {
+      readonly track: u16;
+    } & Struct;
+    readonly isRefundSubmissionDeposit: boolean;
+    readonly asRefundSubmissionDeposit: {
+      readonly index: u32;
+    } & Struct;
+    readonly isSetMetadata: boolean;
+    readonly asSetMetadata: {
+      readonly index: u32;
+      readonly maybeHash: Option<H256>;
+    } & Struct;
+    readonly type: 'Submit' | 'PlaceDecisionDeposit' | 'RefundDecisionDeposit' | 'Cancel' | 'Kill' | 'NudgeReferendum' | 'OneFewerDeciding' | 'RefundSubmissionDeposit' | 'SetMetadata';
+  }
+
+  /** @name OpalRuntimeOriginCaller (209) */
+  interface OpalRuntimeOriginCaller extends Enum {
+    readonly isSystem: boolean;
+    readonly asSystem: FrameSupportDispatchRawOrigin;
+    readonly isVoid: boolean;
+    readonly isCouncil: boolean;
+    readonly asCouncil: PalletCollectiveRawOrigin;
+    readonly isTechnicalCommittee: boolean;
+    readonly asTechnicalCommittee: PalletCollectiveRawOrigin;
+    readonly isPolkadotXcm: boolean;
+    readonly asPolkadotXcm: PalletXcmOrigin;
+    readonly isCumulusXcm: boolean;
+    readonly asCumulusXcm: CumulusPalletXcmOrigin;
+    readonly isOrigins: boolean;
+    readonly asOrigins: PalletGovOriginsOrigin;
+    readonly isEthereum: boolean;
+    readonly asEthereum: PalletEthereumRawOrigin;
+    readonly type: 'System' | 'Void' | 'Council' | 'TechnicalCommittee' | 'PolkadotXcm' | 'CumulusXcm' | 'Origins' | 'Ethereum';
+  }
+
+  /** @name FrameSupportDispatchRawOrigin (210) */
+  interface FrameSupportDispatchRawOrigin extends Enum {
+    readonly isRoot: boolean;
+    readonly isSigned: boolean;
+    readonly asSigned: AccountId32;
+    readonly isNone: boolean;
+    readonly type: 'Root' | 'Signed' | 'None';
+  }
+
+  /** @name PalletCollectiveRawOrigin (211) */
+  interface PalletCollectiveRawOrigin extends Enum {
+    readonly isMembers: boolean;
+    readonly asMembers: ITuple<[u32, u32]>;
+    readonly isMember: boolean;
+    readonly asMember: AccountId32;
+    readonly isPhantom: boolean;
+    readonly type: 'Members' | 'Member' | 'Phantom';
+  }
+
+  /** @name PalletGovOriginsOrigin (213) */
+  interface PalletGovOriginsOrigin extends Enum {
+    readonly isFellowshipProposition: boolean;
+    readonly type: 'FellowshipProposition';
+  }
+
+  /** @name PalletXcmOrigin (214) */
+  interface PalletXcmOrigin extends Enum {
+    readonly isXcm: boolean;
+    readonly asXcm: StagingXcmV3MultiLocation;
+    readonly isResponse: boolean;
+    readonly asResponse: StagingXcmV3MultiLocation;
+    readonly type: 'Xcm' | 'Response';
+  }
+
+  /** @name CumulusPalletXcmOrigin (215) */
+  interface CumulusPalletXcmOrigin extends Enum {
+    readonly isRelay: boolean;
+    readonly isSiblingParachain: boolean;
+    readonly asSiblingParachain: u32;
+    readonly type: 'Relay' | 'SiblingParachain';
+  }
+
+  /** @name PalletEthereumRawOrigin (216) */
+  interface PalletEthereumRawOrigin extends Enum {
+    readonly isEthereumTransaction: boolean;
+    readonly asEthereumTransaction: H160;
+    readonly type: 'EthereumTransaction';
+  }
+
+  /** @name SpCoreVoid (218) */
+  type SpCoreVoid = Null;
+
+  /** @name FrameSupportScheduleDispatchTime (219) */
+  interface FrameSupportScheduleDispatchTime extends Enum {
+    readonly isAt: boolean;
+    readonly asAt: u32;
+    readonly isAfter: boolean;
+    readonly asAfter: u32;
+    readonly type: 'At' | 'After';
+  }
+
+  /** @name PalletSchedulerCall (220) */
+  interface PalletSchedulerCall extends Enum {
+    readonly isSchedule: boolean;
+    readonly asSchedule: {
+      readonly when: u32;
+      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+      readonly priority: u8;
+      readonly call: Call;
+    } & Struct;
+    readonly isCancel: boolean;
+    readonly asCancel: {
+      readonly when: u32;
+      readonly index: u32;
+    } & Struct;
+    readonly isScheduleNamed: boolean;
+    readonly asScheduleNamed: {
+      readonly id: U8aFixed;
+      readonly when: u32;
+      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+      readonly priority: u8;
+      readonly call: Call;
+    } & Struct;
+    readonly isCancelNamed: boolean;
+    readonly asCancelNamed: {
+      readonly id: U8aFixed;
+    } & Struct;
+    readonly isScheduleAfter: boolean;
+    readonly asScheduleAfter: {
+      readonly after: u32;
+      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+      readonly priority: u8;
+      readonly call: Call;
+    } & Struct;
+    readonly isScheduleNamedAfter: boolean;
+    readonly asScheduleNamedAfter: {
+      readonly id: U8aFixed;
+      readonly after: u32;
+      readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+      readonly priority: u8;
+      readonly call: Call;
+    } & Struct;
+    readonly type: 'Schedule' | 'Cancel' | 'ScheduleNamed' | 'CancelNamed' | 'ScheduleAfter' | 'ScheduleNamedAfter';
+  }
+
+  /** @name CumulusPalletXcmpQueueCall (223) */
+  interface CumulusPalletXcmpQueueCall extends Enum {
+    readonly isServiceOverweight: boolean;
+    readonly asServiceOverweight: {
+      readonly index: u64;
+      readonly weightLimit: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isSuspendXcmExecution: boolean;
+    readonly isResumeXcmExecution: boolean;
+    readonly isUpdateSuspendThreshold: boolean;
+    readonly asUpdateSuspendThreshold: {
+      readonly new_: u32;
+    } & Struct;
+    readonly isUpdateDropThreshold: boolean;
+    readonly asUpdateDropThreshold: {
+      readonly new_: u32;
+    } & Struct;
+    readonly isUpdateResumeThreshold: boolean;
+    readonly asUpdateResumeThreshold: {
+      readonly new_: u32;
+    } & Struct;
+    readonly isUpdateThresholdWeight: boolean;
+    readonly asUpdateThresholdWeight: {
+      readonly new_: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isUpdateWeightRestrictDecay: boolean;
+    readonly asUpdateWeightRestrictDecay: {
+      readonly new_: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isUpdateXcmpMaxIndividualWeight: boolean;
+    readonly asUpdateXcmpMaxIndividualWeight: {
+      readonly new_: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight';
+  }
+
+  /** @name PalletXcmCall (224) */
+  interface PalletXcmCall extends Enum {
+    readonly isSend: boolean;
+    readonly asSend: {
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly message: StagingXcmVersionedXcm;
+    } & Struct;
+    readonly isTeleportAssets: boolean;
+    readonly asTeleportAssets: {
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly beneficiary: StagingXcmVersionedMultiLocation;
+      readonly assets: StagingXcmVersionedMultiAssets;
+      readonly feeAssetItem: u32;
+    } & Struct;
+    readonly isReserveTransferAssets: boolean;
+    readonly asReserveTransferAssets: {
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly beneficiary: StagingXcmVersionedMultiLocation;
+      readonly assets: StagingXcmVersionedMultiAssets;
+      readonly feeAssetItem: u32;
+    } & Struct;
+    readonly isExecute: boolean;
+    readonly asExecute: {
+      readonly message: StagingXcmVersionedXcm;
+      readonly maxWeight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isForceXcmVersion: boolean;
+    readonly asForceXcmVersion: {
+      readonly location: StagingXcmV3MultiLocation;
+      readonly version: u32;
+    } & Struct;
+    readonly isForceDefaultXcmVersion: boolean;
+    readonly asForceDefaultXcmVersion: {
+      readonly maybeXcmVersion: Option<u32>;
+    } & Struct;
+    readonly isForceSubscribeVersionNotify: boolean;
+    readonly asForceSubscribeVersionNotify: {
+      readonly location: StagingXcmVersionedMultiLocation;
+    } & Struct;
+    readonly isForceUnsubscribeVersionNotify: boolean;
+    readonly asForceUnsubscribeVersionNotify: {
+      readonly location: StagingXcmVersionedMultiLocation;
+    } & Struct;
+    readonly isLimitedReserveTransferAssets: boolean;
+    readonly asLimitedReserveTransferAssets: {
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly beneficiary: StagingXcmVersionedMultiLocation;
+      readonly assets: StagingXcmVersionedMultiAssets;
+      readonly feeAssetItem: u32;
+      readonly weightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isLimitedTeleportAssets: boolean;
+    readonly asLimitedTeleportAssets: {
+      readonly dest: StagingXcmVersionedMultiLocation;
+      readonly beneficiary: StagingXcmVersionedMultiLocation;
+      readonly assets: StagingXcmVersionedMultiAssets;
+      readonly feeAssetItem: u32;
+      readonly weightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isForceSuspension: boolean;
+    readonly asForceSuspension: {
+      readonly suspended: bool;
+    } & Struct;
+    readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets' | 'ForceSuspension';
+  }
+
+  /** @name StagingXcmVersionedXcm (225) */
+  interface StagingXcmVersionedXcm extends Enum {
+    readonly isV2: boolean;
+    readonly asV2: StagingXcmV2Xcm;
+    readonly isV3: boolean;
+    readonly asV3: StagingXcmV3Xcm;
+    readonly type: 'V2' | 'V3';
+  }
+
+  /** @name StagingXcmV2Xcm (226) */
+  interface StagingXcmV2Xcm extends Vec<StagingXcmV2Instruction> {}
+
+  /** @name StagingXcmV2Instruction (228) */
+  interface StagingXcmV2Instruction extends Enum {
+    readonly isWithdrawAsset: boolean;
+    readonly asWithdrawAsset: StagingXcmV2MultiassetMultiAssets;
+    readonly isReserveAssetDeposited: boolean;
+    readonly asReserveAssetDeposited: StagingXcmV2MultiassetMultiAssets;
+    readonly isReceiveTeleportedAsset: boolean;
+    readonly asReceiveTeleportedAsset: StagingXcmV2MultiassetMultiAssets;
+    readonly isQueryResponse: boolean;
+    readonly asQueryResponse: {
+      readonly queryId: Compact<u64>;
+      readonly response: StagingXcmV2Response;
+      readonly maxWeight: Compact<u64>;
+    } & Struct;
+    readonly isTransferAsset: boolean;
+    readonly asTransferAsset: {
+      readonly assets: StagingXcmV2MultiassetMultiAssets;
+      readonly beneficiary: StagingXcmV2MultiLocation;
+    } & Struct;
+    readonly isTransferReserveAsset: boolean;
+    readonly asTransferReserveAsset: {
+      readonly assets: StagingXcmV2MultiassetMultiAssets;
+      readonly dest: StagingXcmV2MultiLocation;
+      readonly xcm: StagingXcmV2Xcm;
+    } & Struct;
+    readonly isTransact: boolean;
+    readonly asTransact: {
+      readonly originType: StagingXcmV2OriginKind;
+      readonly requireWeightAtMost: Compact<u64>;
+      readonly call: StagingXcmDoubleEncoded;
+    } & Struct;
+    readonly isHrmpNewChannelOpenRequest: boolean;
+    readonly asHrmpNewChannelOpenRequest: {
+      readonly sender: Compact<u32>;
+      readonly maxMessageSize: Compact<u32>;
+      readonly maxCapacity: Compact<u32>;
+    } & Struct;
+    readonly isHrmpChannelAccepted: boolean;
+    readonly asHrmpChannelAccepted: {
+      readonly recipient: Compact<u32>;
+    } & Struct;
+    readonly isHrmpChannelClosing: boolean;
+    readonly asHrmpChannelClosing: {
+      readonly initiator: Compact<u32>;
+      readonly sender: Compact<u32>;
+      readonly recipient: Compact<u32>;
+    } & Struct;
+    readonly isClearOrigin: boolean;
+    readonly isDescendOrigin: boolean;
+    readonly asDescendOrigin: StagingXcmV2MultilocationJunctions;
+    readonly isReportError: boolean;
+    readonly asReportError: {
+      readonly queryId: Compact<u64>;
+      readonly dest: StagingXcmV2MultiLocation;
+      readonly maxResponseWeight: Compact<u64>;
+    } & Struct;
+    readonly isDepositAsset: boolean;
+    readonly asDepositAsset: {
+      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+      readonly maxAssets: Compact<u32>;
+      readonly beneficiary: StagingXcmV2MultiLocation;
+    } & Struct;
+    readonly isDepositReserveAsset: boolean;
+    readonly asDepositReserveAsset: {
+      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+      readonly maxAssets: Compact<u32>;
+      readonly dest: StagingXcmV2MultiLocation;
+      readonly xcm: StagingXcmV2Xcm;
+    } & Struct;
+    readonly isExchangeAsset: boolean;
+    readonly asExchangeAsset: {
+      readonly give: StagingXcmV2MultiassetMultiAssetFilter;
+      readonly receive: StagingXcmV2MultiassetMultiAssets;
+    } & Struct;
+    readonly isInitiateReserveWithdraw: boolean;
+    readonly asInitiateReserveWithdraw: {
+      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+      readonly reserve: StagingXcmV2MultiLocation;
+      readonly xcm: StagingXcmV2Xcm;
+    } & Struct;
+    readonly isInitiateTeleport: boolean;
+    readonly asInitiateTeleport: {
+      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+      readonly dest: StagingXcmV2MultiLocation;
+      readonly xcm: StagingXcmV2Xcm;
+    } & Struct;
+    readonly isQueryHolding: boolean;
+    readonly asQueryHolding: {
+      readonly queryId: Compact<u64>;
+      readonly dest: StagingXcmV2MultiLocation;
+      readonly assets: StagingXcmV2MultiassetMultiAssetFilter;
+      readonly maxResponseWeight: Compact<u64>;
+    } & Struct;
+    readonly isBuyExecution: boolean;
+    readonly asBuyExecution: {
+      readonly fees: StagingXcmV2MultiAsset;
+      readonly weightLimit: StagingXcmV2WeightLimit;
+    } & Struct;
+    readonly isRefundSurplus: boolean;
+    readonly isSetErrorHandler: boolean;
+    readonly asSetErrorHandler: StagingXcmV2Xcm;
+    readonly isSetAppendix: boolean;
+    readonly asSetAppendix: StagingXcmV2Xcm;
+    readonly isClearError: boolean;
+    readonly isClaimAsset: boolean;
+    readonly asClaimAsset: {
+      readonly assets: StagingXcmV2MultiassetMultiAssets;
+      readonly ticket: StagingXcmV2MultiLocation;
+    } & Struct;
+    readonly isTrap: boolean;
+    readonly asTrap: Compact<u64>;
+    readonly isSubscribeVersion: boolean;
+    readonly asSubscribeVersion: {
+      readonly queryId: Compact<u64>;
+      readonly maxResponseWeight: Compact<u64>;
+    } & Struct;
+    readonly isUnsubscribeVersion: boolean;
+    readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion';
+  }
+
+  /** @name StagingXcmV2Response (229) */
+  interface StagingXcmV2Response extends Enum {
+    readonly isNull: boolean;
+    readonly isAssets: boolean;
+    readonly asAssets: StagingXcmV2MultiassetMultiAssets;
+    readonly isExecutionResult: boolean;
+    readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV2TraitsError]>>;
+    readonly isVersion: boolean;
+    readonly asVersion: u32;
+    readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version';
+  }
+
+  /** @name StagingXcmV2TraitsError (232) */
+  interface StagingXcmV2TraitsError extends Enum {
+    readonly isOverflow: boolean;
+    readonly isUnimplemented: boolean;
+    readonly isUntrustedReserveLocation: boolean;
+    readonly isUntrustedTeleportLocation: boolean;
+    readonly isMultiLocationFull: boolean;
+    readonly isMultiLocationNotInvertible: boolean;
+    readonly isBadOrigin: boolean;
+    readonly isInvalidLocation: boolean;
+    readonly isAssetNotFound: boolean;
+    readonly isFailedToTransactAsset: boolean;
+    readonly isNotWithdrawable: boolean;
+    readonly isLocationCannotHold: boolean;
+    readonly isExceedsMaxMessageSize: boolean;
+    readonly isDestinationUnsupported: boolean;
+    readonly isTransport: boolean;
+    readonly isUnroutable: boolean;
+    readonly isUnknownClaim: boolean;
+    readonly isFailedToDecode: boolean;
+    readonly isMaxWeightInvalid: boolean;
+    readonly isNotHoldingFees: boolean;
+    readonly isTooExpensive: boolean;
+    readonly isTrap: boolean;
+    readonly asTrap: u64;
+    readonly isUnhandledXcmVersion: boolean;
+    readonly isWeightLimitReached: boolean;
+    readonly asWeightLimitReached: u64;
+    readonly isBarrier: boolean;
+    readonly isWeightNotComputable: boolean;
+    readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable';
+  }
+
+  /** @name StagingXcmV2OriginKind (233) */
+  interface StagingXcmV2OriginKind extends Enum {
+    readonly isNative: boolean;
+    readonly isSovereignAccount: boolean;
+    readonly isSuperuser: boolean;
+    readonly isXcm: boolean;
+    readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm';
+  }
+
+  /** @name StagingXcmDoubleEncoded (234) */
+  interface StagingXcmDoubleEncoded extends Struct {
+    readonly encoded: Bytes;
+  }
+
+  /** @name StagingXcmV2MultiassetMultiAssetFilter (235) */
+  interface StagingXcmV2MultiassetMultiAssetFilter extends Enum {
+    readonly isDefinite: boolean;
+    readonly asDefinite: StagingXcmV2MultiassetMultiAssets;
+    readonly isWild: boolean;
+    readonly asWild: StagingXcmV2MultiassetWildMultiAsset;
+    readonly type: 'Definite' | 'Wild';
+  }
+
+  /** @name StagingXcmV2MultiassetWildMultiAsset (236) */
+  interface StagingXcmV2MultiassetWildMultiAsset extends Enum {
+    readonly isAll: boolean;
+    readonly isAllOf: boolean;
+    readonly asAllOf: {
+      readonly id: StagingXcmV2MultiassetAssetId;
+      readonly fun: StagingXcmV2MultiassetWildFungibility;
+    } & Struct;
+    readonly type: 'All' | 'AllOf';
+  }
+
+  /** @name StagingXcmV2MultiassetWildFungibility (237) */
+  interface StagingXcmV2MultiassetWildFungibility extends Enum {
+    readonly isFungible: boolean;
+    readonly isNonFungible: boolean;
+    readonly type: 'Fungible' | 'NonFungible';
+  }
+
+  /** @name StagingXcmV2WeightLimit (238) */
+  interface StagingXcmV2WeightLimit extends Enum {
+    readonly isUnlimited: boolean;
+    readonly isLimited: boolean;
+    readonly asLimited: Compact<u64>;
+    readonly type: 'Unlimited' | 'Limited';
+  }
+
+  /** @name StagingXcmV3Xcm (239) */
+  interface StagingXcmV3Xcm extends Vec<StagingXcmV3Instruction> {}
+
+  /** @name StagingXcmV3Instruction (241) */
+  interface StagingXcmV3Instruction extends Enum {
+    readonly isWithdrawAsset: boolean;
+    readonly asWithdrawAsset: StagingXcmV3MultiassetMultiAssets;
+    readonly isReserveAssetDeposited: boolean;
+    readonly asReserveAssetDeposited: StagingXcmV3MultiassetMultiAssets;
+    readonly isReceiveTeleportedAsset: boolean;
+    readonly asReceiveTeleportedAsset: StagingXcmV3MultiassetMultiAssets;
+    readonly isQueryResponse: boolean;
+    readonly asQueryResponse: {
+      readonly queryId: Compact<u64>;
+      readonly response: StagingXcmV3Response;
+      readonly maxWeight: SpWeightsWeightV2Weight;
+      readonly querier: Option<StagingXcmV3MultiLocation>;
+    } & Struct;
+    readonly isTransferAsset: boolean;
+    readonly asTransferAsset: {
+      readonly assets: StagingXcmV3MultiassetMultiAssets;
+      readonly beneficiary: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly isTransferReserveAsset: boolean;
+    readonly asTransferReserveAsset: {
+      readonly assets: StagingXcmV3MultiassetMultiAssets;
+      readonly dest: StagingXcmV3MultiLocation;
+      readonly xcm: StagingXcmV3Xcm;
+    } & Struct;
+    readonly isTransact: boolean;
+    readonly asTransact: {
+      readonly originKind: StagingXcmV2OriginKind;
+      readonly requireWeightAtMost: SpWeightsWeightV2Weight;
+      readonly call: StagingXcmDoubleEncoded;
+    } & Struct;
+    readonly isHrmpNewChannelOpenRequest: boolean;
+    readonly asHrmpNewChannelOpenRequest: {
+      readonly sender: Compact<u32>;
+      readonly maxMessageSize: Compact<u32>;
+      readonly maxCapacity: Compact<u32>;
+    } & Struct;
+    readonly isHrmpChannelAccepted: boolean;
+    readonly asHrmpChannelAccepted: {
+      readonly recipient: Compact<u32>;
+    } & Struct;
+    readonly isHrmpChannelClosing: boolean;
+    readonly asHrmpChannelClosing: {
+      readonly initiator: Compact<u32>;
+      readonly sender: Compact<u32>;
+      readonly recipient: Compact<u32>;
+    } & Struct;
+    readonly isClearOrigin: boolean;
+    readonly isDescendOrigin: boolean;
+    readonly asDescendOrigin: StagingXcmV3Junctions;
+    readonly isReportError: boolean;
+    readonly asReportError: StagingXcmV3QueryResponseInfo;
+    readonly isDepositAsset: boolean;
+    readonly asDepositAsset: {
+      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+      readonly beneficiary: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly isDepositReserveAsset: boolean;
+    readonly asDepositReserveAsset: {
+      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+      readonly dest: StagingXcmV3MultiLocation;
+      readonly xcm: StagingXcmV3Xcm;
+    } & Struct;
+    readonly isExchangeAsset: boolean;
+    readonly asExchangeAsset: {
+      readonly give: StagingXcmV3MultiassetMultiAssetFilter;
+      readonly want: StagingXcmV3MultiassetMultiAssets;
+      readonly maximal: bool;
+    } & Struct;
+    readonly isInitiateReserveWithdraw: boolean;
+    readonly asInitiateReserveWithdraw: {
+      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+      readonly reserve: StagingXcmV3MultiLocation;
+      readonly xcm: StagingXcmV3Xcm;
+    } & Struct;
+    readonly isInitiateTeleport: boolean;
+    readonly asInitiateTeleport: {
+      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+      readonly dest: StagingXcmV3MultiLocation;
+      readonly xcm: StagingXcmV3Xcm;
+    } & Struct;
+    readonly isReportHolding: boolean;
+    readonly asReportHolding: {
+      readonly responseInfo: StagingXcmV3QueryResponseInfo;
+      readonly assets: StagingXcmV3MultiassetMultiAssetFilter;
+    } & Struct;
+    readonly isBuyExecution: boolean;
+    readonly asBuyExecution: {
+      readonly fees: StagingXcmV3MultiAsset;
+      readonly weightLimit: StagingXcmV3WeightLimit;
+    } & Struct;
+    readonly isRefundSurplus: boolean;
+    readonly isSetErrorHandler: boolean;
+    readonly asSetErrorHandler: StagingXcmV3Xcm;
+    readonly isSetAppendix: boolean;
+    readonly asSetAppendix: StagingXcmV3Xcm;
+    readonly isClearError: boolean;
+    readonly isClaimAsset: boolean;
+    readonly asClaimAsset: {
+      readonly assets: StagingXcmV3MultiassetMultiAssets;
+      readonly ticket: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly isTrap: boolean;
+    readonly asTrap: Compact<u64>;
+    readonly isSubscribeVersion: boolean;
+    readonly asSubscribeVersion: {
+      readonly queryId: Compact<u64>;
+      readonly maxResponseWeight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isUnsubscribeVersion: boolean;
+    readonly isBurnAsset: boolean;
+    readonly asBurnAsset: StagingXcmV3MultiassetMultiAssets;
+    readonly isExpectAsset: boolean;
+    readonly asExpectAsset: StagingXcmV3MultiassetMultiAssets;
+    readonly isExpectOrigin: boolean;
+    readonly asExpectOrigin: Option<StagingXcmV3MultiLocation>;
+    readonly isExpectError: boolean;
+    readonly asExpectError: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
+    readonly isExpectTransactStatus: boolean;
+    readonly asExpectTransactStatus: StagingXcmV3MaybeErrorCode;
+    readonly isQueryPallet: boolean;
+    readonly asQueryPallet: {
+      readonly moduleName: Bytes;
+      readonly responseInfo: StagingXcmV3QueryResponseInfo;
+    } & Struct;
+    readonly isExpectPallet: boolean;
+    readonly asExpectPallet: {
+      readonly index: Compact<u32>;
+      readonly name: Bytes;
+      readonly moduleName: Bytes;
+      readonly crateMajor: Compact<u32>;
+      readonly minCrateMinor: Compact<u32>;
+    } & Struct;
+    readonly isReportTransactStatus: boolean;
+    readonly asReportTransactStatus: StagingXcmV3QueryResponseInfo;
+    readonly isClearTransactStatus: boolean;
+    readonly isUniversalOrigin: boolean;
+    readonly asUniversalOrigin: StagingXcmV3Junction;
+    readonly isExportMessage: boolean;
+    readonly asExportMessage: {
+      readonly network: StagingXcmV3JunctionNetworkId;
+      readonly destination: StagingXcmV3Junctions;
+      readonly xcm: StagingXcmV3Xcm;
+    } & Struct;
+    readonly isLockAsset: boolean;
+    readonly asLockAsset: {
+      readonly asset: StagingXcmV3MultiAsset;
+      readonly unlocker: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly isUnlockAsset: boolean;
+    readonly asUnlockAsset: {
+      readonly asset: StagingXcmV3MultiAsset;
+      readonly target: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly isNoteUnlockable: boolean;
+    readonly asNoteUnlockable: {
+      readonly asset: StagingXcmV3MultiAsset;
+      readonly owner: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly isRequestUnlock: boolean;
+    readonly asRequestUnlock: {
+      readonly asset: StagingXcmV3MultiAsset;
+      readonly locker: StagingXcmV3MultiLocation;
+    } & Struct;
+    readonly isSetFeesMode: boolean;
+    readonly asSetFeesMode: {
+      readonly jitWithdraw: bool;
+    } & Struct;
+    readonly isSetTopic: boolean;
+    readonly asSetTopic: U8aFixed;
+    readonly isClearTopic: boolean;
+    readonly isAliasOrigin: boolean;
+    readonly asAliasOrigin: StagingXcmV3MultiLocation;
+    readonly isUnpaidExecution: boolean;
+    readonly asUnpaidExecution: {
+      readonly weightLimit: StagingXcmV3WeightLimit;
+      readonly checkOrigin: Option<StagingXcmV3MultiLocation>;
+    } & Struct;
+    readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'ReportHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion' | 'BurnAsset' | 'ExpectAsset' | 'ExpectOrigin' | 'ExpectError' | 'ExpectTransactStatus' | 'QueryPallet' | 'ExpectPallet' | 'ReportTransactStatus' | 'ClearTransactStatus' | 'UniversalOrigin' | 'ExportMessage' | 'LockAsset' | 'UnlockAsset' | 'NoteUnlockable' | 'RequestUnlock' | 'SetFeesMode' | 'SetTopic' | 'ClearTopic' | 'AliasOrigin' | 'UnpaidExecution';
+  }
+
+  /** @name StagingXcmV3Response (242) */
+  interface StagingXcmV3Response extends Enum {
+    readonly isNull: boolean;
+    readonly isAssets: boolean;
+    readonly asAssets: StagingXcmV3MultiassetMultiAssets;
+    readonly isExecutionResult: boolean;
+    readonly asExecutionResult: Option<ITuple<[u32, StagingXcmV3TraitsError]>>;
+    readonly isVersion: boolean;
+    readonly asVersion: u32;
+    readonly isPalletsInfo: boolean;
+    readonly asPalletsInfo: Vec<StagingXcmV3PalletInfo>;
+    readonly isDispatchResult: boolean;
+    readonly asDispatchResult: StagingXcmV3MaybeErrorCode;
+    readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version' | 'PalletsInfo' | 'DispatchResult';
+  }
+
+  /** @name StagingXcmV3TraitsError (245) */
+  interface StagingXcmV3TraitsError extends Enum {
+    readonly isOverflow: boolean;
+    readonly isUnimplemented: boolean;
+    readonly isUntrustedReserveLocation: boolean;
+    readonly isUntrustedTeleportLocation: boolean;
+    readonly isLocationFull: boolean;
+    readonly isLocationNotInvertible: boolean;
+    readonly isBadOrigin: boolean;
+    readonly isInvalidLocation: boolean;
+    readonly isAssetNotFound: boolean;
+    readonly isFailedToTransactAsset: boolean;
+    readonly isNotWithdrawable: boolean;
+    readonly isLocationCannotHold: boolean;
+    readonly isExceedsMaxMessageSize: boolean;
+    readonly isDestinationUnsupported: boolean;
+    readonly isTransport: boolean;
+    readonly isUnroutable: boolean;
+    readonly isUnknownClaim: boolean;
+    readonly isFailedToDecode: boolean;
+    readonly isMaxWeightInvalid: boolean;
+    readonly isNotHoldingFees: boolean;
+    readonly isTooExpensive: boolean;
+    readonly isTrap: boolean;
+    readonly asTrap: u64;
+    readonly isExpectationFalse: boolean;
+    readonly isPalletNotFound: boolean;
+    readonly isNameMismatch: boolean;
+    readonly isVersionIncompatible: boolean;
+    readonly isHoldingWouldOverflow: boolean;
+    readonly isExportError: boolean;
+    readonly isReanchorFailed: boolean;
+    readonly isNoDeal: boolean;
+    readonly isFeesNotMet: boolean;
+    readonly isLockError: boolean;
+    readonly isNoPermission: boolean;
+    readonly isUnanchored: boolean;
+    readonly isNotDepositable: boolean;
+    readonly isUnhandledXcmVersion: boolean;
+    readonly isWeightLimitReached: boolean;
+    readonly asWeightLimitReached: SpWeightsWeightV2Weight;
+    readonly isBarrier: boolean;
+    readonly isWeightNotComputable: boolean;
+    readonly isExceedsStackLimit: boolean;
+    readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'LocationFull' | 'LocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'ExpectationFalse' | 'PalletNotFound' | 'NameMismatch' | 'VersionIncompatible' | 'HoldingWouldOverflow' | 'ExportError' | 'ReanchorFailed' | 'NoDeal' | 'FeesNotMet' | 'LockError' | 'NoPermission' | 'Unanchored' | 'NotDepositable' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable' | 'ExceedsStackLimit';
+  }
+
+  /** @name StagingXcmV3PalletInfo (247) */
+  interface StagingXcmV3PalletInfo extends Struct {
+    readonly index: Compact<u32>;
+    readonly name: Bytes;
+    readonly moduleName: Bytes;
+    readonly major: Compact<u32>;
+    readonly minor: Compact<u32>;
+    readonly patch: Compact<u32>;
+  }
+
+  /** @name StagingXcmV3MaybeErrorCode (250) */
+  interface StagingXcmV3MaybeErrorCode extends Enum {
+    readonly isSuccess: boolean;
+    readonly isError: boolean;
+    readonly asError: Bytes;
+    readonly isTruncatedError: boolean;
+    readonly asTruncatedError: Bytes;
+    readonly type: 'Success' | 'Error' | 'TruncatedError';
+  }
+
+  /** @name StagingXcmV3QueryResponseInfo (253) */
+  interface StagingXcmV3QueryResponseInfo extends Struct {
+    readonly destination: StagingXcmV3MultiLocation;
+    readonly queryId: Compact<u64>;
+    readonly maxWeight: SpWeightsWeightV2Weight;
+  }
+
+  /** @name StagingXcmV3MultiassetMultiAssetFilter (254) */
+  interface StagingXcmV3MultiassetMultiAssetFilter extends Enum {
+    readonly isDefinite: boolean;
+    readonly asDefinite: StagingXcmV3MultiassetMultiAssets;
+    readonly isWild: boolean;
+    readonly asWild: StagingXcmV3MultiassetWildMultiAsset;
+    readonly type: 'Definite' | 'Wild';
+  }
+
+  /** @name StagingXcmV3MultiassetWildMultiAsset (255) */
+  interface StagingXcmV3MultiassetWildMultiAsset extends Enum {
+    readonly isAll: boolean;
+    readonly isAllOf: boolean;
+    readonly asAllOf: {
+      readonly id: StagingXcmV3MultiassetAssetId;
+      readonly fun: StagingXcmV3MultiassetWildFungibility;
+    } & Struct;
+    readonly isAllCounted: boolean;
+    readonly asAllCounted: Compact<u32>;
+    readonly isAllOfCounted: boolean;
+    readonly asAllOfCounted: {
+      readonly id: StagingXcmV3MultiassetAssetId;
+      readonly fun: StagingXcmV3MultiassetWildFungibility;
+      readonly count: Compact<u32>;
+    } & Struct;
+    readonly type: 'All' | 'AllOf' | 'AllCounted' | 'AllOfCounted';
+  }
+
+  /** @name StagingXcmV3MultiassetWildFungibility (256) */
+  interface StagingXcmV3MultiassetWildFungibility extends Enum {
+    readonly isFungible: boolean;
+    readonly isNonFungible: boolean;
+    readonly type: 'Fungible' | 'NonFungible';
+  }
+
+  /** @name CumulusPalletXcmCall (265) */
+  type CumulusPalletXcmCall = Null;
+
+  /** @name CumulusPalletDmpQueueCall (266) */
+  interface CumulusPalletDmpQueueCall extends Enum {
+    readonly isServiceOverweight: boolean;
+    readonly asServiceOverweight: {
+      readonly index: u64;
+      readonly weightLimit: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly type: 'ServiceOverweight';
+  }
+
+  /** @name PalletInflationCall (267) */
+  interface PalletInflationCall extends Enum {
+    readonly isStartInflation: boolean;
+    readonly asStartInflation: {
+      readonly inflationStartRelayBlock: u32;
+    } & Struct;
+    readonly type: 'StartInflation';
+  }
+
+  /** @name PalletUniqueCall (268) */
+  interface PalletUniqueCall extends Enum {
+    readonly isCreateCollection: boolean;
+    readonly asCreateCollection: {
+      readonly collectionName: Vec<u16>;
+      readonly collectionDescription: Vec<u16>;
+      readonly tokenPrefix: Bytes;
+      readonly mode: UpDataStructsCollectionMode;
+    } & Struct;
+    readonly isCreateCollectionEx: boolean;
+    readonly asCreateCollectionEx: {
+      readonly data: UpDataStructsCreateCollectionData;
+    } & Struct;
+    readonly isDestroyCollection: boolean;
+    readonly asDestroyCollection: {
+      readonly collectionId: u32;
+    } & Struct;
+    readonly isAddToAllowList: boolean;
+    readonly asAddToAllowList: {
+      readonly collectionId: u32;
+      readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
+    } & Struct;
+    readonly isRemoveFromAllowList: boolean;
+    readonly asRemoveFromAllowList: {
+      readonly collectionId: u32;
+      readonly address: PalletEvmAccountBasicCrossAccountIdRepr;
+    } & Struct;
+    readonly isChangeCollectionOwner: boolean;
+    readonly asChangeCollectionOwner: {
+      readonly collectionId: u32;
+      readonly newOwner: AccountId32;
+    } & Struct;
+    readonly isAddCollectionAdmin: boolean;
+    readonly asAddCollectionAdmin: {
+      readonly collectionId: u32;
+      readonly newAdminId: PalletEvmAccountBasicCrossAccountIdRepr;
+    } & Struct;
+    readonly isRemoveCollectionAdmin: boolean;
+    readonly asRemoveCollectionAdmin: {
+      readonly collectionId: u32;
+      readonly accountId: PalletEvmAccountBasicCrossAccountIdRepr;
+    } & Struct;
+    readonly isSetCollectionSponsor: boolean;
+    readonly asSetCollectionSponsor: {
+      readonly collectionId: u32;
+      readonly newSponsor: AccountId32;
+    } & Struct;
+    readonly isConfirmSponsorship: boolean;
+    readonly asConfirmSponsorship: {
+      readonly collectionId: u32;
+    } & Struct;
+    readonly isRemoveCollectionSponsor: boolean;
+    readonly asRemoveCollectionSponsor: {
+      readonly collectionId: u32;
+    } & Struct;
+    readonly isCreateItem: boolean;
+    readonly asCreateItem: {
+      readonly collectionId: u32;
+      readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly data: UpDataStructsCreateItemData;
+    } & Struct;
+    readonly isCreateMultipleItems: boolean;
+    readonly asCreateMultipleItems: {
+      readonly collectionId: u32;
+      readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly itemsData: Vec<UpDataStructsCreateItemData>;
+    } & Struct;
+    readonly isSetCollectionProperties: boolean;
+    readonly asSetCollectionProperties: {
+      readonly collectionId: u32;
+      readonly properties: Vec<UpDataStructsProperty>;
+    } & Struct;
+    readonly isDeleteCollectionProperties: boolean;
+    readonly asDeleteCollectionProperties: {
+      readonly collectionId: u32;
+      readonly propertyKeys: Vec<Bytes>;
+    } & Struct;
+    readonly isSetTokenProperties: boolean;
+    readonly asSetTokenProperties: {
+      readonly collectionId: u32;
+      readonly tokenId: u32;
+      readonly properties: Vec<UpDataStructsProperty>;
+    } & Struct;
+    readonly isDeleteTokenProperties: boolean;
+    readonly asDeleteTokenProperties: {
+      readonly collectionId: u32;
+      readonly tokenId: u32;
+      readonly propertyKeys: Vec<Bytes>;
+    } & Struct;
+    readonly isSetTokenPropertyPermissions: boolean;
+    readonly asSetTokenPropertyPermissions: {
+      readonly collectionId: u32;
+      readonly propertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
+    } & Struct;
+    readonly isCreateMultipleItemsEx: boolean;
+    readonly asCreateMultipleItemsEx: {
+      readonly collectionId: u32;
+      readonly data: UpDataStructsCreateItemExData;
+    } & Struct;
+    readonly isSetTransfersEnabledFlag: boolean;
+    readonly asSetTransfersEnabledFlag: {
+      readonly collectionId: u32;
+      readonly value: bool;
+    } & Struct;
+    readonly isBurnItem: boolean;
+    readonly asBurnItem: {
+      readonly collectionId: u32;
+      readonly itemId: u32;
+      readonly value: u128;
+    } & Struct;
+    readonly isBurnFrom: boolean;
+    readonly asBurnFrom: {
+      readonly collectionId: u32;
+      readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly itemId: u32;
+      readonly value: u128;
+    } & Struct;
+    readonly isTransfer: boolean;
+    readonly asTransfer: {
+      readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly collectionId: u32;
+      readonly itemId: u32;
+      readonly value: u128;
+    } & Struct;
+    readonly isApprove: boolean;
+    readonly asApprove: {
+      readonly spender: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly collectionId: u32;
+      readonly itemId: u32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isApproveFrom: boolean;
+    readonly asApproveFrom: {
+      readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly to: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly collectionId: u32;
+      readonly itemId: u32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isTransferFrom: boolean;
+    readonly asTransferFrom: {
+      readonly from: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly recipient: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly collectionId: u32;
+      readonly itemId: u32;
+      readonly value: u128;
+    } & Struct;
+    readonly isSetCollectionLimits: boolean;
+    readonly asSetCollectionLimits: {
+      readonly collectionId: u32;
+      readonly newLimit: UpDataStructsCollectionLimits;
+    } & Struct;
+    readonly isSetCollectionPermissions: boolean;
+    readonly asSetCollectionPermissions: {
+      readonly collectionId: u32;
+      readonly newPermission: UpDataStructsCollectionPermissions;
+    } & Struct;
+    readonly isRepartition: boolean;
+    readonly asRepartition: {
+      readonly collectionId: u32;
+      readonly tokenId: u32;
+      readonly amount: u128;
+    } & Struct;
+    readonly isSetAllowanceForAll: boolean;
+    readonly asSetAllowanceForAll: {
+      readonly collectionId: u32;
+      readonly operator: PalletEvmAccountBasicCrossAccountIdRepr;
+      readonly approve: bool;
+    } & Struct;
+    readonly isForceRepairCollection: boolean;
+    readonly asForceRepairCollection: {
+      readonly collectionId: u32;
+    } & Struct;
+    readonly isForceRepairItem: boolean;
+    readonly asForceRepairItem: {
+      readonly collectionId: u32;
+      readonly itemId: u32;
+    } & Struct;
+    readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'ApproveFrom' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition' | 'SetAllowanceForAll' | 'ForceRepairCollection' | 'ForceRepairItem';
+  }
+
+  /** @name UpDataStructsCollectionMode (273) */
+  interface UpDataStructsCollectionMode extends Enum {
+    readonly isNft: boolean;
+    readonly isFungible: boolean;
+    readonly asFungible: u8;
+    readonly isReFungible: boolean;
+    readonly type: 'Nft' | 'Fungible' | 'ReFungible';
+  }
+
+  /** @name UpDataStructsCreateCollectionData (274) */
+  interface UpDataStructsCreateCollectionData extends Struct {
+    readonly mode: UpDataStructsCollectionMode;
+    readonly access: Option<UpDataStructsAccessMode>;
+    readonly name: Vec<u16>;
+    readonly description: Vec<u16>;
+    readonly tokenPrefix: Bytes;
+    readonly limits: Option<UpDataStructsCollectionLimits>;
+    readonly permissions: Option<UpDataStructsCollectionPermissions>;
+    readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
+    readonly properties: Vec<UpDataStructsProperty>;
+    readonly adminList: Vec<PalletEvmAccountBasicCrossAccountIdRepr>;
+    readonly pendingSponsor: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
+    readonly flags: U8aFixed;
+  }
+
+  /** @name PalletEvmAccountBasicCrossAccountIdRepr (275) */
+  interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum {
+    readonly isSubstrate: boolean;
+    readonly asSubstrate: AccountId32;
+    readonly isEthereum: boolean;
+    readonly asEthereum: H160;
+    readonly type: 'Substrate' | 'Ethereum';
+  }
+
+  /** @name UpDataStructsAccessMode (277) */
+  interface UpDataStructsAccessMode extends Enum {
+    readonly isNormal: boolean;
+    readonly isAllowList: boolean;
+    readonly type: 'Normal' | 'AllowList';
+  }
+
+  /** @name UpDataStructsCollectionLimits (279) */
+  interface UpDataStructsCollectionLimits extends Struct {
+    readonly accountTokenOwnershipLimit: Option<u32>;
+    readonly sponsoredDataSize: Option<u32>;
+    readonly sponsoredDataRateLimit: Option<UpDataStructsSponsoringRateLimit>;
+    readonly tokenLimit: Option<u32>;
+    readonly sponsorTransferTimeout: Option<u32>;
+    readonly sponsorApproveTimeout: Option<u32>;
+    readonly ownerCanTransfer: Option<bool>;
+    readonly ownerCanDestroy: Option<bool>;
+    readonly transfersEnabled: Option<bool>;
+  }
+
+  /** @name UpDataStructsSponsoringRateLimit (281) */
+  interface UpDataStructsSponsoringRateLimit extends Enum {
+    readonly isSponsoringDisabled: boolean;
+    readonly isBlocks: boolean;
+    readonly asBlocks: u32;
+    readonly type: 'SponsoringDisabled' | 'Blocks';
+  }
+
+  /** @name UpDataStructsCollectionPermissions (284) */
+  interface UpDataStructsCollectionPermissions extends Struct {
+    readonly access: Option<UpDataStructsAccessMode>;
+    readonly mintMode: Option<bool>;
+    readonly nesting: Option<UpDataStructsNestingPermissions>;
+  }
+
+  /** @name UpDataStructsNestingPermissions (286) */
+  interface UpDataStructsNestingPermissions extends Struct {
+    readonly tokenOwner: bool;
+    readonly collectionAdmin: bool;
+    readonly restricted: Option<UpDataStructsOwnerRestrictedSet>;
+  }
+
+  /** @name UpDataStructsOwnerRestrictedSet (288) */
+  interface UpDataStructsOwnerRestrictedSet extends BTreeSet<u32> {}
+
+  /** @name UpDataStructsPropertyKeyPermission (294) */
+  interface UpDataStructsPropertyKeyPermission extends Struct {
+    readonly key: Bytes;
+    readonly permission: UpDataStructsPropertyPermission;
+  }
+
+  /** @name UpDataStructsPropertyPermission (296) */
+  interface UpDataStructsPropertyPermission extends Struct {
+    readonly mutable: bool;
+    readonly collectionAdmin: bool;
+    readonly tokenOwner: bool;
+  }
+
+  /** @name UpDataStructsProperty (299) */
+  interface UpDataStructsProperty extends Struct {
+    readonly key: Bytes;
+    readonly value: Bytes;
+  }
+
+  /** @name UpDataStructsCreateItemData (304) */
+  interface UpDataStructsCreateItemData extends Enum {
+    readonly isNft: boolean;
+    readonly asNft: UpDataStructsCreateNftData;
+    readonly isFungible: boolean;
+    readonly asFungible: UpDataStructsCreateFungibleData;
+    readonly isReFungible: boolean;
+    readonly asReFungible: UpDataStructsCreateReFungibleData;
+    readonly type: 'Nft' | 'Fungible' | 'ReFungible';
+  }
+
+  /** @name UpDataStructsCreateNftData (305) */
+  interface UpDataStructsCreateNftData extends Struct {
+    readonly properties: Vec<UpDataStructsProperty>;
+  }
+
+  /** @name UpDataStructsCreateFungibleData (306) */
+  interface UpDataStructsCreateFungibleData extends Struct {
+    readonly value: u128;
+  }
+
+  /** @name UpDataStructsCreateReFungibleData (307) */
+  interface UpDataStructsCreateReFungibleData extends Struct {
+    readonly pieces: u128;
+    readonly properties: Vec<UpDataStructsProperty>;
+  }
+
+  /** @name UpDataStructsCreateItemExData (311) */
+  interface UpDataStructsCreateItemExData extends Enum {
+    readonly isNft: boolean;
+    readonly asNft: Vec<UpDataStructsCreateNftExData>;
+    readonly isFungible: boolean;
+    readonly asFungible: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
+    readonly isRefungibleMultipleItems: boolean;
+    readonly asRefungibleMultipleItems: Vec<UpDataStructsCreateRefungibleExSingleOwner>;
+    readonly isRefungibleMultipleOwners: boolean;
+    readonly asRefungibleMultipleOwners: UpDataStructsCreateRefungibleExMultipleOwners;
+    readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners';
+  }
+
+  /** @name UpDataStructsCreateNftExData (313) */
+  interface UpDataStructsCreateNftExData extends Struct {
+    readonly properties: Vec<UpDataStructsProperty>;
+    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+  }
+
+  /** @name UpDataStructsCreateRefungibleExSingleOwner (320) */
+  interface UpDataStructsCreateRefungibleExSingleOwner extends Struct {
+    readonly user: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly pieces: u128;
+    readonly properties: Vec<UpDataStructsProperty>;
+  }
+
+  /** @name UpDataStructsCreateRefungibleExMultipleOwners (322) */
+  interface UpDataStructsCreateRefungibleExMultipleOwners extends Struct {
+    readonly users: BTreeMap<PalletEvmAccountBasicCrossAccountIdRepr, u128>;
+    readonly properties: Vec<UpDataStructsProperty>;
+  }
+
+  /** @name PalletConfigurationCall (323) */
+  interface PalletConfigurationCall extends Enum {
+    readonly isSetWeightToFeeCoefficientOverride: boolean;
+    readonly asSetWeightToFeeCoefficientOverride: {
+      readonly coeff: Option<u64>;
+    } & Struct;
+    readonly isSetMinGasPriceOverride: boolean;
+    readonly asSetMinGasPriceOverride: {
+      readonly coeff: Option<u64>;
+    } & Struct;
+    readonly isSetAppPromotionConfigurationOverride: boolean;
+    readonly asSetAppPromotionConfigurationOverride: {
+      readonly configuration: PalletConfigurationAppPromotionConfiguration;
+    } & Struct;
+    readonly isSetCollatorSelectionDesiredCollators: boolean;
+    readonly asSetCollatorSelectionDesiredCollators: {
+      readonly max: Option<u32>;
+    } & Struct;
+    readonly isSetCollatorSelectionLicenseBond: boolean;
+    readonly asSetCollatorSelectionLicenseBond: {
+      readonly amount: Option<u128>;
+    } & Struct;
+    readonly isSetCollatorSelectionKickThreshold: boolean;
+    readonly asSetCollatorSelectionKickThreshold: {
+      readonly threshold: Option<u32>;
+    } & Struct;
+    readonly type: 'SetWeightToFeeCoefficientOverride' | 'SetMinGasPriceOverride' | 'SetAppPromotionConfigurationOverride' | 'SetCollatorSelectionDesiredCollators' | 'SetCollatorSelectionLicenseBond' | 'SetCollatorSelectionKickThreshold';
+  }
+
+  /** @name PalletConfigurationAppPromotionConfiguration (325) */
+  interface PalletConfigurationAppPromotionConfiguration extends Struct {
+    readonly recalculationInterval: Option<u32>;
+    readonly pendingInterval: Option<u32>;
+    readonly intervalIncome: Option<Perbill>;
+    readonly maxStakersPerCalculation: Option<u8>;
+  }
+
+  /** @name PalletStructureCall (330) */
+  type PalletStructureCall = Null;
+
+  /** @name PalletAppPromotionCall (331) */
+  interface PalletAppPromotionCall extends Enum {
+    readonly isSetAdminAddress: boolean;
+    readonly asSetAdminAddress: {
+      readonly admin: PalletEvmAccountBasicCrossAccountIdRepr;
+    } & Struct;
+    readonly isStake: boolean;
+    readonly asStake: {
+      readonly amount: u128;
+    } & Struct;
+    readonly isUnstakeAll: boolean;
+    readonly isSponsorCollection: boolean;
+    readonly asSponsorCollection: {
+      readonly collectionId: u32;
+    } & Struct;
+    readonly isStopSponsoringCollection: boolean;
+    readonly asStopSponsoringCollection: {
+      readonly collectionId: u32;
+    } & Struct;
+    readonly isSponsorContract: boolean;
+    readonly asSponsorContract: {
+      readonly contractId: H160;
+    } & Struct;
+    readonly isStopSponsoringContract: boolean;
+    readonly asStopSponsoringContract: {
+      readonly contractId: H160;
+    } & Struct;
+    readonly isPayoutStakers: boolean;
+    readonly asPayoutStakers: {
+      readonly stakersNumber: Option<u8>;
+    } & Struct;
+    readonly isUnstakePartial: boolean;
+    readonly asUnstakePartial: {
+      readonly amount: u128;
+    } & Struct;
+    readonly isForceUnstake: boolean;
+    readonly asForceUnstake: {
+      readonly pendingBlocks: Vec<u32>;
+    } & Struct;
+    readonly type: 'SetAdminAddress' | 'Stake' | 'UnstakeAll' | 'SponsorCollection' | 'StopSponsoringCollection' | 'SponsorContract' | 'StopSponsoringContract' | 'PayoutStakers' | 'UnstakePartial' | 'ForceUnstake';
+  }
+
+  /** @name PalletForeignAssetsModuleCall (333) */
+  interface PalletForeignAssetsModuleCall extends Enum {
+    readonly isRegisterForeignAsset: boolean;
+    readonly asRegisterForeignAsset: {
+      readonly owner: AccountId32;
+      readonly location: StagingXcmVersionedMultiLocation;
+      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+    } & Struct;
+    readonly isUpdateForeignAsset: boolean;
+    readonly asUpdateForeignAsset: {
+      readonly foreignAssetId: u32;
+      readonly location: StagingXcmVersionedMultiLocation;
+      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+    } & Struct;
+    readonly type: 'RegisterForeignAsset' | 'UpdateForeignAsset';
+  }
+
+  /** @name PalletForeignAssetsModuleAssetMetadata (334) */
+  interface PalletForeignAssetsModuleAssetMetadata extends Struct {
+    readonly name: Bytes;
+    readonly symbol: Bytes;
+    readonly decimals: u8;
+    readonly minimalBalance: u128;
+  }
+
+  /** @name PalletEvmCall (337) */
+  interface PalletEvmCall extends Enum {
+    readonly isWithdraw: boolean;
+    readonly asWithdraw: {
+      readonly address: H160;
+      readonly value: u128;
+    } & Struct;
+    readonly isCall: boolean;
+    readonly asCall: {
+      readonly source: H160;
+      readonly target: H160;
+      readonly input: Bytes;
+      readonly value: U256;
+      readonly gasLimit: u64;
+      readonly maxFeePerGas: U256;
+      readonly maxPriorityFeePerGas: Option<U256>;
+      readonly nonce: Option<U256>;
+      readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
+    } & Struct;
+    readonly isCreate: boolean;
+    readonly asCreate: {
+      readonly source: H160;
+      readonly init: Bytes;
+      readonly value: U256;
+      readonly gasLimit: u64;
+      readonly maxFeePerGas: U256;
+      readonly maxPriorityFeePerGas: Option<U256>;
+      readonly nonce: Option<U256>;
+      readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
+    } & Struct;
+    readonly isCreate2: boolean;
+    readonly asCreate2: {
+      readonly source: H160;
+      readonly init: Bytes;
+      readonly salt: H256;
+      readonly value: U256;
+      readonly gasLimit: u64;
+      readonly maxFeePerGas: U256;
+      readonly maxPriorityFeePerGas: Option<U256>;
+      readonly nonce: Option<U256>;
+      readonly accessList: Vec<ITuple<[H160, Vec<H256>]>>;
+    } & Struct;
+    readonly type: 'Withdraw' | 'Call' | 'Create' | 'Create2';
+  }
+
+  /** @name PalletEthereumCall (344) */
+  interface PalletEthereumCall extends Enum {
+    readonly isTransact: boolean;
+    readonly asTransact: {
+      readonly transaction: EthereumTransactionTransactionV2;
+    } & Struct;
+    readonly type: 'Transact';
+  }
+
+  /** @name EthereumTransactionTransactionV2 (345) */
+  interface EthereumTransactionTransactionV2 extends Enum {
+    readonly isLegacy: boolean;
+    readonly asLegacy: EthereumTransactionLegacyTransaction;
+    readonly isEip2930: boolean;
+    readonly asEip2930: EthereumTransactionEip2930Transaction;
+    readonly isEip1559: boolean;
+    readonly asEip1559: EthereumTransactionEip1559Transaction;
+    readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
+  }
+
+  /** @name EthereumTransactionLegacyTransaction (346) */
+  interface EthereumTransactionLegacyTransaction extends Struct {
+    readonly nonce: U256;
+    readonly gasPrice: U256;
+    readonly gasLimit: U256;
+    readonly action: EthereumTransactionTransactionAction;
+    readonly value: U256;
+    readonly input: Bytes;
+    readonly signature: EthereumTransactionTransactionSignature;
+  }
+
+  /** @name EthereumTransactionTransactionAction (347) */
+  interface EthereumTransactionTransactionAction extends Enum {
+    readonly isCall: boolean;
+    readonly asCall: H160;
+    readonly isCreate: boolean;
+    readonly type: 'Call' | 'Create';
+  }
+
+  /** @name EthereumTransactionTransactionSignature (348) */
+  interface EthereumTransactionTransactionSignature extends Struct {
+    readonly v: u64;
+    readonly r: H256;
+    readonly s: H256;
+  }
+
+  /** @name EthereumTransactionEip2930Transaction (350) */
+  interface EthereumTransactionEip2930Transaction extends Struct {
+    readonly chainId: u64;
+    readonly nonce: U256;
+    readonly gasPrice: U256;
+    readonly gasLimit: U256;
+    readonly action: EthereumTransactionTransactionAction;
+    readonly value: U256;
+    readonly input: Bytes;
+    readonly accessList: Vec<EthereumTransactionAccessListItem>;
+    readonly oddYParity: bool;
+    readonly r: H256;
+    readonly s: H256;
+  }
+
+  /** @name EthereumTransactionAccessListItem (352) */
+  interface EthereumTransactionAccessListItem extends Struct {
+    readonly address: H160;
+    readonly storageKeys: Vec<H256>;
+  }
+
+  /** @name EthereumTransactionEip1559Transaction (353) */
+  interface EthereumTransactionEip1559Transaction extends Struct {
+    readonly chainId: u64;
+    readonly nonce: U256;
+    readonly maxPriorityFeePerGas: U256;
+    readonly maxFeePerGas: U256;
+    readonly gasLimit: U256;
+    readonly action: EthereumTransactionTransactionAction;
+    readonly value: U256;
+    readonly input: Bytes;
+    readonly accessList: Vec<EthereumTransactionAccessListItem>;
+    readonly oddYParity: bool;
+    readonly r: H256;
+    readonly s: H256;
+  }
+
+  /** @name PalletEvmContractHelpersCall (354) */
+  interface PalletEvmContractHelpersCall extends Enum {
+    readonly isMigrateFromSelfSponsoring: boolean;
+    readonly asMigrateFromSelfSponsoring: {
+      readonly addresses: Vec<H160>;
+    } & Struct;
+    readonly type: 'MigrateFromSelfSponsoring';
+  }
+
+  /** @name PalletEvmMigrationCall (356) */
+  interface PalletEvmMigrationCall extends Enum {
+    readonly isBegin: boolean;
+    readonly asBegin: {
+      readonly address: H160;
+    } & Struct;
+    readonly isSetData: boolean;
+    readonly asSetData: {
+      readonly address: H160;
+      readonly data: Vec<ITuple<[H256, H256]>>;
+    } & Struct;
+    readonly isFinish: boolean;
+    readonly asFinish: {
+      readonly address: H160;
+      readonly code: Bytes;
+    } & Struct;
+    readonly isInsertEthLogs: boolean;
+    readonly asInsertEthLogs: {
+      readonly logs: Vec<EthereumLog>;
+    } & Struct;
+    readonly isInsertEvents: boolean;
+    readonly asInsertEvents: {
+      readonly events: Vec<Bytes>;
+    } & Struct;
+    readonly isRemoveRmrkData: boolean;
+    readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents' | 'RemoveRmrkData';
+  }
+
+  /** @name EthereumLog (360) */
+  interface EthereumLog extends Struct {
+    readonly address: H160;
+    readonly topics: Vec<H256>;
+    readonly data: Bytes;
+  }
+
+  /** @name PalletMaintenanceCall (361) */
+  interface PalletMaintenanceCall extends Enum {
+    readonly isEnable: boolean;
+    readonly isDisable: boolean;
+    readonly type: 'Enable' | 'Disable';
+  }
+
+  /** @name PalletUtilityCall (362) */
+  interface PalletUtilityCall extends Enum {
+    readonly isBatch: boolean;
+    readonly asBatch: {
+      readonly calls: Vec<Call>;
+    } & Struct;
+    readonly isAsDerivative: boolean;
+    readonly asAsDerivative: {
+      readonly index: u16;
+      readonly call: Call;
+    } & Struct;
+    readonly isBatchAll: boolean;
+    readonly asBatchAll: {
+      readonly calls: Vec<Call>;
+    } & Struct;
+    readonly isDispatchAs: boolean;
+    readonly asDispatchAs: {
+      readonly asOrigin: OpalRuntimeOriginCaller;
+      readonly call: Call;
+    } & Struct;
+    readonly isForceBatch: boolean;
+    readonly asForceBatch: {
+      readonly calls: Vec<Call>;
+    } & Struct;
+    readonly isWithWeight: boolean;
+    readonly asWithWeight: {
+      readonly call: Call;
+      readonly weight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly type: 'Batch' | 'AsDerivative' | 'BatchAll' | 'DispatchAs' | 'ForceBatch' | 'WithWeight';
+  }
+
+  /** @name PalletTestUtilsCall (364) */
+  interface PalletTestUtilsCall extends Enum {
+    readonly isEnable: boolean;
+    readonly isSetTestValue: boolean;
+    readonly asSetTestValue: {
+      readonly value: u32;
+    } & Struct;
+    readonly isSetTestValueAndRollback: boolean;
+    readonly asSetTestValueAndRollback: {
+      readonly value: u32;
+    } & Struct;
+    readonly isIncTestValue: boolean;
+    readonly isJustTakeFee: boolean;
+    readonly isBatchAll: boolean;
+    readonly asBatchAll: {
+      readonly calls: Vec<Call>;
+    } & Struct;
+    readonly type: 'Enable' | 'SetTestValue' | 'SetTestValueAndRollback' | 'IncTestValue' | 'JustTakeFee' | 'BatchAll';
+  }
+
+  /** @name PalletSchedulerEvent (366) */
+  interface PalletSchedulerEvent extends Enum {
+    readonly isScheduled: boolean;
+    readonly asScheduled: {
+      readonly when: u32;
+      readonly index: u32;
+    } & Struct;
+    readonly isCanceled: boolean;
+    readonly asCanceled: {
+      readonly when: u32;
+      readonly index: u32;
+    } & Struct;
+    readonly isDispatched: boolean;
+    readonly asDispatched: {
+      readonly task: ITuple<[u32, u32]>;
+      readonly id: Option<U8aFixed>;
+      readonly result: Result<Null, SpRuntimeDispatchError>;
+    } & Struct;
+    readonly isCallUnavailable: boolean;
+    readonly asCallUnavailable: {
+      readonly task: ITuple<[u32, u32]>;
+      readonly id: Option<U8aFixed>;
+    } & Struct;
+    readonly isPeriodicFailed: boolean;
+    readonly asPeriodicFailed: {
+      readonly task: ITuple<[u32, u32]>;
+      readonly id: Option<U8aFixed>;
+    } & Struct;
+    readonly isPermanentlyOverweight: boolean;
+    readonly asPermanentlyOverweight: {
+      readonly task: ITuple<[u32, u32]>;
+      readonly id: Option<U8aFixed>;
+    } & Struct;
+    readonly type: 'Scheduled' | 'Canceled' | 'Dispatched' | 'CallUnavailable' | 'PeriodicFailed' | 'PermanentlyOverweight';
+  }
+
+  /** @name CumulusPalletXcmpQueueEvent (367) */
+  interface CumulusPalletXcmpQueueEvent extends Enum {
+    readonly isSuccess: boolean;
+    readonly asSuccess: {
+      readonly messageHash: U8aFixed;
+      readonly messageId: U8aFixed;
+      readonly weight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isFail: boolean;
+    readonly asFail: {
+      readonly messageHash: U8aFixed;
+      readonly messageId: U8aFixed;
+      readonly error: StagingXcmV3TraitsError;
+      readonly weight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isBadVersion: boolean;
+    readonly asBadVersion: {
+      readonly messageHash: U8aFixed;
+    } & Struct;
+    readonly isBadFormat: boolean;
+    readonly asBadFormat: {
+      readonly messageHash: U8aFixed;
+    } & Struct;
+    readonly isXcmpMessageSent: boolean;
+    readonly asXcmpMessageSent: {
+      readonly messageHash: U8aFixed;
+    } & Struct;
+    readonly isOverweightEnqueued: boolean;
+    readonly asOverweightEnqueued: {
+      readonly sender: u32;
+      readonly sentAt: u32;
+      readonly index: u64;
+      readonly required: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isOverweightServiced: boolean;
+    readonly asOverweightServiced: {
+      readonly index: u64;
+      readonly used: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly type: 'Success' | 'Fail' | 'BadVersion' | 'BadFormat' | 'XcmpMessageSent' | 'OverweightEnqueued' | 'OverweightServiced';
+  }
+
+  /** @name PalletXcmEvent (368) */
+  interface PalletXcmEvent extends Enum {
+    readonly isAttempted: boolean;
+    readonly asAttempted: {
+      readonly outcome: StagingXcmV3TraitsOutcome;
+    } & Struct;
+    readonly isSent: boolean;
+    readonly asSent: {
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly destination: StagingXcmV3MultiLocation;
+      readonly message: StagingXcmV3Xcm;
+      readonly messageId: U8aFixed;
+    } & Struct;
+    readonly isUnexpectedResponse: boolean;
+    readonly asUnexpectedResponse: {
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly queryId: u64;
+    } & Struct;
+    readonly isResponseReady: boolean;
+    readonly asResponseReady: {
+      readonly queryId: u64;
+      readonly response: StagingXcmV3Response;
+    } & Struct;
+    readonly isNotified: boolean;
+    readonly asNotified: {
+      readonly queryId: u64;
+      readonly palletIndex: u8;
+      readonly callIndex: u8;
+    } & Struct;
+    readonly isNotifyOverweight: boolean;
+    readonly asNotifyOverweight: {
+      readonly queryId: u64;
+      readonly palletIndex: u8;
+      readonly callIndex: u8;
+      readonly actualWeight: SpWeightsWeightV2Weight;
+      readonly maxBudgetedWeight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isNotifyDispatchError: boolean;
+    readonly asNotifyDispatchError: {
+      readonly queryId: u64;
+      readonly palletIndex: u8;
+      readonly callIndex: u8;
+    } & Struct;
+    readonly isNotifyDecodeFailed: boolean;
+    readonly asNotifyDecodeFailed: {
+      readonly queryId: u64;
+      readonly palletIndex: u8;
+      readonly callIndex: u8;
+    } & Struct;
+    readonly isInvalidResponder: boolean;
+    readonly asInvalidResponder: {
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly queryId: u64;
+      readonly expectedLocation: Option<StagingXcmV3MultiLocation>;
+    } & Struct;
+    readonly isInvalidResponderVersion: boolean;
+    readonly asInvalidResponderVersion: {
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly queryId: u64;
+    } & Struct;
+    readonly isResponseTaken: boolean;
+    readonly asResponseTaken: {
+      readonly queryId: u64;
+    } & Struct;
+    readonly isAssetsTrapped: boolean;
+    readonly asAssetsTrapped: {
+      readonly hash_: H256;
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly assets: StagingXcmVersionedMultiAssets;
+    } & Struct;
+    readonly isVersionChangeNotified: boolean;
+    readonly asVersionChangeNotified: {
+      readonly destination: StagingXcmV3MultiLocation;
+      readonly result: u32;
+      readonly cost: StagingXcmV3MultiassetMultiAssets;
+      readonly messageId: U8aFixed;
+    } & Struct;
+    readonly isSupportedVersionChanged: boolean;
+    readonly asSupportedVersionChanged: {
+      readonly location: StagingXcmV3MultiLocation;
+      readonly version: u32;
+    } & Struct;
+    readonly isNotifyTargetSendFail: boolean;
+    readonly asNotifyTargetSendFail: {
+      readonly location: StagingXcmV3MultiLocation;
+      readonly queryId: u64;
+      readonly error: StagingXcmV3TraitsError;
+    } & Struct;
+    readonly isNotifyTargetMigrationFail: boolean;
+    readonly asNotifyTargetMigrationFail: {
+      readonly location: StagingXcmVersionedMultiLocation;
+      readonly queryId: u64;
+    } & Struct;
+    readonly isInvalidQuerierVersion: boolean;
+    readonly asInvalidQuerierVersion: {
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly queryId: u64;
+    } & Struct;
+    readonly isInvalidQuerier: boolean;
+    readonly asInvalidQuerier: {
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly queryId: u64;
+      readonly expectedQuerier: StagingXcmV3MultiLocation;
+      readonly maybeActualQuerier: Option<StagingXcmV3MultiLocation>;
+    } & Struct;
+    readonly isVersionNotifyStarted: boolean;
+    readonly asVersionNotifyStarted: {
+      readonly destination: StagingXcmV3MultiLocation;
+      readonly cost: StagingXcmV3MultiassetMultiAssets;
+      readonly messageId: U8aFixed;
+    } & Struct;
+    readonly isVersionNotifyRequested: boolean;
+    readonly asVersionNotifyRequested: {
+      readonly destination: StagingXcmV3MultiLocation;
+      readonly cost: StagingXcmV3MultiassetMultiAssets;
+      readonly messageId: U8aFixed;
+    } & Struct;
+    readonly isVersionNotifyUnrequested: boolean;
+    readonly asVersionNotifyUnrequested: {
+      readonly destination: StagingXcmV3MultiLocation;
+      readonly cost: StagingXcmV3MultiassetMultiAssets;
+      readonly messageId: U8aFixed;
+    } & Struct;
+    readonly isFeesPaid: boolean;
+    readonly asFeesPaid: {
+      readonly paying: StagingXcmV3MultiLocation;
+      readonly fees: StagingXcmV3MultiassetMultiAssets;
+    } & Struct;
+    readonly isAssetsClaimed: boolean;
+    readonly asAssetsClaimed: {
+      readonly hash_: H256;
+      readonly origin: StagingXcmV3MultiLocation;
+      readonly assets: StagingXcmVersionedMultiAssets;
+    } & Struct;
+    readonly type: 'Attempted' | 'Sent' | 'UnexpectedResponse' | 'ResponseReady' | 'Notified' | 'NotifyOverweight' | 'NotifyDispatchError' | 'NotifyDecodeFailed' | 'InvalidResponder' | 'InvalidResponderVersion' | 'ResponseTaken' | 'AssetsTrapped' | 'VersionChangeNotified' | 'SupportedVersionChanged' | 'NotifyTargetSendFail' | 'NotifyTargetMigrationFail' | 'InvalidQuerierVersion' | 'InvalidQuerier' | 'VersionNotifyStarted' | 'VersionNotifyRequested' | 'VersionNotifyUnrequested' | 'FeesPaid' | 'AssetsClaimed';
+  }
+
+  /** @name StagingXcmV3TraitsOutcome (369) */
+  interface StagingXcmV3TraitsOutcome extends Enum {
+    readonly isComplete: boolean;
+    readonly asComplete: SpWeightsWeightV2Weight;
+    readonly isIncomplete: boolean;
+    readonly asIncomplete: ITuple<[SpWeightsWeightV2Weight, StagingXcmV3TraitsError]>;
+    readonly isError: boolean;
+    readonly asError: StagingXcmV3TraitsError;
+    readonly type: 'Complete' | 'Incomplete' | 'Error';
+  }
+
+  /** @name CumulusPalletXcmEvent (370) */
+  interface CumulusPalletXcmEvent extends Enum {
+    readonly isInvalidFormat: boolean;
+    readonly asInvalidFormat: U8aFixed;
+    readonly isUnsupportedVersion: boolean;
+    readonly asUnsupportedVersion: U8aFixed;
+    readonly isExecutedDownward: boolean;
+    readonly asExecutedDownward: ITuple<[U8aFixed, StagingXcmV3TraitsOutcome]>;
+    readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward';
+  }
+
+  /** @name CumulusPalletDmpQueueEvent (371) */
+  interface CumulusPalletDmpQueueEvent extends Enum {
+    readonly isInvalidFormat: boolean;
+    readonly asInvalidFormat: {
+      readonly messageHash: U8aFixed;
+    } & Struct;
+    readonly isUnsupportedVersion: boolean;
+    readonly asUnsupportedVersion: {
+      readonly messageHash: U8aFixed;
+    } & Struct;
+    readonly isExecutedDownward: boolean;
+    readonly asExecutedDownward: {
+      readonly messageHash: U8aFixed;
+      readonly messageId: U8aFixed;
+      readonly outcome: StagingXcmV3TraitsOutcome;
+    } & Struct;
+    readonly isWeightExhausted: boolean;
+    readonly asWeightExhausted: {
+      readonly messageHash: U8aFixed;
+      readonly messageId: U8aFixed;
+      readonly remainingWeight: SpWeightsWeightV2Weight;
+      readonly requiredWeight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isOverweightEnqueued: boolean;
+    readonly asOverweightEnqueued: {
+      readonly messageHash: U8aFixed;
+      readonly messageId: U8aFixed;
+      readonly overweightIndex: u64;
+      readonly requiredWeight: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isOverweightServiced: boolean;
+    readonly asOverweightServiced: {
+      readonly overweightIndex: u64;
+      readonly weightUsed: SpWeightsWeightV2Weight;
+    } & Struct;
+    readonly isMaxMessagesExhausted: boolean;
+    readonly asMaxMessagesExhausted: {
+      readonly messageHash: U8aFixed;
+    } & Struct;
+    readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced' | 'MaxMessagesExhausted';
+  }
+
+  /** @name PalletConfigurationEvent (372) */
+  interface PalletConfigurationEvent extends Enum {
+    readonly isNewDesiredCollators: boolean;
+    readonly asNewDesiredCollators: {
+      readonly desiredCollators: Option<u32>;
+    } & Struct;
+    readonly isNewCollatorLicenseBond: boolean;
+    readonly asNewCollatorLicenseBond: {
+      readonly bondCost: Option<u128>;
+    } & Struct;
+    readonly isNewCollatorKickThreshold: boolean;
+    readonly asNewCollatorKickThreshold: {
+      readonly lengthInBlocks: Option<u32>;
+    } & Struct;
+    readonly type: 'NewDesiredCollators' | 'NewCollatorLicenseBond' | 'NewCollatorKickThreshold';
+  }
+
+  /** @name PalletCommonEvent (373) */
+  interface PalletCommonEvent extends Enum {
+    readonly isCollectionCreated: boolean;
+    readonly asCollectionCreated: ITuple<[u32, u8, AccountId32]>;
+    readonly isCollectionDestroyed: boolean;
+    readonly asCollectionDestroyed: u32;
+    readonly isItemCreated: boolean;
+    readonly asItemCreated: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+    readonly isItemDestroyed: boolean;
+    readonly asItemDestroyed: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+    readonly isTransfer: boolean;
+    readonly asTransfer: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+    readonly isApproved: boolean;
+    readonly asApproved: ITuple<[u32, u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, u128]>;
+    readonly isApprovedForAll: boolean;
+    readonly asApprovedForAll: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmAccountBasicCrossAccountIdRepr, bool]>;
+    readonly isCollectionPropertySet: boolean;
+    readonly asCollectionPropertySet: ITuple<[u32, Bytes]>;
+    readonly isCollectionPropertyDeleted: boolean;
+    readonly asCollectionPropertyDeleted: ITuple<[u32, Bytes]>;
+    readonly isTokenPropertySet: boolean;
+    readonly asTokenPropertySet: ITuple<[u32, u32, Bytes]>;
+    readonly isTokenPropertyDeleted: boolean;
+    readonly asTokenPropertyDeleted: ITuple<[u32, u32, Bytes]>;
+    readonly isPropertyPermissionSet: boolean;
+    readonly asPropertyPermissionSet: ITuple<[u32, Bytes]>;
+    readonly isAllowListAddressAdded: boolean;
+    readonly asAllowListAddressAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+    readonly isAllowListAddressRemoved: boolean;
+    readonly asAllowListAddressRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+    readonly isCollectionAdminAdded: boolean;
+    readonly asCollectionAdminAdded: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+    readonly isCollectionAdminRemoved: boolean;
+    readonly asCollectionAdminRemoved: ITuple<[u32, PalletEvmAccountBasicCrossAccountIdRepr]>;
+    readonly isCollectionLimitSet: boolean;
+    readonly asCollectionLimitSet: u32;
+    readonly isCollectionOwnerChanged: boolean;
+    readonly asCollectionOwnerChanged: ITuple<[u32, AccountId32]>;
+    readonly isCollectionPermissionSet: boolean;
+    readonly asCollectionPermissionSet: u32;
+    readonly isCollectionSponsorSet: boolean;
+    readonly asCollectionSponsorSet: ITuple<[u32, AccountId32]>;
+    readonly isSponsorshipConfirmed: boolean;
+    readonly asSponsorshipConfirmed: ITuple<[u32, AccountId32]>;
+    readonly isCollectionSponsorRemoved: boolean;
+    readonly asCollectionSponsorRemoved: u32;
+    readonly type: 'CollectionCreated' | 'CollectionDestroyed' | 'ItemCreated' | 'ItemDestroyed' | 'Transfer' | 'Approved' | 'ApprovedForAll' | 'CollectionPropertySet' | 'CollectionPropertyDeleted' | 'TokenPropertySet' | 'TokenPropertyDeleted' | 'PropertyPermissionSet' | 'AllowListAddressAdded' | 'AllowListAddressRemoved' | 'CollectionAdminAdded' | 'CollectionAdminRemoved' | 'CollectionLimitSet' | 'CollectionOwnerChanged' | 'CollectionPermissionSet' | 'CollectionSponsorSet' | 'SponsorshipConfirmed' | 'CollectionSponsorRemoved';
+  }
+
+  /** @name PalletStructureEvent (374) */
+  interface PalletStructureEvent extends Enum {
+    readonly isExecuted: boolean;
+    readonly asExecuted: Result<Null, SpRuntimeDispatchError>;
+    readonly type: 'Executed';
+  }
+
+  /** @name PalletAppPromotionEvent (375) */
+  interface PalletAppPromotionEvent extends Enum {
+    readonly isStakingRecalculation: boolean;
+    readonly asStakingRecalculation: ITuple<[AccountId32, u128, u128]>;
+    readonly isStake: boolean;
+    readonly asStake: ITuple<[AccountId32, u128]>;
+    readonly isUnstake: boolean;
+    readonly asUnstake: ITuple<[AccountId32, u128]>;
+    readonly isSetAdmin: boolean;
+    readonly asSetAdmin: AccountId32;
+    readonly type: 'StakingRecalculation' | 'Stake' | 'Unstake' | 'SetAdmin';
+  }
+
+  /** @name PalletForeignAssetsModuleEvent (376) */
+  interface PalletForeignAssetsModuleEvent extends Enum {
+    readonly isForeignAssetRegistered: boolean;
+    readonly asForeignAssetRegistered: {
+      readonly assetId: u32;
+      readonly assetAddress: StagingXcmV3MultiLocation;
+      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+    } & Struct;
+    readonly isForeignAssetUpdated: boolean;
+    readonly asForeignAssetUpdated: {
+      readonly assetId: u32;
+      readonly assetAddress: StagingXcmV3MultiLocation;
+      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+    } & Struct;
+    readonly isAssetRegistered: boolean;
+    readonly asAssetRegistered: {
+      readonly assetId: PalletForeignAssetsAssetId;
+      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+    } & Struct;
+    readonly isAssetUpdated: boolean;
+    readonly asAssetUpdated: {
+      readonly assetId: PalletForeignAssetsAssetId;
+      readonly metadata: PalletForeignAssetsModuleAssetMetadata;
+    } & Struct;
+    readonly type: 'ForeignAssetRegistered' | 'ForeignAssetUpdated' | 'AssetRegistered' | 'AssetUpdated';
+  }
+
+  /** @name PalletEvmEvent (377) */
+  interface PalletEvmEvent extends Enum {
+    readonly isLog: boolean;
+    readonly asLog: {
+      readonly log: EthereumLog;
+    } & Struct;
+    readonly isCreated: boolean;
+    readonly asCreated: {
+      readonly address: H160;
+    } & Struct;
+    readonly isCreatedFailed: boolean;
+    readonly asCreatedFailed: {
+      readonly address: H160;
+    } & Struct;
+    readonly isExecuted: boolean;
+    readonly asExecuted: {
+      readonly address: H160;
+    } & Struct;
+    readonly isExecutedFailed: boolean;
+    readonly asExecutedFailed: {
+      readonly address: H160;
+    } & Struct;
+    readonly type: 'Log' | 'Created' | 'CreatedFailed' | 'Executed' | 'ExecutedFailed';
+  }
+
+  /** @name PalletEthereumEvent (378) */
+  interface PalletEthereumEvent extends Enum {
+    readonly isExecuted: boolean;
+    readonly asExecuted: {
+      readonly from: H160;
+      readonly to: H160;
+      readonly transactionHash: H256;
+      readonly exitReason: EvmCoreErrorExitReason;
+      readonly extraData: Bytes;
+    } & Struct;
+    readonly type: 'Executed';
+  }
+
+  /** @name EvmCoreErrorExitReason (379) */
+  interface EvmCoreErrorExitReason extends Enum {
+    readonly isSucceed: boolean;
+    readonly asSucceed: EvmCoreErrorExitSucceed;
+    readonly isError: boolean;
+    readonly asError: EvmCoreErrorExitError;
+    readonly isRevert: boolean;
+    readonly asRevert: EvmCoreErrorExitRevert;
+    readonly isFatal: boolean;
+    readonly asFatal: EvmCoreErrorExitFatal;
+    readonly type: 'Succeed' | 'Error' | 'Revert' | 'Fatal';
+  }
+
+  /** @name EvmCoreErrorExitSucceed (380) */
+  interface EvmCoreErrorExitSucceed extends Enum {
+    readonly isStopped: boolean;
+    readonly isReturned: boolean;
+    readonly isSuicided: boolean;
+    readonly type: 'Stopped' | 'Returned' | 'Suicided';
+  }
+
+  /** @name EvmCoreErrorExitError (381) */
+  interface EvmCoreErrorExitError extends Enum {
+    readonly isStackUnderflow: boolean;
+    readonly isStackOverflow: boolean;
+    readonly isInvalidJump: boolean;
+    readonly isInvalidRange: boolean;
+    readonly isDesignatedInvalid: boolean;
+    readonly isCallTooDeep: boolean;
+    readonly isCreateCollision: boolean;
+    readonly isCreateContractLimit: boolean;
+    readonly isOutOfOffset: boolean;
+    readonly isOutOfGas: boolean;
+    readonly isOutOfFund: boolean;
+    readonly isPcUnderflow: boolean;
+    readonly isCreateEmpty: boolean;
+    readonly isOther: boolean;
+    readonly asOther: Text;
+    readonly isMaxNonce: boolean;
+    readonly isInvalidCode: boolean;
+    readonly asInvalidCode: u8;
+    readonly type: 'StackUnderflow' | 'StackOverflow' | 'InvalidJump' | 'InvalidRange' | 'DesignatedInvalid' | 'CallTooDeep' | 'CreateCollision' | 'CreateContractLimit' | 'OutOfOffset' | 'OutOfGas' | 'OutOfFund' | 'PcUnderflow' | 'CreateEmpty' | 'Other' | 'MaxNonce' | 'InvalidCode';
+  }
+
+  /** @name EvmCoreErrorExitRevert (385) */
+  interface EvmCoreErrorExitRevert extends Enum {
+    readonly isReverted: boolean;
+    readonly type: 'Reverted';
+  }
+
+  /** @name EvmCoreErrorExitFatal (386) */
+  interface EvmCoreErrorExitFatal extends Enum {
+    readonly isNotSupported: boolean;
+    readonly isUnhandledInterrupt: boolean;
+    readonly isCallErrorAsFatal: boolean;
+    readonly asCallErrorAsFatal: EvmCoreErrorExitError;
+    readonly isOther: boolean;
+    readonly asOther: Text;
+    readonly type: 'NotSupported' | 'UnhandledInterrupt' | 'CallErrorAsFatal' | 'Other';
+  }
+
+  /** @name PalletEvmContractHelpersEvent (387) */
+  interface PalletEvmContractHelpersEvent extends Enum {
+    readonly isContractSponsorSet: boolean;
+    readonly asContractSponsorSet: ITuple<[H160, AccountId32]>;
+    readonly isContractSponsorshipConfirmed: boolean;
+    readonly asContractSponsorshipConfirmed: ITuple<[H160, AccountId32]>;
+    readonly isContractSponsorRemoved: boolean;
+    readonly asContractSponsorRemoved: H160;
+    readonly type: 'ContractSponsorSet' | 'ContractSponsorshipConfirmed' | 'ContractSponsorRemoved';
+  }
+
+  /** @name PalletEvmMigrationEvent (388) */
+  interface PalletEvmMigrationEvent extends Enum {
+    readonly isTestEvent: boolean;
+    readonly type: 'TestEvent';
+  }
+
+  /** @name PalletMaintenanceEvent (389) */
+  interface PalletMaintenanceEvent extends Enum {
+    readonly isMaintenanceEnabled: boolean;
+    readonly isMaintenanceDisabled: boolean;
+    readonly type: 'MaintenanceEnabled' | 'MaintenanceDisabled';
+  }
+
+  /** @name PalletUtilityEvent (390) */
+  interface PalletUtilityEvent extends Enum {
+    readonly isBatchInterrupted: boolean;
+    readonly asBatchInterrupted: {
+      readonly index: u32;
+      readonly error: SpRuntimeDispatchError;
+    } & Struct;
+    readonly isBatchCompleted: boolean;
+    readonly isBatchCompletedWithErrors: boolean;
+    readonly isItemCompleted: boolean;
+    readonly isItemFailed: boolean;
+    readonly asItemFailed: {
+      readonly error: SpRuntimeDispatchError;
+    } & Struct;
+    readonly isDispatchedAs: boolean;
+    readonly asDispatchedAs: {
+      readonly result: Result<Null, SpRuntimeDispatchError>;
+    } & Struct;
+    readonly type: 'BatchInterrupted' | 'BatchCompleted' | 'BatchCompletedWithErrors' | 'ItemCompleted' | 'ItemFailed' | 'DispatchedAs';
+  }
+
+  /** @name PalletTestUtilsEvent (391) */
+  interface PalletTestUtilsEvent extends Enum {
+    readonly isValueIsSet: boolean;
+    readonly isShouldRollback: boolean;
+    readonly isBatchCompleted: boolean;
+    readonly type: 'ValueIsSet' | 'ShouldRollback' | 'BatchCompleted';
+  }
+
+  /** @name FrameSystemPhase (392) */
+  interface FrameSystemPhase extends Enum {
+    readonly isApplyExtrinsic: boolean;
+    readonly asApplyExtrinsic: u32;
+    readonly isFinalization: boolean;
+    readonly isInitialization: boolean;
+    readonly type: 'ApplyExtrinsic' | 'Finalization' | 'Initialization';
+  }
+
+  /** @name FrameSystemLastRuntimeUpgradeInfo (394) */
+  interface FrameSystemLastRuntimeUpgradeInfo extends Struct {
+    readonly specVersion: Compact<u32>;
+    readonly specName: Text;
+  }
+
+  /** @name FrameSystemLimitsBlockWeights (395) */
+  interface FrameSystemLimitsBlockWeights extends Struct {
+    readonly baseBlock: SpWeightsWeightV2Weight;
+    readonly maxBlock: SpWeightsWeightV2Weight;
+    readonly perClass: FrameSupportDispatchPerDispatchClassWeightsPerClass;
+  }
+
+  /** @name FrameSupportDispatchPerDispatchClassWeightsPerClass (396) */
+  interface FrameSupportDispatchPerDispatchClassWeightsPerClass extends Struct {
+    readonly normal: FrameSystemLimitsWeightsPerClass;
+    readonly operational: FrameSystemLimitsWeightsPerClass;
+    readonly mandatory: FrameSystemLimitsWeightsPerClass;
+  }
+
+  /** @name FrameSystemLimitsWeightsPerClass (397) */
+  interface FrameSystemLimitsWeightsPerClass extends Struct {
+    readonly baseExtrinsic: SpWeightsWeightV2Weight;
+    readonly maxExtrinsic: Option<SpWeightsWeightV2Weight>;
+    readonly maxTotal: Option<SpWeightsWeightV2Weight>;
+    readonly reserved: Option<SpWeightsWeightV2Weight>;
+  }
+
+  /** @name FrameSystemLimitsBlockLength (399) */
+  interface FrameSystemLimitsBlockLength extends Struct {
+    readonly max: FrameSupportDispatchPerDispatchClassU32;
+  }
+
+  /** @name FrameSupportDispatchPerDispatchClassU32 (400) */
+  interface FrameSupportDispatchPerDispatchClassU32 extends Struct {
+    readonly normal: u32;
+    readonly operational: u32;
+    readonly mandatory: u32;
+  }
+
+  /** @name SpWeightsRuntimeDbWeight (401) */
+  interface SpWeightsRuntimeDbWeight extends Struct {
+    readonly read: u64;
+    readonly write: u64;
+  }
+
+  /** @name SpVersionRuntimeVersion (402) */
+  interface SpVersionRuntimeVersion extends Struct {
+    readonly specName: Text;
+    readonly implName: Text;
+    readonly authoringVersion: u32;
+    readonly specVersion: u32;
+    readonly implVersion: u32;
+    readonly apis: Vec<ITuple<[U8aFixed, u32]>>;
+    readonly transactionVersion: u32;
+    readonly stateVersion: u8;
+  }
+
+  /** @name FrameSystemError (406) */
+  interface FrameSystemError extends Enum {
+    readonly isInvalidSpecName: boolean;
+    readonly isSpecVersionNeedsToIncrease: boolean;
+    readonly isFailedToExtractRuntimeVersion: boolean;
+    readonly isNonDefaultComposite: boolean;
+    readonly isNonZeroRefCount: boolean;
+    readonly isCallFiltered: boolean;
+    readonly type: 'InvalidSpecName' | 'SpecVersionNeedsToIncrease' | 'FailedToExtractRuntimeVersion' | 'NonDefaultComposite' | 'NonZeroRefCount' | 'CallFiltered';
+  }
+
+  /** @name CumulusPalletParachainSystemUnincludedSegmentAncestor (408) */
+  interface CumulusPalletParachainSystemUnincludedSegmentAncestor extends Struct {
+    readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
+    readonly paraHeadHash: Option<H256>;
+    readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
+  }
+
+  /** @name CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth (409) */
+  interface CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth extends Struct {
+    readonly umpMsgCount: u32;
+    readonly umpTotalBytes: u32;
+    readonly hrmpOutgoing: BTreeMap<u32, CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate>;
+  }
+
+  /** @name CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate (411) */
+  interface CumulusPalletParachainSystemUnincludedSegmentHrmpChannelUpdate extends Struct {
+    readonly msgCount: u32;
+    readonly totalBytes: u32;
+  }
+
+  /** @name PolkadotPrimitivesV5UpgradeGoAhead (415) */
+  interface PolkadotPrimitivesV5UpgradeGoAhead extends Enum {
+    readonly isAbort: boolean;
+    readonly isGoAhead: boolean;
+    readonly type: 'Abort' | 'GoAhead';
+  }
+
+  /** @name CumulusPalletParachainSystemUnincludedSegmentSegmentTracker (416) */
+  interface CumulusPalletParachainSystemUnincludedSegmentSegmentTracker extends Struct {
+    readonly usedBandwidth: CumulusPalletParachainSystemUnincludedSegmentUsedBandwidth;
+    readonly hrmpWatermark: Option<u32>;
+    readonly consumedGoAheadSignal: Option<PolkadotPrimitivesV5UpgradeGoAhead>;
+  }
+
+  /** @name PolkadotPrimitivesV5UpgradeRestriction (418) */
+  interface PolkadotPrimitivesV5UpgradeRestriction extends Enum {
+    readonly isPresent: boolean;
+    readonly type: 'Present';
+  }
+
+  /** @name CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot (419) */
+  interface CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot extends Struct {
+    readonly dmqMqcHead: H256;
+    readonly relayDispatchQueueRemainingCapacity: CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity;
+    readonly ingressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
+    readonly egressChannels: Vec<ITuple<[u32, PolkadotPrimitivesV5AbridgedHrmpChannel]>>;
+  }
+
+  /** @name CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity (420) */
+  interface CumulusPalletParachainSystemRelayStateSnapshotRelayDispatchQueueRemainingCapacity extends Struct {
+    readonly remainingCount: u32;
+    readonly remainingSize: u32;
+  }
+
+  /** @name PolkadotPrimitivesV5AbridgedHrmpChannel (423) */
+  interface PolkadotPrimitivesV5AbridgedHrmpChannel extends Struct {
+    readonly maxCapacity: u32;
+    readonly maxTotalSize: u32;
+    readonly maxMessageSize: u32;
+    readonly msgCount: u32;
+    readonly totalSize: u32;
+    readonly mqcHead: Option<H256>;
+  }
+
+  /** @name PolkadotPrimitivesV5AbridgedHostConfiguration (424) */
+  interface PolkadotPrimitivesV5AbridgedHostConfiguration extends Struct {
+    readonly maxCodeSize: u32;
+    readonly maxHeadDataSize: u32;
+    readonly maxUpwardQueueCount: u32;
+    readonly maxUpwardQueueSize: u32;
+    readonly maxUpwardMessageSize: u32;
+    readonly maxUpwardMessageNumPerCandidate: u32;
+    readonly hrmpMaxMessageNumPerCandidate: u32;
+    readonly validationUpgradeCooldown: u32;
+    readonly validationUpgradeDelay: u32;
+    readonly asyncBackingParams: PolkadotPrimitivesVstagingAsyncBackingParams;
+  }
+
+  /** @name PolkadotPrimitivesVstagingAsyncBackingParams (425) */
+  interface PolkadotPrimitivesVstagingAsyncBackingParams extends Struct {
+    readonly maxCandidateDepth: u32;
+    readonly allowedAncestryLen: u32;
+  }
+
+  /** @name PolkadotCorePrimitivesOutboundHrmpMessage (431) */
+  interface PolkadotCorePrimitivesOutboundHrmpMessage extends Struct {
+    readonly recipient: u32;
+    readonly data: Bytes;
+  }
+
+  /** @name CumulusPalletParachainSystemCodeUpgradeAuthorization (432) */
+  interface CumulusPalletParachainSystemCodeUpgradeAuthorization extends Struct {
+    readonly codeHash: H256;
+    readonly checkVersion: bool;
+  }
+
+  /** @name CumulusPalletParachainSystemError (433) */
+  interface CumulusPalletParachainSystemError extends Enum {
+    readonly isOverlappingUpgrades: boolean;
+    readonly isProhibitedByPolkadot: boolean;
+    readonly isTooBig: boolean;
+    readonly isValidationDataNotAvailable: boolean;
+    readonly isHostConfigurationNotAvailable: boolean;
+    readonly isNotScheduled: boolean;
+    readonly isNothingAuthorized: boolean;
+    readonly isUnauthorized: boolean;
+    readonly type: 'OverlappingUpgrades' | 'ProhibitedByPolkadot' | 'TooBig' | 'ValidationDataNotAvailable' | 'HostConfigurationNotAvailable' | 'NotScheduled' | 'NothingAuthorized' | 'Unauthorized';
+  }
+
+  /** @name PalletCollatorSelectionError (435) */
+  interface PalletCollatorSelectionError extends Enum {
+    readonly isTooManyCandidates: boolean;
+    readonly isUnknown: boolean;
+    readonly isPermission: boolean;
+    readonly isAlreadyHoldingLicense: boolean;
+    readonly isNoLicense: boolean;
+    readonly isAlreadyCandidate: boolean;
+    readonly isNotCandidate: boolean;
+    readonly isTooManyInvulnerables: boolean;
+    readonly isTooFewInvulnerables: boolean;
+    readonly isAlreadyInvulnerable: boolean;
+    readonly isNotInvulnerable: boolean;
+    readonly isNoAssociatedValidatorId: boolean;
+    readonly isValidatorNotRegistered: boolean;
+    readonly type: 'TooManyCandidates' | 'Unknown' | 'Permission' | 'AlreadyHoldingLicense' | 'NoLicense' | 'AlreadyCandidate' | 'NotCandidate' | 'TooManyInvulnerables' | 'TooFewInvulnerables' | 'AlreadyInvulnerable' | 'NotInvulnerable' | 'NoAssociatedValidatorId' | 'ValidatorNotRegistered';
+  }
+
+  /** @name SpCoreCryptoKeyTypeId (439) */
+  interface SpCoreCryptoKeyTypeId extends U8aFixed {}
+
+  /** @name PalletSessionError (440) */
+  interface PalletSessionError extends Enum {
+    readonly isInvalidProof: boolean;
+    readonly isNoAssociatedValidatorId: boolean;
+    readonly isDuplicatedKey: boolean;
+    readonly isNoKeys: boolean;
+    readonly isNoAccount: boolean;
+    readonly type: 'InvalidProof' | 'NoAssociatedValidatorId' | 'DuplicatedKey' | 'NoKeys' | 'NoAccount';
+  }
+
+  /** @name PalletBalancesBalanceLock (446) */
+  interface PalletBalancesBalanceLock extends Struct {
+    readonly id: U8aFixed;
+    readonly amount: u128;
+    readonly reasons: PalletBalancesReasons;
+  }
+
+  /** @name PalletBalancesReasons (447) */
+  interface PalletBalancesReasons extends Enum {
+    readonly isFee: boolean;
+    readonly isMisc: boolean;
+    readonly isAll: boolean;
+    readonly type: 'Fee' | 'Misc' | 'All';
+  }
+
+  /** @name PalletBalancesReserveData (450) */
+  interface PalletBalancesReserveData extends Struct {
+    readonly id: U8aFixed;
+    readonly amount: u128;
+  }
+
+  /** @name OpalRuntimeRuntimeHoldReason (454) */
+  interface OpalRuntimeRuntimeHoldReason extends Enum {
+    readonly isCollatorSelection: boolean;
+    readonly asCollatorSelection: PalletCollatorSelectionHoldReason;
+    readonly type: 'CollatorSelection';
+  }
+
+  /** @name PalletCollatorSelectionHoldReason (455) */
+  interface PalletCollatorSelectionHoldReason extends Enum {
+    readonly isLicenseBond: boolean;
+    readonly type: 'LicenseBond';
+  }
+
+  /** @name PalletBalancesIdAmount (458) */
+  interface PalletBalancesIdAmount extends Struct {
+    readonly id: U8aFixed;
+    readonly amount: u128;
+  }
+
+  /** @name PalletBalancesError (460) */
+  interface PalletBalancesError extends Enum {
+    readonly isVestingBalance: boolean;
+    readonly isLiquidityRestrictions: boolean;
+    readonly isInsufficientBalance: boolean;
+    readonly isExistentialDeposit: boolean;
+    readonly isExpendability: boolean;
+    readonly isExistingVestingSchedule: boolean;
+    readonly isDeadAccount: boolean;
+    readonly isTooManyReserves: boolean;
+    readonly isTooManyHolds: boolean;
+    readonly isTooManyFreezes: boolean;
+    readonly type: 'VestingBalance' | 'LiquidityRestrictions' | 'InsufficientBalance' | 'ExistentialDeposit' | 'Expendability' | 'ExistingVestingSchedule' | 'DeadAccount' | 'TooManyReserves' | 'TooManyHolds' | 'TooManyFreezes';
+  }
+
+  /** @name PalletTransactionPaymentReleases (462) */
+  interface PalletTransactionPaymentReleases extends Enum {
+    readonly isV1Ancient: boolean;
+    readonly isV2: boolean;
+    readonly type: 'V1Ancient' | 'V2';
+  }
+
+  /** @name PalletTreasuryProposal (463) */
+  interface PalletTreasuryProposal extends Struct {
+    readonly proposer: AccountId32;
+    readonly value: u128;
+    readonly beneficiary: AccountId32;
+    readonly bond: u128;
+  }
+
+  /** @name FrameSupportPalletId (466) */
+  interface FrameSupportPalletId extends U8aFixed {}
+
+  /** @name PalletTreasuryError (467) */
+  interface PalletTreasuryError extends Enum {
+    readonly isInsufficientProposersBalance: boolean;
+    readonly isInvalidIndex: boolean;
+    readonly isTooManyApprovals: boolean;
+    readonly isInsufficientPermission: boolean;
+    readonly isProposalNotApproved: boolean;
+    readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'InsufficientPermission' | 'ProposalNotApproved';
+  }
+
+  /** @name PalletSudoError (468) */
+  interface PalletSudoError extends Enum {
+    readonly isRequireSudo: boolean;
+    readonly type: 'RequireSudo';
+  }
+
+  /** @name OrmlVestingModuleError (470) */
+  interface OrmlVestingModuleError extends Enum {
+    readonly isZeroVestingPeriod: boolean;
+    readonly isZeroVestingPeriodCount: boolean;
+    readonly isInsufficientBalanceToLock: boolean;
+    readonly isTooManyVestingSchedules: boolean;
+    readonly isAmountLow: boolean;
+    readonly isMaxVestingSchedulesExceeded: boolean;
+    readonly type: 'ZeroVestingPeriod' | 'ZeroVestingPeriodCount' | 'InsufficientBalanceToLock' | 'TooManyVestingSchedules' | 'AmountLow' | 'MaxVestingSchedulesExceeded';
+  }
+
+  /** @name OrmlXtokensModuleError (471) */
+  interface OrmlXtokensModuleError extends Enum {
+    readonly isAssetHasNoReserve: boolean;
+    readonly isNotCrossChainTransfer: boolean;
+    readonly isInvalidDest: boolean;
+    readonly isNotCrossChainTransferableCurrency: boolean;
+    readonly isUnweighableMessage: boolean;
+    readonly isXcmExecutionFailed: boolean;
+    readonly isCannotReanchor: boolean;
+    readonly isInvalidAncestry: boolean;
+    readonly isInvalidAsset: boolean;
+    readonly isDestinationNotInvertible: boolean;
+    readonly isBadVersion: boolean;
+    readonly isDistinctReserveForAssetAndFee: boolean;
+    readonly isZeroFee: boolean;
+    readonly isZeroAmount: boolean;
+    readonly isTooManyAssetsBeingSent: boolean;
+    readonly isAssetIndexNonExistent: boolean;
+    readonly isFeeNotEnough: boolean;
+    readonly isNotSupportedMultiLocation: boolean;
+    readonly isMinXcmFeeNotDefined: boolean;
+    readonly type: 'AssetHasNoReserve' | 'NotCrossChainTransfer' | 'InvalidDest' | 'NotCrossChainTransferableCurrency' | 'UnweighableMessage' | 'XcmExecutionFailed' | 'CannotReanchor' | 'InvalidAncestry' | 'InvalidAsset' | 'DestinationNotInvertible' | 'BadVersion' | 'DistinctReserveForAssetAndFee' | 'ZeroFee' | 'ZeroAmount' | 'TooManyAssetsBeingSent' | 'AssetIndexNonExistent' | 'FeeNotEnough' | 'NotSupportedMultiLocation' | 'MinXcmFeeNotDefined';
+  }
+
+  /** @name OrmlTokensBalanceLock (474) */
+  interface OrmlTokensBalanceLock extends Struct {
+    readonly id: U8aFixed;
+    readonly amount: u128;
+  }
+
+  /** @name OrmlTokensAccountData (476) */
+  interface OrmlTokensAccountData extends Struct {
+    readonly free: u128;
+    readonly reserved: u128;
+    readonly frozen: u128;
+  }
+
+  /** @name OrmlTokensReserveData (478) */
+  interface OrmlTokensReserveData extends Struct {
+    readonly id: Null;
+    readonly amount: u128;
+  }
+
+  /** @name OrmlTokensModuleError (480) */
+  interface OrmlTokensModuleError extends Enum {
+    readonly isBalanceTooLow: boolean;
+    readonly isAmountIntoBalanceFailed: boolean;
+    readonly isLiquidityRestrictions: boolean;
+    readonly isMaxLocksExceeded: boolean;
+    readonly isKeepAlive: boolean;
+    readonly isExistentialDeposit: boolean;
+    readonly isDeadAccount: boolean;
+    readonly isTooManyReserves: boolean;
+    readonly type: 'BalanceTooLow' | 'AmountIntoBalanceFailed' | 'LiquidityRestrictions' | 'MaxLocksExceeded' | 'KeepAlive' | 'ExistentialDeposit' | 'DeadAccount' | 'TooManyReserves';
+  }
+
+  /** @name PalletIdentityRegistrarInfo (485) */
+  interface PalletIdentityRegistrarInfo extends Struct {
+    readonly account: AccountId32;
+    readonly fee: u128;
+    readonly fields: PalletIdentityBitFlags;
+  }
+
+  /** @name PalletIdentityError (487) */
+  interface PalletIdentityError extends Enum {
+    readonly isTooManySubAccounts: boolean;
+    readonly isNotFound: boolean;
+    readonly isNotNamed: boolean;
+    readonly isEmptyIndex: boolean;
+    readonly isFeeChanged: boolean;
+    readonly isNoIdentity: boolean;
+    readonly isStickyJudgement: boolean;
+    readonly isJudgementGiven: boolean;
+    readonly isInvalidJudgement: boolean;
+    readonly isInvalidIndex: boolean;
+    readonly isInvalidTarget: boolean;
+    readonly isTooManyFields: boolean;
+    readonly isTooManyRegistrars: boolean;
+    readonly isAlreadyClaimed: boolean;
+    readonly isNotSub: boolean;
+    readonly isNotOwned: boolean;
+    readonly isJudgementForDifferentIdentity: boolean;
+    readonly isJudgementPaymentFailed: boolean;
+    readonly type: 'TooManySubAccounts' | 'NotFound' | 'NotNamed' | 'EmptyIndex' | 'FeeChanged' | 'NoIdentity' | 'StickyJudgement' | 'JudgementGiven' | 'InvalidJudgement' | 'InvalidIndex' | 'InvalidTarget' | 'TooManyFields' | 'TooManyRegistrars' | 'AlreadyClaimed' | 'NotSub' | 'NotOwned' | 'JudgementForDifferentIdentity' | 'JudgementPaymentFailed';
+  }
+
+  /** @name PalletPreimageRequestStatus (488) */
+  interface PalletPreimageRequestStatus extends Enum {
+    readonly isUnrequested: boolean;
+    readonly asUnrequested: {
+      readonly deposit: ITuple<[AccountId32, u128]>;
+      readonly len: u32;
+    } & Struct;
+    readonly isRequested: boolean;
+    readonly asRequested: {
+      readonly deposit: Option<ITuple<[AccountId32, u128]>>;
+      readonly count: u32;
+      readonly len: Option<u32>;
+    } & Struct;
+    readonly type: 'Unrequested' | 'Requested';
+  }
+
+  /** @name PalletPreimageError (493) */
+  interface PalletPreimageError extends Enum {
+    readonly isTooBig: boolean;
+    readonly isAlreadyNoted: boolean;
+    readonly isNotAuthorized: boolean;
+    readonly isNotNoted: boolean;
+    readonly isRequested: boolean;
+    readonly isNotRequested: boolean;
+    readonly type: 'TooBig' | 'AlreadyNoted' | 'NotAuthorized' | 'NotNoted' | 'Requested' | 'NotRequested';
+  }
+
+  /** @name PalletDemocracyReferendumInfo (499) */
+  interface PalletDemocracyReferendumInfo extends Enum {
+    readonly isOngoing: boolean;
+    readonly asOngoing: PalletDemocracyReferendumStatus;
+    readonly isFinished: boolean;
+    readonly asFinished: {
+      readonly approved: bool;
+      readonly end: u32;
+    } & Struct;
+    readonly type: 'Ongoing' | 'Finished';
+  }
+
+  /** @name PalletDemocracyReferendumStatus (500) */
+  interface PalletDemocracyReferendumStatus extends Struct {
+    readonly end: u32;
+    readonly proposal: FrameSupportPreimagesBounded;
+    readonly threshold: PalletDemocracyVoteThreshold;
+    readonly delay: u32;
+    readonly tally: PalletDemocracyTally;
+  }
+
+  /** @name PalletDemocracyTally (501) */
+  interface PalletDemocracyTally extends Struct {
+    readonly ayes: u128;
+    readonly nays: u128;
+    readonly turnout: u128;
+  }
+
+  /** @name PalletDemocracyVoteVoting (502) */
+  interface PalletDemocracyVoteVoting extends Enum {
+    readonly isDirect: boolean;
+    readonly asDirect: {
+      readonly votes: Vec<ITuple<[u32, PalletDemocracyVoteAccountVote]>>;
+      readonly delegations: PalletDemocracyDelegations;
+      readonly prior: PalletDemocracyVotePriorLock;
+    } & Struct;
+    readonly isDelegating: boolean;
+    readonly asDelegating: {
+      readonly balance: u128;
+      readonly target: AccountId32;
+      readonly conviction: PalletDemocracyConviction;
+      readonly delegations: PalletDemocracyDelegations;
+      readonly prior: PalletDemocracyVotePriorLock;
+    } & Struct;
+    readonly type: 'Direct' | 'Delegating';
+  }
+
+  /** @name PalletDemocracyDelegations (506) */
+  interface PalletDemocracyDelegations extends Struct {
+    readonly votes: u128;
+    readonly capital: u128;
+  }
+
+  /** @name PalletDemocracyVotePriorLock (507) */
+  interface PalletDemocracyVotePriorLock extends ITuple<[u32, u128]> {}
+
+  /** @name PalletDemocracyError (510) */
+  interface PalletDemocracyError extends Enum {
+    readonly isValueLow: boolean;
+    readonly isProposalMissing: boolean;
+    readonly isAlreadyCanceled: boolean;
+    readonly isDuplicateProposal: boolean;
+    readonly isProposalBlacklisted: boolean;
+    readonly isNotSimpleMajority: boolean;
+    readonly isInvalidHash: boolean;
+    readonly isNoProposal: boolean;
+    readonly isAlreadyVetoed: boolean;
+    readonly isReferendumInvalid: boolean;
+    readonly isNoneWaiting: boolean;
+    readonly isNotVoter: boolean;
+    readonly isNoPermission: boolean;
+    readonly isAlreadyDelegating: boolean;
+    readonly isInsufficientFunds: boolean;
+    readonly isNotDelegating: boolean;
+    readonly isVotesExist: boolean;
+    readonly isInstantNotAllowed: boolean;
+    readonly isNonsense: boolean;
+    readonly isWrongUpperBound: boolean;
+    readonly isMaxVotesReached: boolean;
+    readonly isTooMany: boolean;
+    readonly isVotingPeriodLow: boolean;
+    readonly isPreimageNotExist: boolean;
+    readonly type: 'ValueLow' | 'ProposalMissing' | 'AlreadyCanceled' | 'DuplicateProposal' | 'ProposalBlacklisted' | 'NotSimpleMajority' | 'InvalidHash' | 'NoProposal' | 'AlreadyVetoed' | 'ReferendumInvalid' | 'NoneWaiting' | 'NotVoter' | 'NoPermission' | 'AlreadyDelegating' | 'InsufficientFunds' | 'NotDelegating' | 'VotesExist' | 'InstantNotAllowed' | 'Nonsense' | 'WrongUpperBound' | 'MaxVotesReached' | 'TooMany' | 'VotingPeriodLow' | 'PreimageNotExist';
+  }
+
+  /** @name PalletCollectiveVotes (512) */
+  interface PalletCollectiveVotes extends Struct {
+    readonly index: u32;
+    readonly threshold: u32;
+    readonly ayes: Vec<AccountId32>;
+    readonly nays: Vec<AccountId32>;
+    readonly end: u32;
+  }
+
+  /** @name PalletCollectiveError (513) */
+  interface PalletCollectiveError extends Enum {
+    readonly isNotMember: boolean;
+    readonly isDuplicateProposal: boolean;
+    readonly isProposalMissing: boolean;
+    readonly isWrongIndex: boolean;
+    readonly isDuplicateVote: boolean;
+    readonly isAlreadyInitialized: boolean;
+    readonly isTooEarly: boolean;
+    readonly isTooManyProposals: boolean;
+    readonly isWrongProposalWeight: boolean;
+    readonly isWrongProposalLength: boolean;
+    readonly isPrimeAccountNotMember: boolean;
+    readonly type: 'NotMember' | 'DuplicateProposal' | 'ProposalMissing' | 'WrongIndex' | 'DuplicateVote' | 'AlreadyInitialized' | 'TooEarly' | 'TooManyProposals' | 'WrongProposalWeight' | 'WrongProposalLength' | 'PrimeAccountNotMember';
+  }
+
+  /** @name PalletMembershipError (517) */
+  interface PalletMembershipError extends Enum {
+    readonly isAlreadyMember: boolean;
+    readonly isNotMember: boolean;
+    readonly isTooManyMembers: boolean;
+    readonly type: 'AlreadyMember' | 'NotMember' | 'TooManyMembers';
+  }
+
+  /** @name PalletRankedCollectiveMemberRecord (520) */
+  interface PalletRankedCollectiveMemberRecord extends Struct {
+    readonly rank: u16;
+  }
+
+  /** @name PalletRankedCollectiveError (525) */
+  interface PalletRankedCollectiveError extends Enum {
+    readonly isAlreadyMember: boolean;
+    readonly isNotMember: boolean;
+    readonly isNotPolling: boolean;
+    readonly isOngoing: boolean;
+    readonly isNoneRemaining: boolean;
+    readonly isCorruption: boolean;
+    readonly isRankTooLow: boolean;
+    readonly isInvalidWitness: boolean;
+    readonly isNoPermission: boolean;
+    readonly type: 'AlreadyMember' | 'NotMember' | 'NotPolling' | 'Ongoing' | 'NoneRemaining' | 'Corruption' | 'RankTooLow' | 'InvalidWitness' | 'NoPermission';
+  }
+
+  /** @name PalletReferendaReferendumInfo (526) */
+  interface PalletReferendaReferendumInfo extends Enum {
+    readonly isOngoing: boolean;
+    readonly asOngoing: PalletReferendaReferendumStatus;
+    readonly isApproved: boolean;
+    readonly asApproved: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+    readonly isRejected: boolean;
+    readonly asRejected: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+    readonly isCancelled: boolean;
+    readonly asCancelled: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+    readonly isTimedOut: boolean;
+    readonly asTimedOut: ITuple<[u32, Option<PalletReferendaDeposit>, Option<PalletReferendaDeposit>]>;
+    readonly isKilled: boolean;
+    readonly asKilled: u32;
+    readonly type: 'Ongoing' | 'Approved' | 'Rejected' | 'Cancelled' | 'TimedOut' | 'Killed';
+  }
+
+  /** @name PalletReferendaReferendumStatus (527) */
+  interface PalletReferendaReferendumStatus extends Struct {
+    readonly track: u16;
+    readonly origin: OpalRuntimeOriginCaller;
+    readonly proposal: FrameSupportPreimagesBounded;
+    readonly enactment: FrameSupportScheduleDispatchTime;
+    readonly submitted: u32;
+    readonly submissionDeposit: PalletReferendaDeposit;
+    readonly decisionDeposit: Option<PalletReferendaDeposit>;
+    readonly deciding: Option<PalletReferendaDecidingStatus>;
+    readonly tally: PalletRankedCollectiveTally;
+    readonly inQueue: bool;
+    readonly alarm: Option<ITuple<[u32, ITuple<[u32, u32]>]>>;
+  }
+
+  /** @name PalletReferendaDeposit (528) */
+  interface PalletReferendaDeposit extends Struct {
+    readonly who: AccountId32;
+    readonly amount: u128;
+  }
+
+  /** @name PalletReferendaDecidingStatus (531) */
+  interface PalletReferendaDecidingStatus extends Struct {
+    readonly since: u32;
+    readonly confirming: Option<u32>;
+  }
+
+  /** @name PalletReferendaTrackInfo (537) */
+  interface PalletReferendaTrackInfo extends Struct {
+    readonly name: Text;
+    readonly maxDeciding: u32;
+    readonly decisionDeposit: u128;
+    readonly preparePeriod: u32;
+    readonly decisionPeriod: u32;
+    readonly confirmPeriod: u32;
+    readonly minEnactmentPeriod: u32;
+    readonly minApproval: PalletReferendaCurve;
+    readonly minSupport: PalletReferendaCurve;
+  }
+
+  /** @name PalletReferendaCurve (538) */
+  interface PalletReferendaCurve extends Enum {
+    readonly isLinearDecreasing: boolean;
+    readonly asLinearDecreasing: {
+      readonly length: Perbill;
+      readonly floor: Perbill;
+      readonly ceil: Perbill;
+    } & Struct;
+    readonly isSteppedDecreasing: boolean;
+    readonly asSteppedDecreasing: {
+      readonly begin: Perbill;
+      readonly end: Perbill;
+      readonly step: Perbill;
+      readonly period: Perbill;
+    } & Struct;
+    readonly isReciprocal: boolean;
+    readonly asReciprocal: {
+      readonly factor: i64;
+      readonly xOffset: i64;
+      readonly yOffset: i64;
+    } & Struct;
+    readonly type: 'LinearDecreasing' | 'SteppedDecreasing' | 'Reciprocal';
+  }
+
+  /** @name PalletReferendaError (541) */
+  interface PalletReferendaError extends Enum {
+    readonly isNotOngoing: boolean;
+    readonly isHasDeposit: boolean;
+    readonly isBadTrack: boolean;
+    readonly isFull: boolean;
+    readonly isQueueEmpty: boolean;
+    readonly isBadReferendum: boolean;
+    readonly isNothingToDo: boolean;
+    readonly isNoTrack: boolean;
+    readonly isUnfinished: boolean;
+    readonly isNoPermission: boolean;
+    readonly isNoDeposit: boolean;
+    readonly isBadStatus: boolean;
+    readonly isPreimageNotExist: boolean;
+    readonly type: 'NotOngoing' | 'HasDeposit' | 'BadTrack' | 'Full' | 'QueueEmpty' | 'BadReferendum' | 'NothingToDo' | 'NoTrack' | 'Unfinished' | 'NoPermission' | 'NoDeposit' | 'BadStatus' | 'PreimageNotExist';
+  }
+
+  /** @name PalletSchedulerScheduled (544) */
+  interface PalletSchedulerScheduled extends Struct {
+    readonly maybeId: Option<U8aFixed>;
+    readonly priority: u8;
+    readonly call: FrameSupportPreimagesBounded;
+    readonly maybePeriodic: Option<ITuple<[u32, u32]>>;
+    readonly origin: OpalRuntimeOriginCaller;
+  }
+
+  /** @name PalletSchedulerError (546) */
+  interface PalletSchedulerError extends Enum {
+    readonly isFailedToSchedule: boolean;
+    readonly isNotFound: boolean;
+    readonly isTargetBlockNumberInPast: boolean;
+    readonly isRescheduleNoChange: boolean;
+    readonly isNamed: boolean;
+    readonly type: 'FailedToSchedule' | 'NotFound' | 'TargetBlockNumberInPast' | 'RescheduleNoChange' | 'Named';
+  }
+
+  /** @name CumulusPalletXcmpQueueInboundChannelDetails (548) */
+  interface CumulusPalletXcmpQueueInboundChannelDetails extends Struct {
+    readonly sender: u32;
+    readonly state: CumulusPalletXcmpQueueInboundState;
+    readonly messageMetadata: Vec<ITuple<[u32, PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat]>>;
+  }
+
+  /** @name CumulusPalletXcmpQueueInboundState (549) */
+  interface CumulusPalletXcmpQueueInboundState extends Enum {
+    readonly isOk: boolean;
+    readonly isSuspended: boolean;
+    readonly type: 'Ok' | 'Suspended';
+  }
+
+  /** @name PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat (552) */
+  interface PolkadotParachainPrimitivesPrimitivesXcmpMessageFormat extends Enum {
+    readonly isConcatenatedVersionedXcm: boolean;
+    readonly isConcatenatedEncodedBlob: boolean;
+    readonly isSignals: boolean;
+    readonly type: 'ConcatenatedVersionedXcm' | 'ConcatenatedEncodedBlob' | 'Signals';
+  }
+
+  /** @name CumulusPalletXcmpQueueOutboundChannelDetails (555) */
+  interface CumulusPalletXcmpQueueOutboundChannelDetails extends Struct {
+    readonly recipient: u32;
+    readonly state: CumulusPalletXcmpQueueOutboundState;
+    readonly signalsExist: bool;
+    readonly firstIndex: u16;
+    readonly lastIndex: u16;
+  }
+
+  /** @name CumulusPalletXcmpQueueOutboundState (556) */
+  interface CumulusPalletXcmpQueueOutboundState extends Enum {
+    readonly isOk: boolean;
+    readonly isSuspended: boolean;
+    readonly type: 'Ok' | 'Suspended';
+  }
+
+  /** @name CumulusPalletXcmpQueueQueueConfigData (558) */
+  interface CumulusPalletXcmpQueueQueueConfigData extends Struct {
+    readonly suspendThreshold: u32;
+    readonly dropThreshold: u32;
+    readonly resumeThreshold: u32;
+    readonly thresholdWeight: SpWeightsWeightV2Weight;
+    readonly weightRestrictDecay: SpWeightsWeightV2Weight;
+    readonly xcmpMaxIndividualWeight: SpWeightsWeightV2Weight;
+  }
+
+  /** @name CumulusPalletXcmpQueueError (560) */
+  interface CumulusPalletXcmpQueueError extends Enum {
+    readonly isFailedToSend: boolean;
+    readonly isBadXcmOrigin: boolean;
+    readonly isBadXcm: boolean;
+    readonly isBadOverweightIndex: boolean;
+    readonly isWeightOverLimit: boolean;
+    readonly type: 'FailedToSend' | 'BadXcmOrigin' | 'BadXcm' | 'BadOverweightIndex' | 'WeightOverLimit';
+  }
+
+  /** @name PalletXcmQueryStatus (561) */
+  interface PalletXcmQueryStatus extends Enum {
+    readonly isPending: boolean;
+    readonly asPending: {
+      readonly responder: StagingXcmVersionedMultiLocation;
+      readonly maybeMatchQuerier: Option<StagingXcmVersionedMultiLocation>;
+      readonly maybeNotify: Option<ITuple<[u8, u8]>>;
+      readonly timeout: u32;
+    } & Struct;
+    readonly isVersionNotifier: boolean;
+    readonly asVersionNotifier: {
+      readonly origin: StagingXcmVersionedMultiLocation;
+      readonly isActive: bool;
+    } & Struct;
+    readonly isReady: boolean;
+    readonly asReady: {
+      readonly response: StagingXcmVersionedResponse;
+      readonly at: u32;
+    } & Struct;
+    readonly type: 'Pending' | 'VersionNotifier' | 'Ready';
+  }
+
+  /** @name StagingXcmVersionedResponse (565) */
+  interface StagingXcmVersionedResponse extends Enum {
+    readonly isV2: boolean;
+    readonly asV2: StagingXcmV2Response;
+    readonly isV3: boolean;
+    readonly asV3: StagingXcmV3Response;
+    readonly type: 'V2' | 'V3';
+  }
+
+  /** @name PalletXcmVersionMigrationStage (571) */
+  interface PalletXcmVersionMigrationStage extends Enum {
+    readonly isMigrateSupportedVersion: boolean;
+    readonly isMigrateVersionNotifiers: boolean;
+    readonly isNotifyCurrentTargets: boolean;
+    readonly asNotifyCurrentTargets: Option<Bytes>;
+    readonly isMigrateAndNotifyOldTargets: boolean;
+    readonly type: 'MigrateSupportedVersion' | 'MigrateVersionNotifiers' | 'NotifyCurrentTargets' | 'MigrateAndNotifyOldTargets';
+  }
+
+  /** @name StagingXcmVersionedAssetId (574) */
+  interface StagingXcmVersionedAssetId extends Enum {
+    readonly isV3: boolean;
+    readonly asV3: StagingXcmV3MultiassetAssetId;
+    readonly type: 'V3';
+  }
+
+  /** @name PalletXcmRemoteLockedFungibleRecord (575) */
+  interface PalletXcmRemoteLockedFungibleRecord extends Struct {
+    readonly amount: u128;
+    readonly owner: StagingXcmVersionedMultiLocation;
+    readonly locker: StagingXcmVersionedMultiLocation;
+    readonly consumers: Vec<ITuple<[Null, u128]>>;
+  }
+
+  /** @name PalletXcmError (582) */
+  interface PalletXcmError extends Enum {
+    readonly isUnreachable: boolean;
+    readonly isSendFailure: boolean;
+    readonly isFiltered: boolean;
+    readonly isUnweighableMessage: boolean;
+    readonly isDestinationNotInvertible: boolean;
+    readonly isEmpty: boolean;
+    readonly isCannotReanchor: boolean;
+    readonly isTooManyAssets: boolean;
+    readonly isInvalidOrigin: boolean;
+    readonly isBadVersion: boolean;
+    readonly isBadLocation: boolean;
+    readonly isNoSubscription: boolean;
+    readonly isAlreadySubscribed: boolean;
+    readonly isInvalidAsset: boolean;
+    readonly isLowBalance: boolean;
+    readonly isTooManyLocks: boolean;
+    readonly isAccountNotSovereign: boolean;
+    readonly isFeesNotMet: boolean;
+    readonly isLockNotFound: boolean;
+    readonly isInUse: boolean;
+    readonly type: 'Unreachable' | 'SendFailure' | 'Filtered' | 'UnweighableMessage' | 'DestinationNotInvertible' | 'Empty' | 'CannotReanchor' | 'TooManyAssets' | 'InvalidOrigin' | 'BadVersion' | 'BadLocation' | 'NoSubscription' | 'AlreadySubscribed' | 'InvalidAsset' | 'LowBalance' | 'TooManyLocks' | 'AccountNotSovereign' | 'FeesNotMet' | 'LockNotFound' | 'InUse';
+  }
+
+  /** @name CumulusPalletXcmError (583) */
+  type CumulusPalletXcmError = Null;
+
+  /** @name CumulusPalletDmpQueueConfigData (584) */
+  interface CumulusPalletDmpQueueConfigData extends Struct {
+    readonly maxIndividual: SpWeightsWeightV2Weight;
+  }
+
+  /** @name CumulusPalletDmpQueuePageIndexData (585) */
+  interface CumulusPalletDmpQueuePageIndexData extends Struct {
+    readonly beginUsed: u32;
+    readonly endUsed: u32;
+    readonly overweightCount: u64;
+  }
+
+  /** @name CumulusPalletDmpQueueError (588) */
+  interface CumulusPalletDmpQueueError extends Enum {
+    readonly isUnknown: boolean;
+    readonly isOverLimit: boolean;
+    readonly type: 'Unknown' | 'OverLimit';
+  }
+
+  /** @name PalletUniqueError (592) */
+  interface PalletUniqueError extends Enum {
+    readonly isCollectionDecimalPointLimitExceeded: boolean;
+    readonly isEmptyArgument: boolean;
+    readonly isRepartitionCalledOnNonRefungibleCollection: boolean;
+    readonly type: 'CollectionDecimalPointLimitExceeded' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection';
+  }
+
+  /** @name PalletConfigurationError (593) */
+  interface PalletConfigurationError extends Enum {
+    readonly isInconsistentConfiguration: boolean;
+    readonly type: 'InconsistentConfiguration';
+  }
+
+  /** @name UpDataStructsCollection (594) */
+  interface UpDataStructsCollection extends Struct {
+    readonly owner: AccountId32;
+    readonly mode: UpDataStructsCollectionMode;
+    readonly name: Vec<u16>;
+    readonly description: Vec<u16>;
+    readonly tokenPrefix: Bytes;
+    readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
+    readonly limits: UpDataStructsCollectionLimits;
+    readonly permissions: UpDataStructsCollectionPermissions;
+    readonly flags: U8aFixed;
+  }
+
+  /** @name UpDataStructsSponsorshipStateAccountId32 (595) */
+  interface UpDataStructsSponsorshipStateAccountId32 extends Enum {
+    readonly isDisabled: boolean;
+    readonly isUnconfirmed: boolean;
+    readonly asUnconfirmed: AccountId32;
+    readonly isConfirmed: boolean;
+    readonly asConfirmed: AccountId32;
+    readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
+  }
+
+  /** @name UpDataStructsProperties (596) */
+  interface UpDataStructsProperties extends Struct {
+    readonly map: UpDataStructsPropertiesMapBoundedVec;
+    readonly consumedSpace: u32;
+    readonly reserved: u32;
+  }
+
+  /** @name UpDataStructsPropertiesMapBoundedVec (597) */
+  interface UpDataStructsPropertiesMapBoundedVec extends BTreeMap<Bytes, Bytes> {}
+
+  /** @name UpDataStructsPropertiesMapPropertyPermission (602) */
+  interface UpDataStructsPropertiesMapPropertyPermission extends BTreeMap<Bytes, UpDataStructsPropertyPermission> {}
+
+  /** @name UpDataStructsCollectionStats (609) */
+  interface UpDataStructsCollectionStats extends Struct {
+    readonly created: u32;
+    readonly destroyed: u32;
+    readonly alive: u32;
+  }
+
+  /** @name UpDataStructsTokenChild (610) */
+  interface UpDataStructsTokenChild extends Struct {
+    readonly token: u32;
+    readonly collection: u32;
+  }
+
+  /** @name PhantomTypeUpDataStructs (611) */
+  interface PhantomTypeUpDataStructs extends Vec<ITuple<[UpDataStructsTokenData, UpDataStructsRpcCollection, UpPovEstimateRpcPovInfo]>> {}
+
+  /** @name UpDataStructsTokenData (613) */
+  interface UpDataStructsTokenData extends Struct {
+    readonly properties: Vec<UpDataStructsProperty>;
+    readonly owner: Option<PalletEvmAccountBasicCrossAccountIdRepr>;
+    readonly pieces: u128;
+  }
+
+  /** @name UpDataStructsRpcCollection (614) */
+  interface UpDataStructsRpcCollection extends Struct {
+    readonly owner: AccountId32;
+    readonly mode: UpDataStructsCollectionMode;
+    readonly name: Vec<u16>;
+    readonly description: Vec<u16>;
+    readonly tokenPrefix: Bytes;
+    readonly sponsorship: UpDataStructsSponsorshipStateAccountId32;
+    readonly limits: UpDataStructsCollectionLimits;
+    readonly permissions: UpDataStructsCollectionPermissions;
+    readonly tokenPropertyPermissions: Vec<UpDataStructsPropertyKeyPermission>;
+    readonly properties: Vec<UpDataStructsProperty>;
+    readonly readOnly: bool;
+    readonly flags: UpDataStructsRpcCollectionFlags;
+  }
+
+  /** @name UpDataStructsRpcCollectionFlags (615) */
+  interface UpDataStructsRpcCollectionFlags extends Struct {
+    readonly foreign: bool;
+    readonly erc721metadata: bool;
+  }
+
+  /** @name UpPovEstimateRpcPovInfo (616) */
+  interface UpPovEstimateRpcPovInfo extends Struct {
+    readonly proofSize: u64;
+    readonly compactProofSize: u64;
+    readonly compressedProofSize: u64;
+    readonly results: Vec<Result<Result<Null, SpRuntimeDispatchError>, SpRuntimeTransactionValidityTransactionValidityError>>;
+    readonly keyValues: Vec<UpPovEstimateRpcTrieKeyValue>;
+  }
+
+  /** @name SpRuntimeTransactionValidityTransactionValidityError (619) */
+  interface SpRuntimeTransactionValidityTransactionValidityError extends Enum {
+    readonly isInvalid: boolean;
+    readonly asInvalid: SpRuntimeTransactionValidityInvalidTransaction;
+    readonly isUnknown: boolean;
+    readonly asUnknown: SpRuntimeTransactionValidityUnknownTransaction;
+    readonly type: 'Invalid' | 'Unknown';
+  }
+
+  /** @name SpRuntimeTransactionValidityInvalidTransaction (620) */
+  interface SpRuntimeTransactionValidityInvalidTransaction extends Enum {
+    readonly isCall: boolean;
+    readonly isPayment: boolean;
+    readonly isFuture: boolean;
+    readonly isStale: boolean;
+    readonly isBadProof: boolean;
+    readonly isAncientBirthBlock: boolean;
+    readonly isExhaustsResources: boolean;
+    readonly isCustom: boolean;
+    readonly asCustom: u8;
+    readonly isBadMandatory: boolean;
+    readonly isMandatoryValidation: boolean;
+    readonly isBadSigner: boolean;
+    readonly type: 'Call' | 'Payment' | 'Future' | 'Stale' | 'BadProof' | 'AncientBirthBlock' | 'ExhaustsResources' | 'Custom' | 'BadMandatory' | 'MandatoryValidation' | 'BadSigner';
+  }
+
+  /** @name SpRuntimeTransactionValidityUnknownTransaction (621) */
+  interface SpRuntimeTransactionValidityUnknownTransaction extends Enum {
+    readonly isCannotLookup: boolean;
+    readonly isNoUnsignedValidator: boolean;
+    readonly isCustom: boolean;
+    readonly asCustom: u8;
+    readonly type: 'CannotLookup' | 'NoUnsignedValidator' | 'Custom';
+  }
+
+  /** @name UpPovEstimateRpcTrieKeyValue (623) */
+  interface UpPovEstimateRpcTrieKeyValue extends Struct {
+    readonly key: Bytes;
+    readonly value: Bytes;
+  }
+
+  /** @name PalletCommonError (625) */
+  interface PalletCommonError extends Enum {
+    readonly isCollectionNotFound: boolean;
+    readonly isMustBeTokenOwner: boolean;
+    readonly isNoPermission: boolean;
+    readonly isCantDestroyNotEmptyCollection: boolean;
+    readonly isPublicMintingNotAllowed: boolean;
+    readonly isAddressNotInAllowlist: boolean;
+    readonly isCollectionNameLimitExceeded: boolean;
+    readonly isCollectionDescriptionLimitExceeded: boolean;
+    readonly isCollectionTokenPrefixLimitExceeded: boolean;
+    readonly isTotalCollectionsLimitExceeded: boolean;
+    readonly isCollectionAdminCountExceeded: boolean;
+    readonly isCollectionLimitBoundsExceeded: boolean;
+    readonly isOwnerPermissionsCantBeReverted: boolean;
+    readonly isTransferNotAllowed: boolean;
+    readonly isAccountTokenLimitExceeded: boolean;
+    readonly isCollectionTokenLimitExceeded: boolean;
+    readonly isMetadataFlagFrozen: boolean;
+    readonly isTokenNotFound: boolean;
+    readonly isTokenValueTooLow: boolean;
+    readonly isApprovedValueTooLow: boolean;
+    readonly isCantApproveMoreThanOwned: boolean;
+    readonly isAddressIsNotEthMirror: boolean;
+    readonly isAddressIsZero: boolean;
+    readonly isUnsupportedOperation: boolean;
+    readonly isNotSufficientFounds: boolean;
+    readonly isUserIsNotAllowedToNest: boolean;
+    readonly isSourceCollectionIsNotAllowedToNest: boolean;
+    readonly isCollectionFieldSizeExceeded: boolean;
+    readonly isNoSpaceForProperty: boolean;
+    readonly isPropertyLimitReached: boolean;
+    readonly isPropertyKeyIsTooLong: boolean;
+    readonly isInvalidCharacterInPropertyKey: boolean;
+    readonly isEmptyPropertyKey: boolean;
+    readonly isCollectionIsExternal: boolean;
+    readonly isCollectionIsInternal: boolean;
+    readonly isConfirmSponsorshipFail: boolean;
+    readonly isUserIsNotCollectionAdmin: boolean;
+    readonly isFungibleItemsHaveNoId: boolean;
+    readonly type: 'CollectionNotFound' | 'MustBeTokenOwner' | 'NoPermission' | 'CantDestroyNotEmptyCollection' | 'PublicMintingNotAllowed' | 'AddressNotInAllowlist' | 'CollectionNameLimitExceeded' | 'CollectionDescriptionLimitExceeded' | 'CollectionTokenPrefixLimitExceeded' | 'TotalCollectionsLimitExceeded' | 'CollectionAdminCountExceeded' | 'CollectionLimitBoundsExceeded' | 'OwnerPermissionsCantBeReverted' | 'TransferNotAllowed' | 'AccountTokenLimitExceeded' | 'CollectionTokenLimitExceeded' | 'MetadataFlagFrozen' | 'TokenNotFound' | 'TokenValueTooLow' | 'ApprovedValueTooLow' | 'CantApproveMoreThanOwned' | 'AddressIsNotEthMirror' | 'AddressIsZero' | 'UnsupportedOperation' | 'NotSufficientFounds' | 'UserIsNotAllowedToNest' | 'SourceCollectionIsNotAllowedToNest' | 'CollectionFieldSizeExceeded' | 'NoSpaceForProperty' | 'PropertyLimitReached' | 'PropertyKeyIsTooLong' | 'InvalidCharacterInPropertyKey' | 'EmptyPropertyKey' | 'CollectionIsExternal' | 'CollectionIsInternal' | 'ConfirmSponsorshipFail' | 'UserIsNotCollectionAdmin' | 'FungibleItemsHaveNoId';
+  }
+
+  /** @name PalletFungibleError (627) */
+  interface PalletFungibleError extends Enum {
+    readonly isNotFungibleDataUsedToMintFungibleCollectionToken: boolean;
+    readonly isFungibleItemsDontHaveData: boolean;
+    readonly isFungibleDisallowsNesting: boolean;
+    readonly isSettingPropertiesNotAllowed: boolean;
+    readonly isSettingAllowanceForAllNotAllowed: boolean;
+    readonly isFungibleTokensAreAlwaysValid: boolean;
+    readonly type: 'NotFungibleDataUsedToMintFungibleCollectionToken' | 'FungibleItemsDontHaveData' | 'FungibleDisallowsNesting' | 'SettingPropertiesNotAllowed' | 'SettingAllowanceForAllNotAllowed' | 'FungibleTokensAreAlwaysValid';
+  }
+
+  /** @name PalletRefungibleError (632) */
+  interface PalletRefungibleError extends Enum {
+    readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean;
+    readonly isWrongRefungiblePieces: boolean;
+    readonly isRepartitionWhileNotOwningAllPieces: boolean;
+    readonly isRefungibleDisallowsNesting: boolean;
+    readonly isSettingPropertiesNotAllowed: boolean;
+    readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed';
+  }
+
+  /** @name PalletNonfungibleItemData (633) */
+  interface PalletNonfungibleItemData extends Struct {
+    readonly owner: PalletEvmAccountBasicCrossAccountIdRepr;
+  }
+
+  /** @name UpDataStructsPropertyScope (635) */
+  interface UpDataStructsPropertyScope extends Enum {
+    readonly isNone: boolean;
+    readonly isRmrk: boolean;
+    readonly type: 'None' | 'Rmrk';
+  }
+
+  /** @name PalletNonfungibleError (638) */
+  interface PalletNonfungibleError extends Enum {
+    readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean;
+    readonly isNonfungibleItemsHaveNoAmount: boolean;
+    readonly isCantBurnNftWithChildren: boolean;
+    readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren';
+  }
+
+  /** @name PalletStructureError (639) */
+  interface PalletStructureError extends Enum {
+    readonly isOuroborosDetected: boolean;
+    readonly isDepthLimit: boolean;
+    readonly isBreadthLimit: boolean;
+    readonly isTokenNotFound: boolean;
+    readonly isCantNestTokenUnderCollection: boolean;
+    readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound' | 'CantNestTokenUnderCollection';
+  }
+
+  /** @name PalletAppPromotionError (644) */
+  interface PalletAppPromotionError extends Enum {
+    readonly isAdminNotSet: boolean;
+    readonly isNoPermission: boolean;
+    readonly isNotSufficientFunds: boolean;
+    readonly isPendingForBlockOverflow: boolean;
+    readonly isSponsorNotSet: boolean;
+    readonly isInsufficientStakedBalance: boolean;
+    readonly isInconsistencyState: boolean;
+    readonly type: 'AdminNotSet' | 'NoPermission' | 'NotSufficientFunds' | 'PendingForBlockOverflow' | 'SponsorNotSet' | 'InsufficientStakedBalance' | 'InconsistencyState';
+  }
+
+  /** @name PalletForeignAssetsModuleError (645) */
+  interface PalletForeignAssetsModuleError extends Enum {
+    readonly isBadLocation: boolean;
+    readonly isMultiLocationExisted: boolean;
+    readonly isAssetIdNotExists: boolean;
+    readonly isAssetIdExisted: boolean;
+    readonly type: 'BadLocation' | 'MultiLocationExisted' | 'AssetIdNotExists' | 'AssetIdExisted';
+  }
+
+  /** @name PalletEvmCodeMetadata (646) */
+  interface PalletEvmCodeMetadata extends Struct {
+    readonly size_: u64;
+    readonly hash_: H256;
+  }
+
+  /** @name PalletEvmError (648) */
+  interface PalletEvmError extends Enum {
+    readonly isBalanceLow: boolean;
+    readonly isFeeOverflow: boolean;
+    readonly isPaymentOverflow: boolean;
+    readonly isWithdrawFailed: boolean;
+    readonly isGasPriceTooLow: boolean;
+    readonly isInvalidNonce: boolean;
+    readonly isGasLimitTooLow: boolean;
+    readonly isGasLimitTooHigh: boolean;
+    readonly isUndefined: boolean;
+    readonly isReentrancy: boolean;
+    readonly isTransactionMustComeFromEOA: boolean;
+    readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce' | 'GasLimitTooLow' | 'GasLimitTooHigh' | 'Undefined' | 'Reentrancy' | 'TransactionMustComeFromEOA';
+  }
+
+  /** @name FpRpcTransactionStatus (651) */
+  interface FpRpcTransactionStatus extends Struct {
+    readonly transactionHash: H256;
+    readonly transactionIndex: u32;
+    readonly from: H160;
+    readonly to: Option<H160>;
+    readonly contractAddress: Option<H160>;
+    readonly logs: Vec<EthereumLog>;
+    readonly logsBloom: EthbloomBloom;
+  }
+
+  /** @name EthbloomBloom (653) */
+  interface EthbloomBloom extends U8aFixed {}
+
+  /** @name EthereumReceiptReceiptV3 (655) */
+  interface EthereumReceiptReceiptV3 extends Enum {
+    readonly isLegacy: boolean;
+    readonly asLegacy: EthereumReceiptEip658ReceiptData;
+    readonly isEip2930: boolean;
+    readonly asEip2930: EthereumReceiptEip658ReceiptData;
+    readonly isEip1559: boolean;
+    readonly asEip1559: EthereumReceiptEip658ReceiptData;
+    readonly type: 'Legacy' | 'Eip2930' | 'Eip1559';
+  }
+
+  /** @name EthereumReceiptEip658ReceiptData (656) */
+  interface EthereumReceiptEip658ReceiptData extends Struct {
+    readonly statusCode: u8;
+    readonly usedGas: U256;
+    readonly logsBloom: EthbloomBloom;
+    readonly logs: Vec<EthereumLog>;
+  }
+
+  /** @name EthereumBlock (657) */
+  interface EthereumBlock extends Struct {
+    readonly header: EthereumHeader;
+    readonly transactions: Vec<EthereumTransactionTransactionV2>;
+    readonly ommers: Vec<EthereumHeader>;
+  }
+
+  /** @name EthereumHeader (658) */
+  interface EthereumHeader extends Struct {
+    readonly parentHash: H256;
+    readonly ommersHash: H256;
+    readonly beneficiary: H160;
+    readonly stateRoot: H256;
+    readonly transactionsRoot: H256;
+    readonly receiptsRoot: H256;
+    readonly logsBloom: EthbloomBloom;
+    readonly difficulty: U256;
+    readonly number: U256;
+    readonly gasLimit: U256;
+    readonly gasUsed: U256;
+    readonly timestamp: u64;
+    readonly extraData: Bytes;
+    readonly mixHash: H256;
+    readonly nonce: EthereumTypesHashH64;
+  }
+
+  /** @name EthereumTypesHashH64 (659) */
+  interface EthereumTypesHashH64 extends U8aFixed {}
+
+  /** @name PalletEthereumError (664) */
+  interface PalletEthereumError extends Enum {
+    readonly isInvalidSignature: boolean;
+    readonly isPreLogExists: boolean;
+    readonly type: 'InvalidSignature' | 'PreLogExists';
+  }
+
+  /** @name PalletEvmCoderSubstrateError (665) */
+  interface PalletEvmCoderSubstrateError extends Enum {
+    readonly isOutOfGas: boolean;
+    readonly isOutOfFund: boolean;
+    readonly type: 'OutOfGas' | 'OutOfFund';
+  }
+
+  /** @name UpDataStructsSponsorshipStateBasicCrossAccountIdRepr (666) */
+  interface UpDataStructsSponsorshipStateBasicCrossAccountIdRepr extends Enum {
+    readonly isDisabled: boolean;
+    readonly isUnconfirmed: boolean;
+    readonly asUnconfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly isConfirmed: boolean;
+    readonly asConfirmed: PalletEvmAccountBasicCrossAccountIdRepr;
+    readonly type: 'Disabled' | 'Unconfirmed' | 'Confirmed';
+  }
+
+  /** @name PalletEvmContractHelpersSponsoringModeT (667) */
+  interface PalletEvmContractHelpersSponsoringModeT extends Enum {
+    readonly isDisabled: boolean;
+    readonly isAllowlisted: boolean;
+    readonly isGenerous: boolean;
+    readonly type: 'Disabled' | 'Allowlisted' | 'Generous';
+  }
+
+  /** @name PalletEvmContractHelpersError (673) */
+  interface PalletEvmContractHelpersError extends Enum {
+    readonly isNoPermission: boolean;
+    readonly isNoPendingSponsor: boolean;
+    readonly isTooManyMethodsHaveSponsoredLimit: boolean;
+    readonly type: 'NoPermission' | 'NoPendingSponsor' | 'TooManyMethodsHaveSponsoredLimit';
+  }
+
+  /** @name PalletEvmMigrationError (674) */
+  interface PalletEvmMigrationError extends Enum {
+    readonly isAccountNotEmpty: boolean;
+    readonly isAccountIsNotMigrating: boolean;
+    readonly isBadEvent: boolean;
+    readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating' | 'BadEvent';
+  }
+
+  /** @name PalletMaintenanceError (675) */
+  type PalletMaintenanceError = Null;
+
+  /** @name PalletUtilityError (676) */
+  interface PalletUtilityError extends Enum {
+    readonly isTooManyCalls: boolean;
+    readonly type: 'TooManyCalls';
+  }
+
+  /** @name PalletTestUtilsError (677) */
+  interface PalletTestUtilsError extends Enum {
+    readonly isTestPalletDisabled: boolean;
+    readonly isTriggerRollback: boolean;
+    readonly type: 'TestPalletDisabled' | 'TriggerRollback';
+  }
+
+  /** @name SpRuntimeMultiSignature (679) */
+  interface SpRuntimeMultiSignature extends Enum {
+    readonly isEd25519: boolean;
+    readonly asEd25519: SpCoreEd25519Signature;
+    readonly isSr25519: boolean;
+    readonly asSr25519: SpCoreSr25519Signature;
+    readonly isEcdsa: boolean;
+    readonly asEcdsa: SpCoreEcdsaSignature;
+    readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa';
+  }
+
+  /** @name SpCoreEd25519Signature (680) */
+  interface SpCoreEd25519Signature extends U8aFixed {}
+
+  /** @name SpCoreSr25519Signature (682) */
+  interface SpCoreSr25519Signature extends U8aFixed {}
+
+  /** @name SpCoreEcdsaSignature (683) */
+  interface SpCoreEcdsaSignature extends U8aFixed {}
+
+  /** @name FrameSystemExtensionsCheckSpecVersion (686) */
+  type FrameSystemExtensionsCheckSpecVersion = Null;
+
+  /** @name FrameSystemExtensionsCheckTxVersion (687) */
+  type FrameSystemExtensionsCheckTxVersion = Null;
+
+  /** @name FrameSystemExtensionsCheckGenesis (688) */
+  type FrameSystemExtensionsCheckGenesis = Null;
+
+  /** @name FrameSystemExtensionsCheckNonce (691) */
+  interface FrameSystemExtensionsCheckNonce extends Compact<u32> {}
+
+  /** @name FrameSystemExtensionsCheckWeight (692) */
+  type FrameSystemExtensionsCheckWeight = Null;
+
+  /** @name OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance (693) */
+  type OpalRuntimeRuntimeCommonMaintenanceCheckMaintenance = Null;
+
+  /** @name OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls (694) */
+  type OpalRuntimeRuntimeCommonIdentityDisableIdentityCalls = Null;
+
+  /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (695) */
+  interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact<u128> {}
+
+  /** @name OpalRuntimeRuntime (696) */
+  type OpalRuntimeRuntime = Null;
+
+  /** @name PalletEthereumFakeTransactionFinalizer (697) */
+  type PalletEthereumFakeTransactionFinalizer = Null;
+
+} // declare module
addedjs-packages/types/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/types.ts
@@ -0,0 +1,7 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export * from './appPromotion/types.js';
+export * from './default/types.js';
+export * from './povinfo/types.js';
+export * from './unique/types.js';
addedjs-packages/types/unique/definitions.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/unique/definitions.ts
@@ -0,0 +1,184 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+type RpcParam = {
+  name: string;
+  type: string;
+  isOptional?: true;
+};
+
+const CROSS_ACCOUNT_ID_TYPE = 'PalletEvmAccountBasicCrossAccountIdRepr';
+
+const collectionParam = {name: 'collection', type: 'u32'};
+const tokenParam = {name: 'tokenId', type: 'u32'};
+const propertyKeysParam = {name: 'propertyKeys', type: 'Option<Vec<String>>', isOptional: true};
+const crossAccountParam = (name = 'account') => ({name, type: CROSS_ACCOUNT_ID_TYPE});
+const atParam = {name: 'at', type: 'Hash', isOptional: true};
+
+const fun = (description: string, params: RpcParam[], type: string) => ({
+  description,
+  params: [...params, atParam],
+  type,
+});
+
+export default {
+  types: {},
+  rpc: {
+    accountTokens: fun(
+      'Get tokens owned by an account in a collection',
+      [collectionParam, crossAccountParam()],
+      'Vec<u32>',
+    ),
+    collectionTokens: fun(
+      'Get tokens contained within a collection',
+      [collectionParam],
+      'Vec<u32>',
+    ),
+    tokenExists: fun(
+      'Check if the token exists',
+      [collectionParam, tokenParam],
+      'bool',
+    ),
+
+    tokenOwner: fun(
+      'Get the token owner',
+      [collectionParam, tokenParam],
+      `Option<${CROSS_ACCOUNT_ID_TYPE}>`,
+    ),
+    topmostTokenOwner: fun(
+      'Get the topmost token owner in the hierarchy of a possibly nested token',
+      [collectionParam, tokenParam],
+      `Option<${CROSS_ACCOUNT_ID_TYPE}>`,
+    ),
+    tokenOwners: fun(
+      'Returns 10 tokens owners in no particular order',
+      [collectionParam, tokenParam],
+      `Vec<${CROSS_ACCOUNT_ID_TYPE}>`,
+    ),
+    tokenChildren: fun(
+      'Get tokens nested directly into the token',
+      [collectionParam, tokenParam],
+      'Vec<UpDataStructsTokenChild>',
+    ),
+
+    collectionProperties: fun(
+      'Get collection properties, optionally limited to the provided keys',
+      [collectionParam, propertyKeysParam],
+      'Vec<UpDataStructsProperty>',
+    ),
+    tokenProperties: fun(
+      'Get token properties, optionally limited to the provided keys',
+      [collectionParam, tokenParam, propertyKeysParam],
+      'Vec<UpDataStructsProperty>',
+    ),
+    propertyPermissions: fun(
+      'Get property permissions, optionally limited to the provided keys',
+      [collectionParam, propertyKeysParam],
+      'Vec<UpDataStructsPropertyKeyPermission>',
+    ),
+
+    constMetadata: fun(
+      'Get token constant metadata',
+      [collectionParam, tokenParam],
+      'Vec<u8>',
+    ),
+    variableMetadata: fun(
+      'Get token variable metadata',
+      [collectionParam, tokenParam],
+      'Vec<u8>',
+    ),
+
+    tokenData: fun(
+      'Get token data, including properties, optionally limited to the provided keys, and total pieces for an RFT',
+      [collectionParam, tokenParam, propertyKeysParam],
+      'UpDataStructsTokenData',
+    ),
+    totalSupply: fun(
+      'Get the amount of distinctive tokens present in a collection',
+      [collectionParam],
+      'u32',
+    ),
+
+    accountBalance: fun(
+      'Get the amount of any user tokens owned by an account',
+      [collectionParam, crossAccountParam()],
+      'u32',
+    ),
+    balance: fun(
+      'Get the amount of a specific token owned by an account',
+      [collectionParam, crossAccountParam(), tokenParam],
+      'u128',
+    ),
+    allowance: fun(
+      'Get the amount of currently possible sponsored transactions on a token for the fee to be taken off a sponsor',
+      [collectionParam, crossAccountParam('sender'), crossAccountParam('spender'), tokenParam],
+      'u128',
+    ),
+
+    adminlist: fun(
+      'Get the list of admin accounts of a collection',
+      [collectionParam],
+      'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
+    ),
+    allowlist: fun(
+      'Get the list of accounts allowed to operate within a collection',
+      [collectionParam],
+      'Vec<PalletEvmAccountBasicCrossAccountIdRepr>',
+    ),
+    allowed: fun(
+      'Check if a user is allowed to operate within a collection',
+      [collectionParam, crossAccountParam()],
+      'bool',
+    ),
+
+    lastTokenId: fun(
+      'Get the last token ID created in a collection',
+      [collectionParam],
+      'u32',
+    ),
+    collectionById: fun(
+      'Get a collection by the specified ID',
+      [collectionParam],
+      'Option<UpDataStructsRpcCollection>',
+    ),
+    collectionStats: fun(
+      'Get chain stats about collections',
+      [],
+      'UpDataStructsCollectionStats',
+    ),
+
+    nextSponsored: fun(
+      'Get the number of blocks until sponsoring a transaction is available',
+      [collectionParam, crossAccountParam(), tokenParam],
+      'Option<u64>',
+    ),
+    effectiveCollectionLimits: fun(
+      'Get effective collection limits',
+      [collectionParam],
+      'Option<UpDataStructsCollectionLimits>',
+    ),
+    totalPieces: fun(
+      'Get the total amount of pieces of an RFT',
+      [collectionParam, tokenParam],
+      'Option<u128>',
+    ),
+    allowanceForAll: fun(
+      'Tells whether the given `owner` approves the `operator`.',
+      [collectionParam, crossAccountParam('owner'), crossAccountParam('operator')],
+      'Option<bool>',
+    ),
+  },
+};
addedjs-packages/types/unique/index.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/unique/index.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export * from './types.js';
addedjs-packages/types/unique/types.tsdiffbeforeafterboth
--- /dev/null
+++ b/js-packages/types/unique/types.ts
@@ -0,0 +1,4 @@
+// Auto-generated via `yarn polkadot-types-from-defs`, do not edit
+/* eslint-disable */
+
+export type PHANTOM_UNIQUE = 'unique';
modifiedjs-packages/yarn.lockdiffbeforeafterboth
--- a/js-packages/yarn.lock
+++ b/js-packages/yarn.lock
@@ -595,6 +595,35 @@
   languageName: node
   linkType: hard
 
+"@polkadot/typegen@npm:^10.10.1":
+  version: 10.10.1
+  resolution: "@polkadot/typegen@npm:10.10.1"
+  dependencies:
+    "@polkadot/api": 10.10.1
+    "@polkadot/api-augment": 10.10.1
+    "@polkadot/rpc-augment": 10.10.1
+    "@polkadot/rpc-provider": 10.10.1
+    "@polkadot/types": 10.10.1
+    "@polkadot/types-augment": 10.10.1
+    "@polkadot/types-codec": 10.10.1
+    "@polkadot/types-create": 10.10.1
+    "@polkadot/types-support": 10.10.1
+    "@polkadot/util": ^12.5.1
+    "@polkadot/util-crypto": ^12.5.1
+    "@polkadot/x-ws": ^12.5.1
+    handlebars: ^4.7.8
+    tslib: ^2.6.2
+    yargs: ^17.7.2
+  bin:
+    polkadot-types-chain-info: scripts/polkadot-types-chain-info.mjs
+    polkadot-types-from-chain: scripts/polkadot-types-from-chain.mjs
+    polkadot-types-from-defs: scripts/polkadot-types-from-defs.mjs
+    polkadot-types-internal-interfaces: scripts/polkadot-types-internal-interfaces.mjs
+    polkadot-types-internal-metadata: scripts/polkadot-types-internal-metadata.mjs
+  checksum: 9c167fffc476b59524ef864472b688884739c8f2deccc24b09713d7ce4247e43e352be05261ff21072dc1afbbbd2da750a11f42ae657de481d9b402ef858904b
+  languageName: node
+  linkType: hard
+
 "@polkadot/types-augment@npm:10.10.1":
   version: 10.10.1
   resolution: "@polkadot/types-augment@npm:10.10.1"
@@ -1217,8 +1246,7 @@
   version: 0.0.0-use.local
   resolution: "@unique/opal-types@workspace:types"
   dependencies:
-    rxjs: ^7.8.1
-    tslib: ^2.6.2
+    "@polkadot/typegen": ^10.10.1
   languageName: unknown
   linkType: soft
 
@@ -1965,6 +1993,17 @@
   languageName: node
   linkType: hard
 
+"cliui@npm:^8.0.1":
+  version: 8.0.1
+  resolution: "cliui@npm:8.0.1"
+  dependencies:
+    string-width: ^4.2.0
+    strip-ansi: ^6.0.1
+    wrap-ansi: ^7.0.0
+  checksum: 79648b3b0045f2e285b76fb2e24e207c6db44323581e421c3acbd0e86454cba1b37aea976ab50195a49e7384b871e6dfb2247ad7dec53c02454ac6497394cb56
+  languageName: node
+  linkType: hard
+
 "clone-response@npm:^1.0.2":
   version: 1.0.3
   resolution: "clone-response@npm:1.0.3"
@@ -3376,6 +3415,24 @@
   languageName: node
   linkType: hard
 
+"handlebars@npm:^4.7.8":
+  version: 4.7.8
+  resolution: "handlebars@npm:4.7.8"
+  dependencies:
+    minimist: ^1.2.5
+    neo-async: ^2.6.2
+    source-map: ^0.6.1
+    uglify-js: ^3.1.4
+    wordwrap: ^1.0.0
+  dependenciesMeta:
+    uglify-js:
+      optional: true
+  bin:
+    handlebars: bin/handlebars
+  checksum: 00e68bb5c183fd7b8b63322e6234b5ac8fbb960d712cb3f25587d559c2951d9642df83c04a1172c918c41bcfc81bfbd7a7718bbce93b893e0135fc99edea93ff
+  languageName: node
+  linkType: hard
+
 "har-schema@npm:^2.0.0":
   version: 2.0.0
   resolution: "har-schema@npm:2.0.0"
@@ -4199,7 +4256,7 @@
   languageName: node
   linkType: hard
 
-"minimist@npm:^1.2.6":
+"minimist@npm:^1.2.5, minimist@npm:^1.2.6":
   version: 1.2.8
   resolution: "minimist@npm:1.2.8"
   checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0
@@ -4494,6 +4551,13 @@
   languageName: node
   linkType: hard
 
+"neo-async@npm:^2.6.2":
+  version: 2.6.2
+  resolution: "neo-async@npm:2.6.2"
+  checksum: deac9f8d00eda7b2e5cd1b2549e26e10a0faa70adaa6fdadca701cc55f49ee9018e427f424bac0c790b7c7e2d3068db97f3093f1093975f2acb8f8818b936ed9
+  languageName: node
+  linkType: hard
+
 "next-tick@npm:^1.1.0":
   version: 1.1.0
   resolution: "next-tick@npm:1.1.0"
@@ -5422,6 +5486,13 @@
   languageName: node
   linkType: hard
 
+"source-map@npm:^0.6.1":
+  version: 0.6.1
+  resolution: "source-map@npm:0.6.1"
+  checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2
+  languageName: node
+  linkType: hard
+
 "sshpk@npm:^1.7.0":
   version: 1.18.0
   resolution: "sshpk@npm:1.18.0"
@@ -5473,7 +5544,7 @@
   languageName: node
   linkType: hard
 
-"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0":
+"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
   version: 4.2.3
   resolution: "string-width@npm:4.2.3"
   dependencies:
@@ -5886,6 +5957,15 @@
   languageName: node
   linkType: hard
 
+"uglify-js@npm:^3.1.4":
+  version: 3.17.4
+  resolution: "uglify-js@npm:3.17.4"
+  bin:
+    uglifyjs: bin/uglifyjs
+  checksum: 7b3897df38b6fc7d7d9f4dcd658599d81aa2b1fb0d074829dd4e5290f7318dbca1f4af2f45acb833b95b1fe0ed4698662ab61b87e94328eb4c0a0d3435baf924
+  languageName: node
+  linkType: hard
+
 "ultron@npm:~1.1.0":
   version: 1.1.1
   resolution: "ultron@npm:1.1.1"
@@ -5942,6 +6022,7 @@
     eslint-plugin-mocha: ^10.2.0
     lossless-json: ^3.0.1
     solc: ^0.8.22
+    ts-node: ^10.9.1
     typechain: ^8.3.2
     typescript: ^5.2.2
     web3: 1.10.0
@@ -6416,6 +6497,13 @@
   languageName: node
   linkType: hard
 
+"wordwrap@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "wordwrap@npm:1.0.0"
+  checksum: 2a44b2788165d0a3de71fd517d4880a8e20ea3a82c080ce46e294f0b68b69a2e49cff5f99c600e275c698a90d12c5ea32aff06c311f0db2eb3f1201f3e7b2a04
+  languageName: node
+  linkType: hard
+
 "wordwrapjs@npm:^4.0.0":
   version: 4.0.1
   resolution: "wordwrapjs@npm:4.0.1"
@@ -6573,6 +6661,13 @@
   languageName: node
   linkType: hard
 
+"yargs-parser@npm:^21.1.1":
+  version: 21.1.1
+  resolution: "yargs-parser@npm:21.1.1"
+  checksum: ed2d96a616a9e3e1cc7d204c62ecc61f7aaab633dcbfab2c6df50f7f87b393993fe6640d017759fe112d0cb1e0119f2b4150a87305cc873fd90831c6a58ccf1c
+  languageName: node
+  linkType: hard
+
 "yargs-unparser@npm:2.0.0":
   version: 2.0.0
   resolution: "yargs-unparser@npm:2.0.0"
@@ -6600,6 +6695,21 @@
   languageName: node
   linkType: hard
 
+"yargs@npm:^17.7.2":
+  version: 17.7.2
+  resolution: "yargs@npm:17.7.2"
+  dependencies:
+    cliui: ^8.0.1
+    escalade: ^3.1.1
+    get-caller-file: ^2.0.5
+    require-directory: ^2.1.1
+    string-width: ^4.2.3
+    y18n: ^5.0.5
+    yargs-parser: ^21.1.1
+  checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a
+  languageName: node
+  linkType: hard
+
 "yn@npm:3.1.1":
   version: 3.1.1
   resolution: "yn@npm:3.1.1"